/* ---------- Content (edit here) ---------- */
// tags: ex = exhibition, com = commissioned work, scr = screening/talk,
// res = residency, good = good news. sel: true = shown in "Selected Work" mode.
const projects = [
{ y: 2026, t: "RUSHED", u: "https://paulagarciasans.com/rushed" },
{ y: 2026, t: "Undertale", u: "https://paulagarciasans.com/undertale" },
{ y: 2026, t: "Ethereal Estate", u: "https://paulagarciasans.com/ethereal-estate-1" },
{ y: 2025, t: "The Hatching", u: "https://paulagarciasans.com/the-hatching" },
{ y: 2023, t: "Nacreous Clouds", u: "https://paulagarciasans.com/nacreous-clouds-1" },
{ y: 2022, t: "Bodies that don’t age", u: "https://paulagarciasans.com/bodies-that-don't-age-1" },
{ y: 2021, t: "Hole to Bliss", u: "https://paulagarciasans.com/hole-to-bliss-1" },
{ y: 2020, t: "Hyperindividualized Pleasure", u: "https://paulagarciasans.com/hyperindividualized-pleasure-1" },
{ y: "", t: "COMMISSIONS", u: "https://paulagarciasans.com/commissions" }
];
const cv = [
{ y: 2026, t: "Oblivion was inevitable, Kunstfort bij Vijfhuizen, NL", u: "https://www.kunstfort.nl/nl/tentoonstellingen/de-vergetelheid-was-onvermijdelijk/", tags: ["ex"], sel: true },
{ y: 2026, t: "Friendly Secrecy System, Sickhouse, Enschede, NL", tags: ["ex"], sel: true },
{ y: 2025, t: "Sala d’Art Jove 2025, HAUS, Barcelona, SP", u: "https://saladartjove.cat/es/artistas-archive/paula-garcia-sans/", tags: ["ex", "good"], sel: true },
{ y: 2025, t: "GLOPS Bienal, Vilafranca del Penedès, SP", u: "https://glops.pinnae.cat/artista/paula-garcia-sans/", tags: ["ex"], sel: true },
{ y: 2025, t: "3D modelling for Heleen Mineur’s FOLLY, NL", u: "https://www.centraalmuseum.nl/nl/nu-te-zien/tentoonstellingen/folly-een-speculatieve-hervertelling", tags: ["com"], sel: true },
{ y: 2025, t: "GlogauAIR Residency, Berlin, DE", u: "https://glogauair.net/activity/meet-the-artist-paula-garcia-sans/", tags: ["res"], sel: true },
{ y: 2025, t: "Machinimas, Dokzaal, Amsterdam, NL", u: "https://plantagedok.nl/event/an-evening-of-screenings-by-paula-garcia-sans-faysal-mroueh-and-morgane-billuart/", tags: ["scr"] },
{ y: 2025, t: "Worldbuilding and animations for Lorena Solis Bravo’s Samper Mendoza, Virreina Residency, Bogotá, CO", tags: ["com"] },
{ y: 2025, t: "Prospects, Art Rotterdam, NL", tags: ["ex"] },
{ y: 2025, t: "Animation for Clara Rojas, SIGN Gallery, Groningen, NL", tags: ["com"] },
{ y: 2024, t: "MACA x Museumnight, group show, Amsterdam, NL", tags: ["ex"] },
{ y: 2024, t: "Animations for Lorena Solis Bravo’s myth, moth, mother", tags: ["com"] },
{ y: 2024, t: "The sun called it a day, group show at Neverneverland, Amsterdam, NL", tags: ["ex"] },
{ y: 2024, t: "Everything is one the one, group show at Arti et Amicitiae, Amsterdam, NL", u: "https://www.arti.nl/tentoonstelling/everything-is-on-the-one/", tags: ["ex"] },
{ y: 2023, t: "I gazed at your visceral ripples, group show at Gallery Elipsis, Amsterdam, NL", tags: ["ex"] },
{ y: 2023, t: "Agora, panel talk organized by MARC, Amsterdam, NL", u: "https://marc.amsterdam/MARC-Agora-1", tags: ["scr"] },
{ y: 2023, t: "Boring Festival, commissioned by the Kunstfort bij Vijfhuizen, Haarlem, NL", u: "https://www.kunstfort.nl/en/public-program/kunstfort-bij-vijfhuizen-presents-nacreous-clouds-by-paula-garcia-sans-on-boring-festival/", tags: ["ex", "com"], sel: true },
{ y: 2023, t: "VFX for Josefin Arnell’s Beast&Feast", u: "https://www.franshalsmuseum.nl/en/event/fever-dream/", tags: ["com"] },
{ y: 2023, t: "Future Deserts, group show at Salbker Wassertum, invited by onEARTH Foundation, Magdeburg, DE", tags: ["ex"] },
{ y: 2022, t: "3D design for Mathilde Renault’s The Butterfly Effect, International Documentary Festival of Amsterdam, NL", u: "https://www.idfa.nl/en/film/c0167002-068b-41f4-8376-2b096ec0f847/the-butterfly-effect/", tags: ["com"] },
{ y: 2022, t: "Bodies that don’t age, exhibition commissioned by the Douanelab, Amsterdam, NL", tags: ["ex", "com"], sel: true },
{ y: 2022, t: "BYOB, group show, Upstream Gallery x Oedipus, Amsterdam, NL", tags: ["ex"] },
{ y: 2022, t: "Worlds that world, Kunstfort bij Vijfhuizen, Haarlem, NL", u: "https://www.kunstfort.nl/en/news/presentations-worlds-that-world/", tags: ["ex"] },
{ y: 2022, t: "Subbacultcha x Rosedout World, visuals for event at ISO, Amsterdam, NL", tags: ["com"] },
{ y: 2022, t: "Stedelijk Museum Night, visuals for The New Originals, Amsterdam, NL", u: "https://www.stedelijk.nl/en/events/museumnacht-2021-2", tags: ["com"] },
{ y: 2021, t: "3 campaign images for the Dutch National Opera season of 2021-2022", u: "https://www.operaballet.nl/en/dno-x-young-artists", tags: ["com"] },
{ y: 2021, t: "MIRA festival & IDEAL, group show curated by mmmad.art, Barcelona, ES", tags: ["ex"] },
{ y: 2021, t: "10s10a, online exhibition, curated by mmmad.art for The Wrong Biennale", u: "https://mmmad.art/10s10a-x-The-Wrong-Biennale", tags: ["ex"] },
{ y: 2021, t: "Sensual Havoc II, group show at De School-Subbacultcha space, Amsterdam, NL", u: "https://subbacultcha.nl/event/sensual-havoc-ii/", tags: ["ex"] },
{ y: 2021, t: "There’s never been a winter so long that spring didn’t come, Laurel Project Space, Amsterdam, NL", u: "https://www.laurelprojectspace.com/exhibitions/There's-Never-Been-a-Winter-So-Long-the-Spring-Didn't-Come", tags: ["ex"] },
{ y: 2021, t: "Oh, the places you’d go, group show at the Willem-Twee Museum, Den Bosch, NL", u: "https://www.youtube.com/watch?v=2RtARaV5Qjw", tags: ["ex"] },
{ y: 2021, t: "Animation for Nicolas Arthen’s An intricate well", u: "https://www.pakt.nu/2021/nicola-arthen-am-i-an-object-part-ii/", tags: ["com"] },
{ y: 2021, t: "Comfort and Vision, curated by Salim Bayri for the One Minutes Foundation", u: "https://nieuweinstituut.nl/en/events/comfort-and-vision", tags: ["scr"] },
{ y: 2020, t: "Call for graduates, NEST Ruimte x Melkweg Expo, Den Haag, NL", u: "https://www.mistermotley.nl/calls-graduates/", tags: ["ex"] },
{ y: 2020, t: "Kantarion Sound x Off Center, visuals for Yara Said, The Grey Space in the Middle, Den Haag, NL", tags: ["com"] },
{ y: 2019, t: "Eye on Art, film screening, Eye Museum, Amsterdam, NL", tags: ["scr"] },
{ y: 2017, t: "VR operator, Carne y Arena, Eye Museum, Amsterdam, NL", u: "https://www.eyefilm.nl/en/whats-on/carne-y-arena-virtually-present-physically-invisible/507887", tags: ["com"] }
];
const grants = [
{ y: 2026, t: "Fulbright Scholarship", tags: ["grant", "good"] },
{ y: 2025, t: "Amsterdams Fonds voor de Kunst, Development Grant", tags: ["grant"] },
{ y: 2025, t: "Prize Sala d’Art Jove Barcelona", tags: ["grant", "good"] },
{ y: 2025, t: "Digital Culture Research grant, Stimuleringsfonds", tags: ["grant"] },
{ y: 2024, t: "Mondriaan Fonds Voucher Development", tags: ["grant"] },
{ y: 2024, t: "AFK Project Grant, in collab with Eefje Stenfert", tags: ["grant"] },
{ y: 2024, t: "Prins Bernhard Cultuurfonds, Study Grant", tags: ["grant"] },
{ y: 2023, t: "Mondriaan Fonds Artist Start Grant", tags: ["grant"] }
];
const education = [
{ y: "now", t: "MS Fiction & Entertainment, Southern California Institute of Architecture", tags: [] },
{ y: 2020, t: "BA Moving Image, Gerrit Rietveld Academie", tags: [] }
];
/* ---------- Rendering ---------- */
const TAG_LABELS = { ex: "exhibition", com: "commission", scr: "screening/talk", res: "residency", grant: "grant", good: "good news" };
function row(item, extraClass = "") {
const li = document.createElement("li");
li.dataset.tags = (item.tags || []).join(" ");
if (item.sel) li.dataset.sel = "1";
if (extraClass) li.className = extraClass;
const y = document.createElement("span");
y.className = "year";
y.textContent = item.y === "" ? "" : item.y;
const body = document.createElement("span");
if (item.u) {
const a = document.createElement("a");
a.href = item.u; a.target = "_blank"; a.rel = "noopener";
a.textContent = item.t;
body.append(a);
} else {
body.append(item.t);
}
if ((item.tags || []).includes("good")) {
const g = document.createElement("span");
g.className = "tag tag-good"; g.textContent = "GOOD NEWS";
body.append(g);
}
li.append(y, body);
return li;
}
function renderList(id, items) {
const ul = document.getElementById(id);
items.forEach(i => ul.append(row(i)));
}
renderList("cv-list", cv);
renderList("grant-list", grants);
renderList("edu-list", education);
const pl = document.getElementById("project-list");
projects.forEach(p => {
const li = document.createElement("li");
const y = document.createElement("span");
y.className = "year"; y.textContent = p.y;
const a = document.createElement("a");
a.className = "title"; a.href = p.u;
a.textContent = p.t + (p.y ? " - " + p.y : "");
// original site shows "[Title - Year]"; the year column already carries it
a.textContent = p.t;
const wrap = document.createElement("span");
wrap.append(a);
li.append(y, wrap);
pl.append(li);
});
/* ---------- Quick facts toggle ---------- */
const toggle = document.getElementById("facts-toggle");
const facts = document.getElementById("facts");
toggle.addEventListener("click", () => {
const open = facts.hasAttribute("hidden");
facts.toggleAttribute("hidden", !open);
toggle.setAttribute("aria-expanded", String(open));
toggle.querySelector(".plus").textContent = open ? "–" : "+";
});
/* ---------- Highlight filters ---------- */
const filterButtons = document.querySelectorAll(".filters button");
let active = "";
function applyFilter() {
document.body.classList.toggle("filtering", !!active);
document.querySelectorAll(".cv li").forEach(li => {
const tags = (li.dataset.tags || "").split(" ");
li.classList.toggle("hl", !!active && tags.includes(active));
});
filterButtons.forEach(b => b.setAttribute("aria-pressed", String(b.dataset.filter === active && !!active)));
}
filterButtons.forEach(b => {
b.addEventListener("click", () => {
active = (b.dataset.filter === active) ? "" : b.dataset.filter;
applyFilter();
});
});
/* ---------- Selected / all mode ---------- */
const modeBtn = document.getElementById("mode-toggle");
const modeName = document.getElementById("mode-name");
let showAll = false;
function applyMode() {
document.querySelectorAll("#cv-list li").forEach(li => {
li.hidden = !showAll && !li.dataset.sel;
});
modeName.textContent = showAll ? "'All Work'" : "'Selected Work'";
modeBtn.textContent = showAll ? "show less" : "show more";
}
modeBtn.addEventListener("click", () => { showAll = !showAll; applyMode(); });
applyMode();