Files
Youssef bd9e9ab859 v1.1 — four new themes, the Custom route, and a full copy pass
Adds Carnival, Lumen, Hum, and Cobalt to the catalogue (now 20 themes),
each with its own tokens block, hero archetype, landing copy, and spec doc.

Introduces Custom as a first-class route: when a brief carries real
creative intent, Hallmark designs the whole page from first principles —
palette, type, and structure — rather than recolouring a catalogue theme.
Ships five worked custom examples and a README section.

Rewrites the per-theme landing copy and reconciles every catalogue count
across the site and the skill (20 themes · 21 macrostructures · 50
component archetypes · 70 slop-test gates). Drops the last competitor
references from the CSS comments, fixes the genre rosters, and ignores
the local example explorations so only the featured builds ship.
2026-06-02 17:50:31 +01:00

47 lines
1.5 KiB
JavaScript

/* Cascadia Nightjar ticket — drives the single orchestrated print/tear/stamp.
Progressive enhancement only: the page is fully legible without JS. */
(function () {
"use strict";
var root = document.documentElement;
var reduce = window.matchMedia &&
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduce) {
// Honour reduced motion: show the finished ticket, no animation.
root.classList.remove("is-ready");
return;
}
// Run the print/tear/stamp sequence once, after first paint.
// rAF gives the cleanest start; a timeout backstops it in case rAF is
// throttled (background tab / automation) so the intro never silently stalls.
var started = false;
function play() {
if (started) return;
started = true;
root.classList.add("is-ready");
// Settle to the resting state when the intro ends. The timeout also
// guarantees the final visible state in environments that freeze the
// animation clock (background tabs, automation) — the ticket is never
// left stranded on the hidden first frame.
var settle = function () { root.classList.add("is-done"); };
var ticket = document.querySelector(".ticket");
if (ticket) ticket.addEventListener("animationend", settle, { once: true });
setTimeout(settle, 1300);
}
function schedule() {
requestAnimationFrame(play);
setTimeout(play, 60);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", schedule, { once: true });
} else {
schedule();
}
})();