diff --git a/RECOMMENDED_FONTS.md b/RECOMMENDED_FONTS.md index 7d80228..3cc9d08 100644 --- a/RECOMMENDED_FONTS.md +++ b/RECOMMENDED_FONTS.md @@ -1,50 +1,36 @@ # Recommended fonts for the Y2K theme -The Y2K theme ships `fonts.json = []` per the wave-1 fonts policy -(`~/src/blockninja/themes/docs/FONTS.md`). The theme CSS consumes fonts via -the host CSS custom properties `--font-heading`, `--font-body`, `--font-mono` -with fallback stacks tuned to match the spec's intended aesthetic. +The Y2K theme **bundles** three OFL families as latin-subset woff2 (see +`assets/fonts/LICENSES.md`). They register as `source=template` rows and are +selectable in the typography picker. The theme CSS consumes fonts only through +the host CSS custom properties `--font-heading`, `--font-body`, `--font-mono`, +with fallback stacks that lead with the bundled families — so the intended look +survives before an admin assigns anything. -To get the intended Y2K look, an admin should open the typography panel and -assign the following Google Fonts (or upload the licensed picks where noted): +## Bundled (works out of the box) -## Heading (display) +| Slot | Family | Notes | +|---|---|---| +| Heading / display | **Orbitron** (OFL) | Geometric techno face for chrome-stretched headlines. | +| Body | **Inter** (OFL) | Clean neutral body. | +| Mono | **Share Tech Mono** (OFL) | Eyebrows, kickers, tabular data, the webring badge. | -- **Stretch Pro** — commercial, not in the Google Fonts curated list. - - Source: `upload:Stretch Pro` (admin must source the woff2 separately and - upload via the typography panel's Upload tab). - - Why: matches the spec's primary display face for chrome-stretched - headlines. -- **VT323** — open license, available in Google Fonts. - - Source: `google:VT323`. - - Instruction: Open the typography panel, switch to the Google Fonts tab, - search "VT323", click Add, then assign it to the Heading slot. Use this - when Stretch Pro is not licensed for the site. +Assign each to its slot in the typography panel (they appear under the bundled / +template source). Until then, the fallback stacks below apply automatically. -## Body +## Fallback stacks (declared in `manifest.yaml` CSS) -- **Inter Tight** — open license, available in Google Fonts. - - Source: `google:Inter Tight`. - - Instruction: Open the typography panel, switch to the Google Fonts tab, - search "Inter Tight", click Add, then assign it to the Body slot. +- Heading: `var(--font-heading, "Orbitron", "Michroma", system-ui, sans-serif)` +- Body: `var(--font-body, "Inter", system-ui, -apple-system, "Segoe UI", sans-serif)` +- Mono: `var(--font-mono, "Share Tech Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, monospace)` -## Mono +## Alternatives from the Google Fonts tab -- **Departure Mono** — open license, available as a single-foundry download. - - Source: `upload:Departure Mono` (sourced from - https://departuremono.com — admin uploads the woff2 via the Upload tab). - - Instruction: After uploading the woff2, assign it to the Mono slot in the - typography panel. +- **Michroma** (OFL) — wider techno display; a strong Orbitron alternative for the + Heading slot. Open the typography panel, Google Fonts tab, search "Michroma", + Add, assign to Heading. +- **Inter Tight** (OFL) — tighter body option; assign to Body the same way. -## What works out of the box - -Until an admin assigns fonts, the theme renders with the following fallback -stacks (declared in `css.go` via the host Tailwind input): - -- Headings: `var(--font-heading, "Stretch Pro", "VT323", "Courier New", monospace)` -- Body: inherited from the host `--font-body` with default - `system-ui, -apple-system, Segoe UI, Roboto, sans-serif`. -- Mono: inherited from the host `--font-mono` with default - `ui-monospace, "Departure Mono", "JetBrains Mono", monospace`. - -These keep the theme readable and on-brand even before any picker action. +The bundled subsets cover English (Latin + Latin-1 punctuation). Sites needing +extended Latin, Cyrillic, Greek, or Vietnamese should assign the full family from +the Google Fonts tab instead of relying on the bundled subset. diff --git a/RECOMMENDED_ICONS.md b/RECOMMENDED_ICONS.md new file mode 100644 index 0000000..111bb47 --- /dev/null +++ b/RECOMMENDED_ICONS.md @@ -0,0 +1,15 @@ +# Recommended icon packs for the Y2K theme + +The theme renders icons only as sprite references +(``) and declares its packs in +`plugin.mod` `required_icon_packs`. The loader does not yet auto-install packs, +so install these before activation: + +| Pack | Why | Install | +|---|---|---| +| **lucide** | Default UI glyphs used across overrides and the persona blocks (music, disc, arrow, mail, user, etc.). Bundled with the CMS. | Open `/admin/icons`, confirm Lucide is installed and enabled (it ships with the CMS). | +| **simple-icons** | Brand marks for social links and OAuth buttons (github, instagram, youtube, spotify, bandcamp, google, facebook). Bundled with the CMS. | Open `/admin/icons`, confirm Simple Icons is installed and enabled. | + +Both packs ship bundled with the CMS, so no download is normally required — just +confirm they are enabled under `/admin/icons`. Block schemas default their icon +fields to Lucide names, so the theme renders correctly out of the box. diff --git a/assets/fonts/LICENSES.md b/assets/fonts/LICENSES.md new file mode 100644 index 0000000..bb7cfca --- /dev/null +++ b/assets/fonts/LICENSES.md @@ -0,0 +1,25 @@ +# Bundled font licenses — Y2K theme + +All three families are bundled as latin-subset `.woff2` and are licensed under the +SIL Open Font License 1.1 (OFL). Redistribution inside this theme artifact is permitted. + +| File | Family | License | Source | +|---|---|---|---| +| `orbitron-latin.woff2` | Orbitron (variable, wght 500–900) | OFL 1.1 | Google Fonts (fonts.gstatic.com, v35), latin subset | +| `share-tech-mono-latin.woff2` | Share Tech Mono (400) | OFL 1.1 | Google Fonts (fonts.gstatic.com, v16), latin subset | +| `inter-latin.woff2` | Inter (variable, wght 400–700) | OFL 1.1 | Google Fonts (fonts.gstatic.com, v20), latin subset | + +OFL 1.1 full text: https://openfontlicense.org + +- Orbitron (c) The Orbitron Project Authors (Matt McInerney). +- Share Tech Mono (c) Carrois Apostrophe. +- Inter (c) The Inter Project Authors (Rasmus Andersson). + +These are the Google-hosted latin subsets (Latin + Latin-1 punctuation), chosen to keep +the bundle small (~73 KB total). They cover English content; sites needing extended Latin, +Cyrillic, Greek, or Vietnamese glyphs should assign the full family from the admin Google +Fonts picker instead. + +Orbitron is the geometric techno display face. Michroma (also OFL) is an equally good +heading alternative and is called out in the fallback stack; assign it from the Google +Fonts tab if you prefer its wider proportions. diff --git a/assets/fonts/inter-latin.woff2 b/assets/fonts/inter-latin.woff2 new file mode 100644 index 0000000..d15208d Binary files /dev/null and b/assets/fonts/inter-latin.woff2 differ diff --git a/assets/fonts/orbitron-latin.woff2 b/assets/fonts/orbitron-latin.woff2 new file mode 100644 index 0000000..39d9db5 Binary files /dev/null and b/assets/fonts/orbitron-latin.woff2 differ diff --git a/assets/fonts/share-tech-mono-latin.woff2 b/assets/fonts/share-tech-mono-latin.woff2 new file mode 100644 index 0000000..f48075f Binary files /dev/null and b/assets/fonts/share-tech-mono-latin.woff2 differ diff --git a/assets/placeholder.txt b/assets/placeholder.txt deleted file mode 100644 index e5324b3..0000000 --- a/assets/placeholder.txt +++ /dev/null @@ -1,5 +0,0 @@ -Y2K theme assets directory. - -This pass ships no bundled fonts or images (fonts.json = []). -Add woff2 files under fonts/ and reference them from fonts.json once font -licensing has been resolved (see RECOMMENDED_FONTS.md). diff --git a/blocks/blocks.yaml b/blocks/blocks.yaml index 40f8232..210510f 100644 --- a/blocks/blocks.yaml +++ b/blocks/blocks.yaml @@ -1,66 +1,43 @@ -# Y2K theme block definitions (codeless .bnp, WO-WZ-021). +# Y2K theme persona block definitions (codeless .bnp, WO-TF-026). # Keys are fully qualified (y2k:) because the codeless loader registers # definitions verbatim — there is no PluginBlockRegistry prefixing in the # codeless path (unlike the wasm/templ path). The qualified keys match the -# hardcoded data-block-key attributes and the master_pages.json references. +# data-block attributes and the master_pages.json / seed references. +# +# Builtin-duplicating blocks (navbar, footer, hero, divider, marquee-> +# announcement-bar, merch->pricing/card) are NOT theme blocks — they are +# template overrides in templates/overrides/y2k/. The old nft_gallery block is +# DELETED per WO-TF-026. Only true band/musician persona furniture lives here. blocks: - - key: y2k:chrome_navbar - title: Chrome Navbar - description: Beveled chrome navigation bar with hover sparkle. - category: navigation - schema: chrome_navbar.schema.json - template: chrome_navbar.ninjatpl - - key: y2k:metaball_hero - title: Metaball Hero - description: SVG metaballs on a gradient mesh canvas with headline + CTA. - category: layout - schema: metaball_hero.schema.json - template: metaball_hero.ninjatpl - key: y2k:waveform_player title: Waveform Player - description: Soundcloud-style audio player with a canvas waveform. + description: Track card with cover art, a static chrome equalizer strip, and a native audio player. category: content schema: waveform_player.schema.json template: waveform_player.ninjatpl - - key: y2k:marquee - title: Marquee Ticker - description: Scrolling text marquee with chrome edges; pauses on hover. - category: layout - schema: marquee.schema.json - template: marquee.ninjatpl - key: y2k:tracklist title: Tracklist - description: Tabular tracklist styled like a CD jewel case insert. + description: Numbered tracklist styled like a CD jewel-case insert with durations. category: content schema: tracklist.schema.json template: tracklist.ninjatpl - - key: y2k:merch_card - title: Merch Card - description: Plastic-bevel product card with a corner sticker badge. + - key: y2k:tour_dates + title: Tour Dates + description: Live dates with venue, city, sold-out state, and a ticket link. category: content - schema: merch_card.schema.json - template: merch_card.ninjatpl + schema: tour_dates.schema.json + template: tour_dates.ninjatpl + sample_data: tour_dates.sample.json + - key: y2k:discography + title: Discography + description: Grid of releases with cover art, year, and format. + category: content + schema: discography.schema.json + template: discography.ninjatpl + sample_data: discography.sample.json - key: y2k:webring_badge title: Webring Badge - description: Canonical 88x31 webring button row with prev/next/ring links. + description: Canonical 88x31 webring button row with prev / ring / next links. category: navigation schema: webring_badge.schema.json template: webring_badge.ninjatpl - - key: y2k:glitter_divider - title: Glitter Divider - description: Animated sparkle horizontal rule. - category: layout - schema: glitter_divider.schema.json - template: glitter_divider.ninjatpl - - key: y2k:footer_chrome - title: Chrome Footer - description: Beveled chrome footer strip with optional webring and copyright. - category: layout - schema: footer_chrome.schema.json - template: footer_chrome.ninjatpl - - key: y2k:nft_gallery - title: NFT Gallery - description: Holographic-foil hover gallery for NFT items (presentational only). - category: content - schema: nft_gallery.schema.json - template: nft_gallery.ninjatpl diff --git a/blocks/chrome_navbar.ninjatpl b/blocks/chrome_navbar.ninjatpl deleted file mode 100644 index 89e7fbe..0000000 --- a/blocks/chrome_navbar.ninjatpl +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/blocks/chrome_navbar.schema.json b/blocks/chrome_navbar.schema.json deleted file mode 100644 index 7c16f1b..0000000 --- a/blocks/chrome_navbar.schema.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Chrome Navbar", - "description": "Beveled chrome navigation bar with hover sparkle and brand mark.", - "type": "object", - "properties": { - "menuName": { - "type": "string", - "title": "Menu", - "description": "Menu to render as the primary nav.", - "x-editor": "menu-select", - "default": "main" - }, - "logoText": { - "type": "string", - "title": "Logo Text", - "description": "Brand wordmark (used when no logo image is set).", - "x-editor": "text", - "default": "" - }, - "logoImage": { - "type": "string", - "title": "Logo Image", - "description": "Optional brand image. Overrides logo text when set.", - "x-editor": "media", - "default": "" - } - } -} diff --git a/blocks/discography.ninjatpl b/blocks/discography.ninjatpl new file mode 100644 index 0000000..5cb7184 --- /dev/null +++ b/blocks/discography.ninjatpl @@ -0,0 +1,23 @@ +{# y2k:discography — grid of releases with cover art, year, format, and a link. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if releases %} +
+ {% for release in releases %} +
+
+ {% if release.link %}{% if release.cover %}{% img release.cover alt=release.title class="h-full w-full object-cover transition-transform duration-300 group-hover:scale-105" %}{% else %}{% endif %}{% elif release.cover %}{% img release.cover alt=release.title class="h-full w-full object-cover" %}{% else %}{% endif %} +
+
+ {% if release.title %}

{% if release.link %}{{ release.title }}{% else %}{{ release.title }}{% endif %}

{% endif %} +

{% if release.year %}{{ release.year }}{% endif %}{% if release.format %}{{ release.format }}{% endif %}

+
+
+ {% endfor %} +
+ {% else %} +

No releases yet.

+ {% endif %} +
+
diff --git a/blocks/discography.sample.json b/blocks/discography.sample.json new file mode 100644 index 0000000..dc49085 --- /dev/null +++ b/blocks/discography.sample.json @@ -0,0 +1,9 @@ +{ + "heading": "Discography", + "releases": [ + { "title": "Chrome Hearts", "year": "2001", "format": "LP", "cover": "", "link": "#" }, + { "title": "Dial-Up Dreams", "year": "2000", "format": "EP", "cover": "", "link": "#" }, + { "title": "Frosted", "year": "1999", "format": "Single", "cover": "", "link": "#" }, + { "title": "Skyline Demos", "year": "1998", "format": "Cassette", "cover": "", "link": "#" } + ] +} diff --git a/blocks/discography.schema.json b/blocks/discography.schema.json new file mode 100644 index 0000000..8b55195 --- /dev/null +++ b/blocks/discography.schema.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Discography", + "description": "A grid of releases with cover art, year, and a link.", + "type": "object", + "properties": { + "heading": { + "type": "string", + "title": "Heading", + "x-editor": "text", + "default": "Discography" + }, + "releases": { + "type": "array", + "title": "Releases", + "x-editor": "array", + "items": { + "type": "object", + "properties": { + "title": { "type": "string", "title": "Title", "x-editor": "text" }, + "year": { "type": "string", "title": "Year", "x-editor": "text" }, + "format": { "type": "string", "title": "Format", "x-editor": "text" }, + "cover": { "type": "string", "title": "Cover art", "x-editor": "media" }, + "link": { "type": "string", "title": "Link", "x-editor": "text" } + } + }, + "default": [] + } + } +} diff --git a/blocks/footer_chrome.ninjatpl b/blocks/footer_chrome.ninjatpl deleted file mode 100644 index 3bb6d7d..0000000 --- a/blocks/footer_chrome.ninjatpl +++ /dev/null @@ -1,7 +0,0 @@ -
-
- {% if menuName %}{% endif %} - {% if showWebring != "no" and showWebring != false %}{% endif %} - {% if copyright %}{{ copyright }}{% endif %} -
-
diff --git a/blocks/footer_chrome.schema.json b/blocks/footer_chrome.schema.json deleted file mode 100644 index 8af3bfa..0000000 --- a/blocks/footer_chrome.schema.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Chrome Footer", - "description": "Beveled chrome strip with optional webring and copyright.", - "type": "object", - "properties": { - "menuName": { - "type": "string", - "title": "Menu", - "description": "Optional footer menu to render.", - "x-editor": "menu-select", - "default": "" - }, - "showWebring": { - "type": "string", - "title": "Show Webring", - "description": "Render the 88x31 webring badge row.", - "x-editor": "select", - "enum": ["yes", "no"], - "default": "yes" - }, - "copyright": { - "type": "string", - "title": "Copyright", - "description": "Copyright line.", - "x-editor": "text", - "default": "" - } - } -} diff --git a/blocks/glitter_divider.ninjatpl b/blocks/glitter_divider.ninjatpl deleted file mode 100644 index b1dc853..0000000 --- a/blocks/glitter_divider.ninjatpl +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/blocks/glitter_divider.schema.json b/blocks/glitter_divider.schema.json deleted file mode 100644 index c4d9dc8..0000000 --- a/blocks/glitter_divider.schema.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Glitter Divider", - "description": "Animated sparkle horizontal rule.", - "type": "object", - "properties": { - "variant": { - "type": "string", - "title": "Variant", - "x-editor": "select", - "enum": ["sparkle", "stars", "hearts", "chrome"], - "default": "sparkle" - } - } -} diff --git a/blocks/marquee.ninjatpl b/blocks/marquee.ninjatpl deleted file mode 100644 index 8847274..0000000 --- a/blocks/marquee.ninjatpl +++ /dev/null @@ -1,5 +0,0 @@ -
-
- {% if items %}{% for item in items %}*{{ item }}{% endfor %}{% for item in items %}{% endfor %}{% else %}*now playing*new drop friday*subscribe to the zine{% endif %} -
-
diff --git a/blocks/marquee.schema.json b/blocks/marquee.schema.json deleted file mode 100644 index be838e0..0000000 --- a/blocks/marquee.schema.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Marquee Ticker", - "description": "Scrolling text strip with chrome edges. Pauses on hover/focus.", - "type": "object", - "properties": { - "items": { - "type": "array", - "title": "Items", - "description": "Lines to scroll across the marquee.", - "x-editor": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "speed": { - "type": "string", - "title": "Speed", - "x-editor": "select", - "enum": ["slow", "medium", "fast"], - "default": "medium" - }, - "direction": { - "type": "string", - "title": "Direction", - "x-editor": "select", - "enum": ["left", "right"], - "default": "left" - } - } -} diff --git a/blocks/merch_card.ninjatpl b/blocks/merch_card.ninjatpl deleted file mode 100644 index 129e6f2..0000000 --- a/blocks/merch_card.ninjatpl +++ /dev/null @@ -1,13 +0,0 @@ -
- {% if sticker %}{% if sticker == "sold-out" %}sold out{% else %}{{ sticker }}{% endif %}{% endif %} -
- {% if image %}{{ title }}{% else %}
no image
{% endif %} -
-
-

{{ title|default:"untitled" }}

-
- {{ price|default:"$--" }} - {% if buyHref %}buy{% endif %} -
-
-
diff --git a/blocks/merch_card.schema.json b/blocks/merch_card.schema.json deleted file mode 100644 index 83844c5..0000000 --- a/blocks/merch_card.schema.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Merch Card", - "description": "Plastic-bevel product card with a price sticker.", - "type": "object", - "properties": { - "title": { - "type": "string", - "title": "Product Title", - "x-editor": "text", - "default": "" - }, - "price": { - "type": "string", - "title": "Price", - "description": "Display string e.g. $24.", - "x-editor": "text", - "default": "" - }, - "image": { - "type": "string", - "title": "Image", - "x-editor": "media", - "default": "" - }, - "buyHref": { - "type": "string", - "title": "Buy Link", - "x-editor": "link", - "default": "" - }, - "sticker": { - "type": "string", - "title": "Sticker", - "description": "Corner sticker badge variant.", - "x-editor": "select", - "enum": ["", "new", "hot", "sale", "sold-out"], - "default": "" - } - } -} diff --git a/blocks/metaball_hero.ninjatpl b/blocks/metaball_hero.ninjatpl deleted file mode 100644 index d75f7f5..0000000 --- a/blocks/metaball_hero.ninjatpl +++ /dev/null @@ -1,32 +0,0 @@ -
- -
-
-

{{ headline|default:"new EP out now" }}

- {% if sub %}

{{ sub }}

{% endif %} - {% if ctaLabel %}{{ ctaLabel }}{% endif %} -
-
-
diff --git a/blocks/metaball_hero.schema.json b/blocks/metaball_hero.schema.json deleted file mode 100644 index d45a588..0000000 --- a/blocks/metaball_hero.schema.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Metaball Hero", - "description": "Hero section with SVG metaballs on a gradient mesh canvas.", - "type": "object", - "properties": { - "headline": { - "type": "string", - "title": "Headline", - "description": "Display headline.", - "x-editor": "text", - "default": "" - }, - "sub": { - "type": "string", - "title": "Sub-headline", - "description": "Supporting line under the headline.", - "x-editor": "text", - "default": "" - }, - "ctaLabel": { - "type": "string", - "title": "CTA Label", - "description": "Call-to-action button text.", - "x-editor": "text", - "default": "" - }, - "ctaHref": { - "type": "string", - "title": "CTA Link", - "description": "Where the CTA button points.", - "x-editor": "link", - "default": "" - }, - "bgPreset": { - "type": "string", - "title": "Background Preset", - "description": "Which mesh anchor palette to use.", - "x-editor": "select", - "enum": ["magenta-teal", "after-hours", "bubblegum"], - "default": "magenta-teal" - } - } -} diff --git a/blocks/nft_gallery.ninjatpl b/blocks/nft_gallery.ninjatpl deleted file mode 100644 index f3077b5..0000000 --- a/blocks/nft_gallery.ninjatpl +++ /dev/null @@ -1,9 +0,0 @@ -
- {% if not items %}

No items yet.

{% else %}{% endif %} -
diff --git a/blocks/nft_gallery.schema.json b/blocks/nft_gallery.schema.json deleted file mode 100644 index e8a1be8..0000000 --- a/blocks/nft_gallery.schema.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "NFT Gallery", - "description": "Holographic-foil hover gallery of NFT items (presentational only).", - "type": "object", - "properties": { - "items": { - "type": "array", - "title": "Items", - "x-editor": "collection", - "items": { - "type": "object", - "properties": { - "title": { - "type": "string", - "title": "Title", - "x-editor": "text" - }, - "image": { - "type": "string", - "title": "Image", - "x-editor": "media" - }, - "chain": { - "type": "string", - "title": "Chain", - "x-editor": "select", - "enum": ["", "eth", "sol", "polygon", "base", "tezos"] - }, - "href": { - "type": "string", - "title": "Link", - "x-editor": "link" - } - }, - "required": ["title"] - }, - "default": [] - } - } -} diff --git a/blocks/tour_dates.ninjatpl b/blocks/tour_dates.ninjatpl new file mode 100644 index 0000000..74844b7 --- /dev/null +++ b/blocks/tour_dates.ninjatpl @@ -0,0 +1,23 @@ +{# y2k:tour_dates — live dates with venue, city, and a ticket link. Chrome-bevel rows. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} + {% if dates %} +
    + {% for show in dates %} +
  • + {% if show.date %}{{ show.date }}{% endif %} + + {% if show.venue %}{{ show.venue }}{% endif %} + {% if show.city %}{{ show.city }}{% endif %} + + {% if show.soldOut %}Sold out{% elif show.ticketUrl %}Tickets{% endif %} +
  • + {% endfor %} +
+ {% else %} +

No dates announced yet.

+ {% endif %} +
+
diff --git a/blocks/tour_dates.sample.json b/blocks/tour_dates.sample.json new file mode 100644 index 0000000..a8c1bf5 --- /dev/null +++ b/blocks/tour_dates.sample.json @@ -0,0 +1,9 @@ +{ + "heading": "Tour dates", + "intro": "Doors an hour before. All ages unless noted.", + "dates": [ + { "date": "Fri 12 Sep", "venue": "The Chrome Room", "city": "Melbourne", "ticketUrl": "#", "soldOut": false }, + { "date": "Sat 13 Sep", "venue": "Rollerdrome", "city": "Sydney", "ticketUrl": "#", "soldOut": true }, + { "date": "Fri 19 Sep", "venue": "Basement 88", "city": "Brisbane", "ticketUrl": "#", "soldOut": false } + ] +} diff --git a/blocks/tour_dates.schema.json b/blocks/tour_dates.schema.json new file mode 100644 index 0000000..924b162 --- /dev/null +++ b/blocks/tour_dates.schema.json @@ -0,0 +1,36 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Tour Dates", + "description": "A list of live dates with venue, city, and a ticket link.", + "type": "object", + "properties": { + "heading": { + "type": "string", + "title": "Heading", + "x-editor": "text", + "default": "Tour dates" + }, + "intro": { + "type": "string", + "title": "Intro", + "x-editor": "text", + "default": "" + }, + "dates": { + "type": "array", + "title": "Dates", + "x-editor": "array", + "items": { + "type": "object", + "properties": { + "date": { "type": "string", "title": "Date", "x-editor": "text" }, + "venue": { "type": "string", "title": "Venue", "x-editor": "text" }, + "city": { "type": "string", "title": "City", "x-editor": "text" }, + "ticketUrl": { "type": "string", "title": "Ticket URL", "x-editor": "text" }, + "soldOut": { "type": "boolean", "title": "Sold out", "x-editor": "boolean", "default": false } + } + }, + "default": [] + } + } +} diff --git a/blocks/waveform_player.ninjatpl b/blocks/waveform_player.ninjatpl index ce2b81e..f57a7e4 100644 --- a/blocks/waveform_player.ninjatpl +++ b/blocks/waveform_player.ninjatpl @@ -1,13 +1,13 @@ {% if audioUrl or trackTitle %}
- {% if coverImage %}{{ trackTitle }}{% else %}{% endif %} + {% if coverImage %}{% img coverImage alt=trackTitle class="w-full h-full object-cover" %}{% else %}{% endif %}
{{ trackTitle|default:"untitled track" }} - {% if artist %}{{ artist }}{% endif %} + {% if artist %}{{ artist }}{% endif %}
- - {% if audioUrl %}{% else %}

No audio source configured.

{% endif %} + + {% if audioUrl %}{% else %}

No audio source set yet.

{% endif %}
{% endif %} diff --git a/fonts.json b/fonts.json index fe51488..b0027fd 100644 --- a/fonts.json +++ b/fonts.json @@ -1 +1,28 @@ -[] +[ + { + "name": "Orbitron", + "family": "Orbitron", + "variants": [ + { "weight": "500", "style": "normal", "file": "fonts/orbitron-latin.woff2" }, + { "weight": "700", "style": "normal", "file": "fonts/orbitron-latin.woff2" }, + { "weight": "900", "style": "normal", "file": "fonts/orbitron-latin.woff2" } + ] + }, + { + "name": "Share Tech Mono", + "family": "Share Tech Mono", + "variants": [ + { "weight": "400", "style": "normal", "file": "fonts/share-tech-mono-latin.woff2" } + ] + }, + { + "name": "Inter", + "family": "Inter", + "variants": [ + { "weight": "400", "style": "normal", "file": "fonts/inter-latin.woff2" }, + { "weight": "500", "style": "normal", "file": "fonts/inter-latin.woff2" }, + { "weight": "600", "style": "normal", "file": "fonts/inter-latin.woff2" }, + { "weight": "700", "style": "normal", "file": "fonts/inter-latin.woff2" } + ] + } +] diff --git a/manifest.yaml b/manifest.yaml index 36b6b6e..95eafb2 100644 --- a/manifest.yaml +++ b/manifest.yaml @@ -1,118 +1,362 @@ -# Y2K theme — codeless .bnp manifest (WO-WZ-021 Phase B). +# Y2K theme — codeless .bnp manifest (WO-TF-026, Wave A). # Synthesized into manifest.pb by `ninja plugin build` (no plugin.wasm). +# Persona: musician / band. Millennium-web nostalgia — liquid chrome, frosted +# iMac plastic (light) and rave blacklight (dark), marquees, glitter, a webring, +# and a dependency-free canvas metaball hero showpiece (hero/landing only). theme_presets: presets.json bundled_fonts: fonts.json master_pages: master_pages.json +required_icon_packs: ["lucide", "simple-icons"] system_templates: - key: y2k title: Y2K - description: "Liquid-chrome Y2K revival theme for indie musicians, zines, and merch shops." + description: "Millennium-web band theme — liquid-chrome bevels, frosted plastic, marquees, glitter, a webring, and a canvas metaball hero." page_templates: - system: y2k key: default title: Default - description: "Chrome navbar + content + footer." + description: "Chrome header, sparkle backdrop, plastic content column, chrome footer" + slots: [header, main, footer] + - system: y2k + key: full-width + title: Full Width + description: "Edge-to-edge showcase layout with a mesh backdrop, no max-width" slots: [header, main, footer] - system: y2k key: landing title: Landing - description: "Hero metaball + marquee + CTAs." - slots: [hero, marquee, main, cta, footer] + description: "Canvas metaball hero, chrome feature grid, glitter CTA, chrome footer" + slots: [header, main, footer] - system: y2k key: article - title: Article / Zine Page - description: "Narrow column with sidebar tracklist." + title: Article / Liner Notes + description: "Prose column with a chrome sidebar aside — liner-notes layout" slots: [header, main, aside, footer] - system: y2k - key: full-width - title: Full Width - description: "Edge-to-edge gradient mesh canvas." + key: blog-index + title: Blog Index + description: "Zine feed of chrome post cards" + slots: [header, main, footer] + - system: y2k + key: contact + title: Contact + description: "Chrome contact console — form panel plus a plastic contact card" + slots: [header, main, footer] + - system: y2k + key: auth + title: Auth + description: "Centered chrome auth panel for login / register / reset" slots: [header, main, footer] +# All 49 mandatory-tier builtin overrides (OVERRIDE-CONTRACT §1), dashed keys. template_overrides: + # Chrome (4) + - { template: y2k, block: navbar } + - { template: y2k, block: footer } + - { template: y2k, block: utility-bar } + - { template: y2k, block: announcement-bar } + # Marketing (12) + - { template: y2k, block: hero } + - { template: y2k, block: feature-grid } + - { template: y2k, block: rich-section } + - { template: y2k, block: stats } + - { template: y2k, block: testimonial } + - { template: y2k, block: logo-strip } + - { template: y2k, block: pricing } + - { template: y2k, block: team } + - { template: y2k, block: faq } + - { template: y2k, block: timeline } + - { template: y2k, block: cta } + - { template: y2k, block: countdown } + # Primitives (11) - { template: y2k, block: heading } - { template: y2k, block: text } - - { template: y2k, block: button } + - { template: y2k, block: rich-text } - { template: y2k, block: image } + - { template: y2k, block: quote } + - { template: y2k, block: divider } + - { template: y2k, block: button } + - { template: y2k, block: card } + - { template: y2k, block: gallery } + - { template: y2k, block: video-embed } + - { template: y2k, block: audio-embed } + # Commerce / local (6) + - { template: y2k, block: menu-list } + - { template: y2k, block: event-list } + - { template: y2k, block: contact-form } + - { template: y2k, block: contact-card } + - { template: y2k, block: hours-location } + - { template: y2k, block: social-links } + # Blog family (12) + - { template: y2k, block: blog-hero } + - { template: y2k, block: featured-posts } + - { template: y2k, block: related-posts } + - { template: y2k, block: popular-posts } + - { template: y2k, block: category-list } + - { template: y2k, block: post-hero } + - { template: y2k, block: post-metadata } + - { template: y2k, block: author-bio } + - { template: y2k, block: author-bio-hero } + - { template: y2k, block: newsletter-signup } + - { template: y2k, block: breadcrumbs } + - { template: y2k, block: sidebar-nav } + # Auth surface (3) + - { template: y2k, block: auth-form } + - { template: y2k, block: password-reset-form } + - { template: y2k, block: auth-status } + # System (1) + - { template: y2k, block: page-suggestions } email_wrappers: - y2k css: input_css_append: | - /* === y2k theme — chrome/bevel/mesh tokens === */ + /* ============================================================ + Y2K — millennium-web band theme (WO-TF-026). + Liquid chrome, frosted-plastic bevels, gradient mesh, marquee, + glitter/sparkle, webring badge, and a canvas metaball hero. + Light = frosted iMac plastic. Dark = rave blacklight. + Every colour resolves through the host shadcn HSL tokens via + hsl(var(--token)); the extra chrome/mesh/bevel custom properties + below are HSL triples consumed the same way. No literal hex/rgb. + ============================================================ */ + + /* --- Chrome / mesh / bevel channel tokens ----------------- */ :root { - --chrome-1: 280 30% 95%; - --chrome-2: 200 40% 75%; - --chrome-3: 320 60% 60%; - --chrome-4: 280 40% 25%; - --mesh-a: 320 95% 58%; - --mesh-b: 185 90% 60%; - --mesh-c: 60 95% 70%; + --chrome-1: 220 40% 96%; + --chrome-2: 205 45% 82%; + --chrome-3: 300 45% 78%; + --chrome-4: 240 35% 55%; + --mesh-a: 320 95% 62%; + --mesh-b: 190 90% 60%; + --mesh-c: 55 95% 68%; --bevel-light: 0 0% 100%; - --bevel-dark: 280 60% 12%; + --bevel-dark: 250 45% 22%; } .dark { - --chrome-1: 280 30% 30%; - --chrome-2: 200 40% 45%; - --chrome-3: 320 60% 50%; - --chrome-4: 280 40% 10%; - --mesh-a: 320 100% 65%; - --mesh-b: 185 95% 55%; - --mesh-c: 60 100% 65%; - --bevel-light: 0 0% 100%; - --bevel-dark: 280 80% 4%; + --chrome-1: 280 35% 24%; + --chrome-2: 250 40% 18%; + --chrome-3: 315 55% 30%; + --chrome-4: 265 45% 10%; + --mesh-a: 320 100% 66%; + --mesh-b: 150 95% 58%; + --mesh-c: 275 95% 66%; + --bevel-light: 300 60% 78%; + --bevel-dark: 270 80% 4%; } - - /* === keyframes === */ - @keyframes marquee-x { + + /* --- Keyframes -------------------------------------------- */ + @keyframes y2k-marquee-x { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(-50%, 0, 0); } } - @keyframes sparkle { - 0%, 100% { opacity: 0; transform: scale(0.5) rotate(0deg); } + @keyframes y2k-sparkle { + 0%, 100% { opacity: 0; transform: scale(0.4) rotate(0deg); } 50% { opacity: 1; transform: scale(1) rotate(180deg); } } - @keyframes metaball-morph { - 0%, 100% { d: path("M 50 100 a 50 50 0 1 1 100 0 a 50 50 0 1 1 -100 0"); } - 50% { d: path("M 40 95 a 60 55 0 1 1 120 5 a 55 60 0 1 1 -120 -5"); } + @keyframes y2k-shimmer-sweep { + 0% { background-position: -150% 0; } + 100% { background-position: 250% 0; } } - - /* === chrome surface utilities === */ + @keyframes y2k-blink { + 0%, 50% { opacity: 1; } + 51%, 100% { opacity: 0; } + } + @keyframes y2k-pulse { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.45; transform: scale(0.82); } + } + + /* --- Typography ------------------------------------------- */ + /* Families flow through the admin-bound CSS variables; the fallback + leads with the bundled OFL family so the intended look survives + before the admin assigns a slot. @font-face for the bundled + families is auto-emitted from fonts.json (source=template rows). */ + .y2k-body { + font-family: var(--font-body, "Inter", system-ui, -apple-system, "Segoe UI", sans-serif); + } + .y2k-mono, + .y2k-mono code, + pre.y2k-mono, + code.y2k-mono { + font-family: var(--font-mono, "Share Tech Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, monospace); + } + .y2k-display { + font-family: var(--font-heading, "Orbitron", "Michroma", system-ui, sans-serif); + letter-spacing: 0.02em; + } + /* Beveled chrome headline — bright top rim, dark under-shadow, coloured halo. */ + .y2k-heading, + .glitch { + font-family: var(--font-heading, "Orbitron", "Michroma", system-ui, sans-serif); + letter-spacing: 0.03em; + text-shadow: + 0 1px 0 hsl(var(--bevel-light) / 0.7), + 0 2px 0 hsl(var(--bevel-dark) / 0.35), + 0 6px 22px hsl(var(--primary) / 0.4); + } + /* Chrome shimmer sweep across big headlines (motion, reduced-motion-safe). */ + .glitch { + position: relative; + background-image: linear-gradient( + 100deg, + transparent 20%, + hsl(var(--bevel-light) / 0.65) 45%, + hsl(var(--accent) / 0.55) 50%, + transparent 60%); + background-size: 220% 100%; + background-repeat: no-repeat; + -webkit-background-clip: text; + background-clip: text; + animation: y2k-shimmer-sweep 5.5s ease-in-out infinite; + } + .y2k-prose, + .y2k-prose p, + .y2k-prose li { + font-family: var(--font-body, "Inter", system-ui, sans-serif); + line-height: 1.75; + } + .y2k-prose code { + color: hsl(var(--primary)); + background-color: hsl(var(--muted)); + padding: 0.125rem 0.375rem; + border-radius: 0.25rem; + font-family: var(--font-mono, "Share Tech Mono", ui-monospace, monospace); + font-size: 0.875em; + } + /* Eyebrow / kicker: mono, uppercase, wide-tracked chrome readout. */ + .y2k-eyebrow { + font-family: var(--font-mono, "Share Tech Mono", ui-monospace, monospace); + text-transform: uppercase; + letter-spacing: 0.22em; + font-size: 0.75rem; + color: hsl(var(--primary)); + } + + /* --- Chrome surfaces / backdrops -------------------------- */ .y2k-chrome-bg { - background-image: - linear-gradient(180deg, - hsl(var(--chrome-1)) 0%, - hsl(var(--chrome-2)) 35%, - hsl(var(--chrome-3)) 70%, - hsl(var(--chrome-4)) 100%); + background-image: linear-gradient(180deg, + hsl(var(--chrome-1)) 0%, + hsl(var(--chrome-2)) 35%, + hsl(var(--chrome-3)) 70%, + hsl(var(--chrome-4)) 100%); } .y2k-mesh-bg { background-image: - radial-gradient(at 20% 20%, hsl(var(--mesh-a) / 0.85) 0%, transparent 55%), - radial-gradient(at 80% 30%, hsl(var(--mesh-b) / 0.80) 0%, transparent 55%), - radial-gradient(at 50% 80%, hsl(var(--mesh-c) / 0.70) 0%, transparent 55%), + radial-gradient(at 18% 20%, hsl(var(--mesh-a) / 0.8) 0%, transparent 55%), + radial-gradient(at 82% 28%, hsl(var(--mesh-b) / 0.75) 0%, transparent 55%), + radial-gradient(at 50% 82%, hsl(var(--mesh-c) / 0.65) 0%, transparent 55%), linear-gradient(135deg, hsl(var(--background)) 0%, hsl(var(--card)) 100%); } - .y2k-bevel { + /* Sticky header chrome bar. */ + .y2k-chrome { + background-image: linear-gradient(180deg, + hsl(var(--chrome-1) / 0.92) 0%, + hsl(var(--chrome-2) / 0.88) 100%); + backdrop-filter: saturate(1.3) blur(6px); + box-shadow: inset 0 1px 0 hsl(var(--bevel-light) / 0.7); + } + + /* --- Bevel / frame / glow --------------------------------- */ + .y2k-bevel, + .hud-frame { + position: relative; border: 2px solid hsl(var(--border)); box-shadow: - inset 0 1px 0 hsl(var(--bevel-light) / 0.85), - inset 0 -2px 0 hsl(var(--bevel-dark) / 0.35), - 0 2px 6px hsl(var(--bevel-dark) / 0.30); + inset 0 1px 0 hsl(var(--bevel-light) / 0.8), + inset 0 -2px 0 hsl(var(--bevel-dark) / 0.3), + 0 3px 10px hsl(var(--bevel-dark) / 0.28); } - .y2k-bevel:active { + .y2k-image-frame { + border-radius: 0.75rem; + border: 2px solid hsl(var(--border)); box-shadow: - inset 0 2px 4px hsl(var(--bevel-dark) / 0.45), - inset 0 -1px 0 hsl(var(--bevel-light) / 0.40); + inset 0 1px 0 hsl(var(--bevel-light) / 0.5), + 0 4px 16px hsl(var(--bevel-dark) / 0.35); } - .y2k-glow { - box-shadow: 0 0 24px hsl(var(--primary) / 0.45); + .y2k-glow, + .neon-edge-magenta { + box-shadow: 0 0 0 1px hsl(var(--primary) / 0.4), 0 0 22px hsl(var(--primary) / 0.28); + transition: box-shadow 180ms ease, transform 180ms ease; } - - /* === marquee === */ + .y2k-glow:hover, .y2k-glow:focus-within, + .neon-edge-magenta:hover, .neon-edge-magenta:focus-within { + box-shadow: 0 0 0 1px hsl(var(--primary) / 0.85), 0 0 32px hsl(var(--primary) / 0.48); + transform: translateY(-2px); + } + .neon-edge-cyan { + box-shadow: 0 0 0 1px hsl(var(--accent) / 0.4), 0 0 22px hsl(var(--accent) / 0.28); + transition: box-shadow 180ms ease, transform 180ms ease; + } + .neon-edge-cyan:hover, .neon-edge-cyan:focus-within { + box-shadow: 0 0 0 1px hsl(var(--accent) / 0.85), 0 0 32px hsl(var(--accent) / 0.48); + transform: translateY(-2px); + } + .neon-edge-lime { + box-shadow: 0 0 0 1px hsl(var(--ring) / 0.4), 0 0 22px hsl(var(--ring) / 0.28); + transition: box-shadow 180ms ease, transform 180ms ease; + } + .neon-edge-lime:hover, .neon-edge-lime:focus-within { + box-shadow: 0 0 0 1px hsl(var(--ring) / 0.85), 0 0 32px hsl(var(--ring) / 0.48); + transform: translateY(-2px); + } + /* clip-cut renders as soft rounded plastic corners in Y2K. */ + .clip-cut { border-radius: 0.75rem; } + + /* --- Buttons ---------------------------------------------- */ + .y2k-button, + .y2k-btn, + .rgb-split { + transition: transform 90ms ease, box-shadow 90ms ease, filter 90ms ease; + } + .y2k-button { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.65rem 1.25rem; + font-weight: 600; + border: 2px solid hsl(var(--border)); + border-radius: 0.75rem; + background-image: linear-gradient(180deg, + hsl(var(--primary) / 0.96) 0%, + hsl(var(--primary)) 50%, + hsl(var(--primary) / 0.82) 100%); + color: hsl(var(--primary-foreground)); + box-shadow: + inset 0 1px 0 hsl(var(--bevel-light) / 0.7), + inset 0 -2px 0 hsl(var(--bevel-dark) / 0.3), + 0 2px 8px hsl(var(--bevel-dark) / 0.25); + } + .y2k-btn { border-radius: 0.75rem; } + .y2k-button:hover, .y2k-btn:hover, .rgb-split:hover, .rgb-split:focus-visible { + transform: translateY(-1px); + filter: brightness(1.05); + } + .y2k-button:active, .y2k-btn:active, .rgb-split:active { + transform: translateY(1px); + box-shadow: + inset 0 2px 5px hsl(var(--bevel-dark) / 0.45), + inset 0 -1px 0 hsl(var(--bevel-light) / 0.3); + } + .y2k-button:focus-visible, .y2k-btn:focus-visible, + .rgb-split:focus-visible, + button:focus-visible, a:focus-visible { + outline: 2px solid hsl(var(--ring)); + outline-offset: 2px; + } + + /* --- Cards ------------------------------------------------ */ + .y2k-card { + border-radius: 0.75rem; + box-shadow: + inset 0 1px 0 hsl(var(--bevel-light) / 0.55), + 0 4px 14px hsl(var(--bevel-dark) / 0.22); + } + + /* --- Marquee ---------------------------------------------- */ .y2k-marquee { overflow: hidden; position: relative; @@ -123,9 +367,10 @@ css: display: inline-flex; gap: 3rem; white-space: nowrap; - animation-name: marquee-x; + animation-name: y2k-marquee-x; animation-timing-function: linear; animation-iteration-count: infinite; + animation-duration: 28s; } .y2k-marquee-track[data-speed="slow"] { animation-duration: 60s; } .y2k-marquee-track[data-speed="medium"] { animation-duration: 30s; } @@ -134,131 +379,117 @@ css: .y2k-marquee:focus-within .y2k-marquee-track { animation-play-state: paused; } - - /* === sparkle decoration === */ + + /* --- Sparkle / glitter atmosphere ------------------------- */ .y2k-sparkle { pointer-events: none; - animation: sparkle 1.6s ease-in-out infinite; + animation: y2k-sparkle 1.6s ease-in-out infinite; } - - /* === metaball morph === */ - .y2k-metaball { - animation: metaball-morph 6s ease-in-out infinite; + /* scanlines (ported name) is repurposed as a faint glitter dot field. */ + .scanlines { + opacity: 0.4; + background-image: + radial-gradient(hsl(var(--primary) / 0.5) 1px, transparent 1.5px), + radial-gradient(hsl(var(--accent) / 0.4) 1px, transparent 1.5px); + background-size: 42px 42px, 68px 68px; + background-position: 0 0, 24px 30px; } - - /* === webring 88x31 badge === */ - .y2k-webring-badge { + + /* --- Status / caret -------------------------------------- */ + .y2k-status-dot { + animation: y2k-pulse 2.2s ease-in-out infinite; + } + .caret-blink { display: inline-block; + animation: y2k-blink 1.05s steps(2, end) infinite; + } + + /* --- Webring 88x31 badge ---------------------------------- */ + .y2k-webring-badge { + display: inline-flex; + align-items: center; + justify-content: center; width: 88px; height: 31px; - line-height: 31px; - text-align: center; font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; border: 1px solid hsl(var(--border)); background-image: linear-gradient(180deg, hsl(var(--chrome-1)) 0%, hsl(var(--chrome-3)) 100%); - color: hsl(var(--primaryForeground)); + color: hsl(var(--primary-foreground)); text-decoration: none; box-shadow: inset 0 1px 0 hsl(var(--bevel-light) / 0.6); } - - /* === holographic foil for NFT gallery === */ - .y2k-foil { - position: relative; - isolation: isolate; + + /* --- Static waveform strip (no canvas — motion tier) ------ */ + .y2k-waveform { + background-image: repeating-linear-gradient( + 90deg, + hsl(var(--primary) / 0.65) 0 2px, + hsl(var(--accent) / 0.4) 2px 4px, + transparent 4px 8px); + -webkit-mask-image: linear-gradient(90deg, transparent, hsl(0 0% 0%) 8%, hsl(0 0% 0%) 92%, transparent); + mask-image: linear-gradient(90deg, transparent, hsl(0 0% 0%) 8%, hsl(0 0% 0%) 92%, transparent); + border-radius: 0.25rem; } - .y2k-foil::after { - content: ""; + + /* --- Empty state / article aside -------------------------- */ + .y2k-empty { + font-family: var(--font-mono, "Share Tech Mono", ui-monospace, monospace); + letter-spacing: 0.14em; + } + .y2k-aside { border-radius: 0.75rem; } + + /* --- Canvas metaball showpiece (hero / landing only) ------ */ + /* The lazy-inits from the hero's inline script; this styles the + container plus the static no-JS chrome-mesh fallback. */ + .y2k-orb-wrap { position: absolute; inset: 0; + overflow: hidden; pointer-events: none; - opacity: 0; - transition: opacity 220ms ease; - background-image: conic-gradient( - from 0deg, - hsl(var(--mesh-a) / 0.45), - hsl(var(--mesh-b) / 0.45), - hsl(var(--mesh-c) / 0.45), - hsl(var(--mesh-a) / 0.45) - ); - mix-blend-mode: color-dodge; - filter: hue-rotate(0deg); } - .y2k-foil:hover::after, - .y2k-foil:focus-within::after { - opacity: 1; - filter: hue-rotate(45deg); + .y2k-orb-canvas { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + display: block; + opacity: 0.65; + filter: blur(2px) contrast(1.15); } - - /* === plastic button override === */ - .y2k-button { - display: inline-flex; - align-items: center; - gap: 0.5rem; - padding: 0.65rem 1.25rem; - font-weight: 600; - border: 2px solid hsl(var(--border)); - border-radius: 0.625rem; - background-image: linear-gradient(180deg, - hsl(var(--primary) / 0.95) 0%, - hsl(var(--primary)) 50%, - hsl(var(--primary) / 0.85) 100%); - color: hsl(var(--primaryForeground)); - box-shadow: - inset 0 1px 0 hsl(var(--bevel-light) / 0.7), - inset 0 -2px 0 hsl(var(--bevel-dark) / 0.3), - 0 2px 6px hsl(var(--bevel-dark) / 0.25); - transition: transform 90ms ease, box-shadow 90ms ease; + .y2k-orb-fallback { + position: absolute; + inset: 0; + background-image: + radial-gradient(60% 55% at 30% 35%, hsl(var(--mesh-a) / 0.55), transparent 60%), + radial-gradient(55% 50% at 72% 45%, hsl(var(--mesh-b) / 0.5), transparent 60%), + radial-gradient(50% 45% at 50% 80%, hsl(var(--mesh-c) / 0.45), transparent 60%), + linear-gradient(to bottom, transparent, hsl(var(--background) / 0.7)); } - .y2k-button:hover { transform: translateY(-1px); } - .y2k-button:active { - transform: translateY(1px); - box-shadow: - inset 0 2px 4px hsl(var(--bevel-dark) / 0.45), - inset 0 -1px 0 hsl(var(--bevel-light) / 0.30); - } - .y2k-button:focus-visible { - outline: 2px solid hsl(var(--ring)); - outline-offset: 2px; - } - - /* === image chrome frame === */ - .y2k-image-frame { - border-radius: 0.625rem; - border: 2px solid hsl(var(--border)); - box-shadow: - inset 0 1px 0 hsl(var(--bevel-light) / 0.5), - 0 4px 14px hsl(var(--bevel-dark) / 0.35); - } - - /* === heading chrome stroke === */ - .y2k-heading { - font-family: var(--font-heading, "Stretch Pro", "VT323", "Courier New", monospace); - letter-spacing: 0.04em; - text-shadow: - 0 1px 0 hsl(var(--bevel-light) / 0.65), - 0 2px 0 hsl(var(--bevel-dark) / 0.35), - 0 6px 22px hsl(var(--primary) / 0.45); - } - - /* === text holographic link underline === */ - .y2k-text a { - background-image: linear-gradient(90deg, - hsl(var(--mesh-a)), - hsl(var(--mesh-b)), - hsl(var(--mesh-c))); - background-size: 100% 2px; - background-repeat: no-repeat; - background-position: 0 100%; - text-decoration: none; - } - - /* === reduced motion overrides === */ + + /* --- Reduced motion: kill all motion ---------------------- */ @media (prefers-reduced-motion: reduce) { .y2k-marquee-track, .y2k-sparkle, - .y2k-metaball { + .glitch, + .caret-blink, + .y2k-status-dot, + .y2k-orb-canvas { animation: none !important; + animation-duration: 0.01ms !important; } + .glitch { + background-image: none; + -webkit-background-clip: border-box; + background-clip: border-box; + } + .y2k-orb-canvas { display: none; } + .y2k-button:hover, .y2k-btn:hover, .rgb-split:hover { transform: none; } + } + + /* --- High-contrast: drop the glitter + orbs --------------- */ + @media (prefers-contrast: more) { + .scanlines, + .y2k-orb-canvas { display: none; } } diff --git a/master_pages.json b/master_pages.json index 3a8f761..a01fcf2 100644 --- a/master_pages.json +++ b/master_pages.json @@ -1,24 +1,22 @@ [ { - "key": "y2k:default-master", - "title": "Y2K Default Master", - "page_templates": ["default", "article"], + "key": "y2k:site-master", + "title": "Y2K Site Master", + "page_templates": ["default", "full-width", "landing", "article", "blog-index", "contact"], "blocks": [ - { "block_key": "y2k:chrome_navbar", "title": "Chrome Navbar", "content": { "menuName": "main", "logoText": "y2k.fm" }, "slot": "header", "sort_order": 0 }, - { "block_key": "y2k:marquee", "title": "Top Marquee", "content": { "items": ["new drop friday", "listen on bandcamp", "subscribe to the zine"], "speed": "medium" }, "slot": "header", "sort_order": 1 }, - { "block_key": "slot", "title": "Main Slot", "content": { "slotName": "main" }, "slot": "main", "sort_order": 0 }, - { "block_key": "y2k:footer_chrome", "title": "Chrome Footer", "content": { "showWebring": true, "copyright": "(c) y2k.fm" }, "slot": "footer", "sort_order": 0 } + { "block_key": "navbar", "title": "Chrome Navbar", "content": { "menuName": "main", "logoType": "text", "logoText": { "text": "CHROME STATIC" }, "companyName": "Chrome Static", "stickyBehavior": "sticky", "cta": { "text": "Listen", "url": "/#listen" } }, "slot": "header", "sort_order": 0 }, + { "block_key": "slot", "title": "Main Content", "content": { "slotName": "main", "placeholder": "Page content" }, "slot": "main", "sort_order": 0 }, + { "block_key": "footer", "title": "Chrome Footer", "content": { "layout": "columns", "companyName": "Chrome Static", "tagline": "A four-piece from the frosted end of the millennium.", "copyright": "Chrome Static" }, "slot": "footer", "sort_order": 0 } ] }, { - "key": "y2k:landing-master", - "title": "Y2K Landing Master", - "page_templates": ["landing", "full-width"], + "key": "y2k:auth-master", + "title": "Y2K Auth Master", + "page_templates": ["auth"], "blocks": [ - { "block_key": "y2k:metaball_hero", "title": "Metaball Hero", "content": { "headline": "new EP out now", "sub": "liquid chrome dreams", "ctaLabel": "listen" }, "slot": "hero", "sort_order": 0 }, - { "block_key": "y2k:marquee", "title": "Marquee", "content": { "items": ["* new * new * new"], "speed": "fast" }, "slot": "marquee", "sort_order": 0 }, - { "block_key": "slot", "title": "Main Slot", "content": { "slotName": "main" }, "slot": "main", "sort_order": 0 }, - { "block_key": "y2k:footer_chrome", "title": "Chrome Footer", "content": { "showWebring": true }, "slot": "footer", "sort_order": 0 } + { "block_key": "navbar", "title": "Chrome Navbar", "content": { "menuName": "main", "logoType": "text", "logoText": { "text": "CHROME STATIC" }, "companyName": "Chrome Static" }, "slot": "header", "sort_order": 0 }, + { "block_key": "slot", "title": "Auth Panel", "content": { "slotName": "main" }, "slot": "main", "sort_order": 0 }, + { "block_key": "footer", "title": "Chrome Footer", "content": { "layout": "minimal", "companyName": "Chrome Static", "tagline": "A four-piece from the frosted end of the millennium.", "copyright": "Chrome Static" }, "slot": "footer", "sort_order": 0 } ] } ] diff --git a/plugin.mod b/plugin.mod index 69cb004..37f7a2e 100644 --- a/plugin.mod +++ b/plugin.mod @@ -3,7 +3,8 @@ name = "y2k" display_name = "Y2K" scope = "@themes" version = "0.2.0" -description = "Liquid-chrome Y2K revival theme for indie musicians, zines, and merch shops. Gradient mesh backgrounds, plastic-bevel buttons, marquee text, waveform blocks." +description = "Millennium-web band theme — liquid-chrome bevels, frosted plastic, gradient mesh, marquees, glitter, a webring, and a canvas metaball hero. Built for musicians and bands." kind = "theme" categories = ["templates"] -tags = ["retro", "chrome", "vaporwave", "pastel", "music", "creator", "nostalgia", "web3"] +tags = ["retro", "chrome", "vaporwave", "music", "band", "creator", "nostalgia", "dark"] +required_icon_packs = ["lucide", "simple-icons"] diff --git a/presets.json b/presets.json index fce2c32..bd370e7 100644 --- a/presets.json +++ b/presets.json @@ -1,109 +1,251 @@ [ { - "id": "chrome-dream", - "name": "Liquid Chrome Dream", - "description": "Magenta-to-teal mesh on tinted glass cards. Works in both light and dark modes.", + "id": "chrome-candy", + "name": "Chrome Candy", + "description": "Bondi-blue frosted plastic in light, UV magenta rave in dark — the Y2K default", "theme": { "mode": "both", "lightColors": { - "background": "300 40% 97%", - "foreground": "280 60% 12%", - "card": "300 30% 99%", - "cardForeground": "280 60% 12%", - "popover": "300 30% 99%", - "popoverForeground": "280 60% 12%", - "primary": "320 95% 58%", + "background": "205 55% 96%", + "foreground": "230 40% 14%", + "card": "205 45% 99%", + "cardForeground": "230 40% 14%", + "popover": "205 45% 99%", + "popoverForeground": "230 40% 14%", + "primary": "320 90% 55%", "primaryForeground": "0 0% 100%", - "secondary": "185 90% 60%", - "secondaryForeground": "200 80% 10%", - "muted": "300 25% 94%", - "mutedForeground": "280 25% 40%", - "accent": "60 95% 70%", - "accentForeground": "280 60% 12%", - "destructive": "0 90% 60%", - "destructiveForeground": "0 0% 100%", - "border": "300 40% 88%", - "input": "300 30% 92%", - "ring": "320 95% 58%" - }, - "darkColors": { - "background": "280 35% 6%", - "foreground": "300 30% 96%", - "card": "280 40% 10%", - "cardForeground": "300 30% 96%", - "popover": "280 40% 10%", - "popoverForeground": "300 30% 96%", - "primary": "320 100% 65%", - "primaryForeground": "280 40% 6%", - "secondary": "185 95% 55%", - "secondaryForeground": "200 80% 8%", - "muted": "280 30% 14%", - "mutedForeground": "300 20% 65%", - "accent": "60 100% 65%", - "accentForeground": "280 60% 8%", - "destructive": "0 95% 60%", - "destructiveForeground": "0 0% 100%", - "border": "300 40% 22%", - "input": "280 30% 16%", - "ring": "320 100% 65%" - } - } - }, - { - "id": "cd-rom-after-hours", - "name": "CD-ROM After Hours", - "description": "After-hours CD-ROM glow — near-black with neon pink and cyan edge leaks.", - "theme": { - "mode": "dark", - "darkColors": { - "background": "260 50% 5%", - "foreground": "180 100% 88%", - "card": "260 50% 9%", - "cardForeground": "180 100% 88%", - "popover": "260 50% 9%", - "popoverForeground": "180 100% 88%", - "primary": "285 100% 62%", - "primaryForeground": "0 0% 100%", - "secondary": "180 100% 50%", - "secondaryForeground": "260 50% 5%", - "muted": "260 30% 14%", - "mutedForeground": "260 20% 60%", - "accent": "45 100% 60%", - "accentForeground": "260 50% 5%", - "destructive": "0 90% 55%", - "destructiveForeground": "0 0% 100%", - "border": "285 60% 25%", - "input": "260 30% 16%", - "ring": "285 100% 62%" - } - } - }, - { - "id": "bubblegum-trapper", - "name": "Bubblegum Trapper", - "description": "Lisa Frank Trapper-Keeper energy — pastel candy and sour apple accents.", - "theme": { - "mode": "light", - "lightColors": { - "background": "45 100% 97%", - "foreground": "300 50% 15%", - "card": "0 0% 100%", - "cardForeground": "300 50% 15%", - "popover": "0 0% 100%", - "popoverForeground": "300 50% 15%", - "primary": "330 95% 60%", - "primaryForeground": "0 0% 100%", - "secondary": "200 90% 70%", - "secondaryForeground": "200 80% 10%", - "muted": "45 60% 92%", - "mutedForeground": "300 25% 45%", - "accent": "90 75% 60%", - "accentForeground": "90 80% 10%", + "secondary": "200 45% 90%", + "secondaryForeground": "230 40% 14%", + "muted": "205 35% 92%", + "mutedForeground": "225 20% 40%", + "accent": "190 85% 45%", + "accentForeground": "230 40% 12%", "destructive": "0 85% 55%", "destructiveForeground": "0 0% 100%", - "border": "330 50% 85%", - "input": "330 40% 90%", - "ring": "330 95% 60%" + "border": "205 35% 84%", + "input": "205 35% 84%", + "ring": "270 80% 58%" + }, + "darkColors": { + "background": "260 45% 6%", + "foreground": "300 25% 95%", + "card": "265 40% 10%", + "cardForeground": "300 25% 95%", + "popover": "265 40% 10%", + "popoverForeground": "300 25% 95%", + "primary": "320 100% 62%", + "primaryForeground": "260 45% 6%", + "secondary": "265 35% 16%", + "secondaryForeground": "300 25% 95%", + "muted": "265 30% 14%", + "mutedForeground": "285 20% 68%", + "accent": "150 95% 58%", + "accentForeground": "260 45% 6%", + "destructive": "0 95% 64%", + "destructiveForeground": "260 45% 6%", + "border": "270 40% 22%", + "input": "270 40% 20%", + "ring": "275 100% 68%" + } + } + }, + { + "id": "bubblegum", + "name": "Bubblegum", + "description": "Cotton-candy pink plastic in light, hot-pink blacklight in dark", + "theme": { + "mode": "both", + "lightColors": { + "background": "330 60% 97%", + "foreground": "320 35% 16%", + "card": "330 50% 99%", + "cardForeground": "320 35% 16%", + "popover": "330 50% 99%", + "popoverForeground": "320 35% 16%", + "primary": "330 90% 58%", + "primaryForeground": "0 0% 100%", + "secondary": "325 45% 92%", + "secondaryForeground": "320 35% 16%", + "muted": "330 35% 93%", + "mutedForeground": "320 20% 42%", + "accent": "270 80% 60%", + "accentForeground": "0 0% 100%", + "destructive": "0 85% 55%", + "destructiveForeground": "0 0% 100%", + "border": "330 35% 86%", + "input": "330 35% 86%", + "ring": "330 90% 58%" + }, + "darkColors": { + "background": "315 45% 6%", + "foreground": "330 30% 95%", + "card": "315 42% 10%", + "cardForeground": "330 30% 95%", + "popover": "315 42% 10%", + "popoverForeground": "330 30% 95%", + "primary": "330 100% 66%", + "primaryForeground": "315 45% 6%", + "secondary": "315 35% 16%", + "secondaryForeground": "330 30% 95%", + "muted": "315 30% 14%", + "mutedForeground": "325 20% 68%", + "accent": "275 100% 72%", + "accentForeground": "315 45% 6%", + "destructive": "0 95% 64%", + "destructiveForeground": "315 45% 6%", + "border": "320 40% 22%", + "input": "320 40% 20%", + "ring": "330 100% 66%" + } + } + }, + { + "id": "lime-fizz", + "name": "Lime Fizz", + "description": "Frosted lime soda in light, radioactive green rave in dark", + "theme": { + "mode": "both", + "lightColors": { + "background": "150 45% 96%", + "foreground": "170 35% 12%", + "card": "150 40% 99%", + "cardForeground": "170 35% 12%", + "popover": "150 40% 99%", + "popoverForeground": "170 35% 12%", + "primary": "150 75% 38%", + "primaryForeground": "0 0% 100%", + "secondary": "150 35% 90%", + "secondaryForeground": "170 35% 12%", + "muted": "150 28% 92%", + "mutedForeground": "170 18% 40%", + "accent": "320 85% 55%", + "accentForeground": "0 0% 100%", + "destructive": "0 85% 55%", + "destructiveForeground": "0 0% 100%", + "border": "150 28% 82%", + "input": "150 28% 82%", + "ring": "150 75% 38%" + }, + "darkColors": { + "background": "165 40% 5%", + "foreground": "140 30% 92%", + "card": "168 38% 8%", + "cardForeground": "140 30% 92%", + "popover": "168 38% 8%", + "popoverForeground": "140 30% 92%", + "primary": "145 100% 55%", + "primaryForeground": "165 40% 5%", + "secondary": "168 30% 14%", + "secondaryForeground": "140 30% 92%", + "muted": "168 26% 12%", + "mutedForeground": "150 18% 66%", + "accent": "320 100% 66%", + "accentForeground": "165 40% 5%", + "destructive": "0 95% 64%", + "destructiveForeground": "165 40% 5%", + "border": "155 35% 20%", + "input": "155 35% 18%", + "ring": "145 100% 55%" + } + } + }, + { + "id": "graphite", + "name": "Graphite", + "description": "Brushed-silver chrome in light, restrained slate blacklight in dark", + "theme": { + "mode": "both", + "lightColors": { + "background": "220 18% 95%", + "foreground": "225 25% 14%", + "card": "220 20% 99%", + "cardForeground": "225 25% 14%", + "popover": "220 20% 99%", + "popoverForeground": "225 25% 14%", + "primary": "230 30% 30%", + "primaryForeground": "0 0% 100%", + "secondary": "220 16% 89%", + "secondaryForeground": "225 25% 14%", + "muted": "220 14% 91%", + "mutedForeground": "225 14% 42%", + "accent": "265 70% 58%", + "accentForeground": "0 0% 100%", + "destructive": "0 85% 55%", + "destructiveForeground": "0 0% 100%", + "border": "220 16% 82%", + "input": "220 16% 82%", + "ring": "265 70% 58%" + }, + "darkColors": { + "background": "230 25% 6%", + "foreground": "220 18% 92%", + "card": "230 24% 9%", + "cardForeground": "220 18% 92%", + "popover": "230 24% 9%", + "popoverForeground": "220 18% 92%", + "primary": "225 30% 78%", + "primaryForeground": "230 25% 6%", + "secondary": "230 22% 15%", + "secondaryForeground": "220 18% 92%", + "muted": "230 20% 13%", + "mutedForeground": "220 14% 66%", + "accent": "270 95% 72%", + "accentForeground": "230 25% 6%", + "destructive": "0 95% 64%", + "destructiveForeground": "230 25% 6%", + "border": "230 25% 20%", + "input": "230 25% 18%", + "ring": "270 95% 72%" + } + } + }, + { + "id": "tangerine", + "name": "Tangerine", + "description": "Translucent tangerine iMac in light, sunset-orange rave in dark", + "theme": { + "mode": "both", + "lightColors": { + "background": "35 70% 96%", + "foreground": "20 40% 15%", + "card": "35 60% 99%", + "cardForeground": "20 40% 15%", + "popover": "35 60% 99%", + "popoverForeground": "20 40% 15%", + "primary": "20 90% 52%", + "primaryForeground": "0 0% 100%", + "secondary": "35 50% 90%", + "secondaryForeground": "20 40% 15%", + "muted": "35 40% 92%", + "mutedForeground": "25 22% 40%", + "accent": "180 75% 40%", + "accentForeground": "0 0% 100%", + "destructive": "0 85% 55%", + "destructiveForeground": "0 0% 100%", + "border": "35 40% 84%", + "input": "35 40% 84%", + "ring": "20 90% 52%" + }, + "darkColors": { + "background": "18 45% 6%", + "foreground": "30 35% 93%", + "card": "18 42% 9%", + "cardForeground": "30 35% 93%", + "popover": "18 42% 9%", + "popoverForeground": "30 35% 93%", + "primary": "22 100% 60%", + "primaryForeground": "18 45% 6%", + "secondary": "18 35% 15%", + "secondaryForeground": "30 35% 93%", + "muted": "18 30% 13%", + "mutedForeground": "25 22% 66%", + "accent": "175 95% 55%", + "accentForeground": "18 45% 6%", + "destructive": "0 95% 64%", + "destructiveForeground": "18 45% 6%", + "border": "20 40% 20%", + "input": "20 40% 18%", + "ring": "22 100% 60%" } } } diff --git a/seed/seed.json b/seed/seed.json new file mode 100644 index 0000000..1dfbfa5 --- /dev/null +++ b/seed/seed.json @@ -0,0 +1,222 @@ +{ + "demo": true, + "settings": { + "merge": { + "companyName": "Chrome Static", + "tagline": "A four-piece from the frosted end of the millennium." + } + }, + "data_tables": [ + { + "key": "contact_submissions", + "name": "Contact Enquiries", + "description": "Seeded contact-form submissions for the Chrome Static demo. A submission becomes a row here and emails the site admins.", + "columns": [ + { "key": "name", "label": "Name", "type": "text", "required": true }, + { "key": "email", "label": "Email", "type": "email", "required": true }, + { "key": "subject", "label": "Subject", "type": "text", "required": false }, + { "key": "message", "label": "Message", "type": "text", "required": true } + ] + } + ], + "pages": [ + { + "slug": "/", + "title": "Chrome Static", + "template_key": "landing", + "blocks": [ + { + "block_key": "hero", + "title": "Hero", + "slot": "main", + "sort_order": 1, + "content": { + "template": "centered", + "minHeight": "screen", + "eyebrow": "New EP out now", + "headline": "Chrome Static", + "subheadline": "Four tracks. Recorded live to tape.", + "description": "We are a four-piece from Melbourne. We make loud, bright pop for the end of the millennium.", + "ctaPrimary": { "text": "Listen", "url": "#listen" }, + "ctaSecondary": { "text": "See the shows", "url": "#shows" } + } + }, + { + "block_key": "y2k:waveform_player", + "title": "Now playing", + "slot": "main", + "sort_order": 2, + "content": { + "trackTitle": "Frosted", + "artist": "Chrome Static", + "audioUrl": "", + "coverImage": "" + } + }, + { + "block_key": "y2k:discography", + "title": "Records", + "slot": "main", + "sort_order": 3, + "content": { + "heading": "Records", + "releases": [ + { "title": "Chrome Hearts", "year": "2001", "format": "LP", "cover": "", "link": "#" }, + { "title": "Dial-Up Dreams", "year": "2000", "format": "EP", "cover": "", "link": "#" }, + { "title": "Frosted", "year": "1999", "format": "Single", "cover": "", "link": "#" }, + { "title": "Skyline Demos", "year": "1998", "format": "Cassette", "cover": "", "link": "#" } + ] + } + }, + { + "block_key": "y2k:tour_dates", + "title": "Shows", + "slot": "main", + "sort_order": 4, + "content": { + "heading": "Shows", + "intro": "Doors an hour before. All ages unless noted.", + "dates": [ + { "date": "Fri 12 Sep", "venue": "The Chrome Room", "city": "Melbourne", "ticketUrl": "#", "soldOut": false }, + { "date": "Sat 13 Sep", "venue": "Rollerdrome", "city": "Sydney", "ticketUrl": "#", "soldOut": true }, + { "date": "Fri 19 Sep", "venue": "Basement 88", "city": "Brisbane", "ticketUrl": "#", "soldOut": false } + ] + } + }, + { + "block_key": "cta", + "title": "CTA", + "slot": "main", + "sort_order": 5, + "content": { + "background": "band", + "heading": "Join the mailing list", + "text": "One email when a record ships or a show goes up. Nothing else.", + "primaryLabel": "Sign up", + "primaryUrl": "/contact" + } + } + ] + }, + { + "slug": "/about", + "title": "The band", + "template_key": "default", + "blocks": [ + { "block_key": "heading", "title": "Heading", "slot": "main", "sort_order": 1, "content": { "text": "The band", "level": 1 } }, + { "block_key": "rich-text", "title": "Story", "slot": "main", "sort_order": 2, "content": { "html": "

Chrome Static started in a share house with a borrowed drum machine and a broken amp. We kept the sound and fixed the amp.

We record live to tape in one room. What you hear on the record is what we play on the night. Four people, no click track, one take when we can get it.

" } }, + { "block_key": "quote", "title": "Quote", "slot": "main", "sort_order": 3, "content": { "style": "pull", "quote": "Loud, bright, and done in one take.", "attribution": "Chrome Static" } }, + { "block_key": "divider", "title": "Divider", "slot": "main", "sort_order": 4, "content": { "variant": "line", "weight": "medium" } }, + { + "block_key": "y2k:tracklist", + "title": "Frosted tracklist", + "slot": "main", + "sort_order": 5, + "content": { + "tracks": [ + { "title": "Frosted", "duration": "3:12", "url": "" }, + { "title": "Dial Tone", "duration": "2:48", "url": "" }, + { "title": "Silver Static", "duration": "4:01", "url": "" }, + { "title": "Skyline", "duration": "3:35", "url": "" } + ] + } + } + ] + }, + { + "slug": "/blog", + "title": "Journal", + "template_key": "blog-index", + "blocks": [ + { "block_key": "card", "title": "Post: Tape", "slot": "main", "sort_order": 1, "content": { "layout": "vertical", "title": "Why we still track to tape", "text": "

Tape makes us commit. Here is what that costs and what we get back.

", "link": "/blog/tracking-to-tape", "linkLabel": "Read on" } }, + { "block_key": "card", "title": "Post: Tour", "slot": "main", "sort_order": 2, "content": { "layout": "vertical", "title": "Notes from the September run", "text": "

Three cities in a van. What went right and what broke.

", "link": "/blog/september-run", "linkLabel": "Read on" } }, + { "block_key": "card", "title": "Post: Gear", "slot": "main", "sort_order": 3, "content": { "layout": "vertical", "title": "The one pedal we cannot replace", "text": "

A cheap chorus pedal from 1999. It is the whole sound.

", "link": "/blog/the-chorus-pedal", "linkLabel": "Read on" } } + ] + }, + { + "slug": "/blog/tracking-to-tape", + "title": "Why we still track to tape", + "template_key": "article", + "parent_slug": "/blog", + "blocks": [ + { "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "Why we still track to tape", "level": 1 } }, + { "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "

Tape does not let you undo. You play it, you keep it, or you play it again. That pressure shows up in the take.

It is slower and it costs more. We think the trade is worth it. The record sounds like a room, not a grid.

" } } + ] + }, + { + "slug": "/blog/september-run", + "title": "Notes from the September run", + "template_key": "article", + "parent_slug": "/blog", + "blocks": [ + { "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "Notes from the September run", "level": 1 } }, + { "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "

Three cities, one van, four amps. The van held. One amp did not.

Sydney sold out and Brisbane nearly did. We ate too much servo food and slept in the van outside a Bunnings. We would do it again next week.

" } } + ] + }, + { + "slug": "/blog/the-chorus-pedal", + "title": "The one pedal we cannot replace", + "template_key": "article", + "parent_slug": "/blog", + "blocks": [ + { "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "The one pedal we cannot replace", "level": 1 } }, + { "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "

It is a plastic chorus pedal from 1999. It hisses and the knob is loose. It is also the whole guitar sound.

We have tried to replace it twice. Both times the record sounded wrong, so the old one went back on the board.

" } } + ] + }, + { + "slug": "/contact", + "title": "Contact", + "template_key": "contact", + "blocks": [ + { + "block_key": "contact-form", + "title": "Contact form", + "slot": "main", + "sort_order": 1, + "content": { + "heading": "Get in touch", + "description": "Booking, press, or just to say hello. We read every message.", + "submitLabel": "Send", + "successMessage": "Thanks. We will get back to you soon.", + "fields": [ + { "name": "name", "label": "Your name", "type": "text", "required": true, "width": "half" }, + { "name": "email", "label": "Email", "type": "email", "required": true, "width": "half" }, + { "name": "subject", "label": "Subject", "type": "text", "required": false, "width": "full" }, + { "name": "message", "label": "Message", "type": "textarea", "required": true, "width": "full" } + ], + "formConfig": { "enabled": true, "captchaEnabled": false, "targetTable": "contact_submissions" } + } + }, + { + "block_key": "contact-card", + "title": "Other ways to reach us", + "slot": "main", + "sort_order": 2, + "content": { + "heading": "Other ways in", + "columns": 3, + "channels": [ + { "icon": "lucide:mail", "label": "Email", "value": "hello@chromestatic.band", "link": "mailto:hello@chromestatic.band" }, + { "icon": "simple-icons:bandcamp", "label": "Bandcamp", "value": "chromestatic.bandcamp.com", "link": "#" }, + { "icon": "simple-icons:instagram", "label": "Instagram", "value": "@chromestatic", "link": "#" } + ] + } + } + ] + }, + { + "slug": "/login", + "title": "Sign in", + "template_key": "auth", + "blocks": [ + { "block_key": "auth-form", "title": "Sign in", "slot": "main", "sort_order": 1, "content": { "default_tab": "login", "show_social": false } } + ] + } + ], + "menu_items": [ + { "menu": "main", "label": "Home", "page_slug": "/", "sort_order": 1 }, + { "menu": "main", "label": "The band", "page_slug": "/about", "sort_order": 2 }, + { "menu": "main", "label": "Journal", "page_slug": "/blog", "sort_order": 3 }, + { "menu": "main", "label": "Contact", "page_slug": "/contact", "sort_order": 4 } + ] +} diff --git a/seed/workflows.json b/seed/workflows.json new file mode 100644 index 0000000..c277456 --- /dev/null +++ b/seed/workflows.json @@ -0,0 +1,19 @@ +{ + "workflows": [ + { + "key": "contact_notify_admins", + "name": "Contact enquiry to admins", + "description": "On a contact-form submission, email the site admins.", + "enabled": true, + "trigger": { "type": "row_inserted", "table": "contact_submissions" }, + "steps": [ + { + "type": "email", + "name": "Email admins", + "template": "Form Submission Notification", + "recipients": { "mode": "admins", "scope": "all" } + } + ] + } + ] +} diff --git a/templates/email/y2k.ninjatpl b/templates/email/y2k.ninjatpl index 8f139ca..8a9fb2a 100644 --- a/templates/email/y2k.ninjatpl +++ b/templates/email/y2k.ninjatpl @@ -18,7 +18,7 @@ } - + {% if preview_text %}
{{ preview_text }}
{% endif %} diff --git a/templates/overrides/y2k/announcement-bar.ninjatpl b/templates/overrides/y2k/announcement-bar.ninjatpl new file mode 100644 index 0000000..516142a --- /dev/null +++ b/templates/overrides/y2k/announcement-bar.ninjatpl @@ -0,0 +1,21 @@ +{# announcement_bar (WO-TF-006): dismissable banner; dismissal persists in localStorage keyed by a content hash (variant + slugified message) so it reappears when the message changes; no-JS fallback is a static non-dismissable strip. Y2K override: mono broadcast readout — data-* + script preserved. #} +{% set anncls = "bg-primary/10 text-foreground" %} +{% if variant == "promo" %}{% set anncls = "bg-primary text-primary-foreground" %}{% elif variant == "alert" %}{% set anncls = "bg-destructive text-destructive-foreground" %}{% endif %} +
+
+

{{ message }}{% if linkText and linkUrl %} {{ linkText }}{% endif %}

+{% if dismissable %}{% endif %} +
+
+{% if dismissable %}{% endif %} diff --git a/templates/overrides/y2k/audio-embed.ninjatpl b/templates/overrides/y2k/audio-embed.ninjatpl new file mode 100644 index 0000000..f9bd0aa --- /dev/null +++ b/templates/overrides/y2k/audio-embed.ninjatpl @@ -0,0 +1,16 @@ +{# audio-embed y2k override: neon panel, mono meta. Facade script + data-bn-* attributes + {% img %} preserved byte-for-byte. #} +
{% if artwork %}
{% img artwork alt=title|default:"" class="h-full w-full object-cover" %}
{% endif %}
{% if title %}

{{ title }}

{% endif %}{% if artist %}

{{ artist }}

{% endif %}
{% if download %}::lucide:download:sm:: Download{% endif %}
diff --git a/templates/overrides/y2k/auth-form.ninjatpl b/templates/overrides/y2k/auth-form.ninjatpl new file mode 100644 index 0000000..42e177f --- /dev/null +++ b/templates/overrides/y2k/auth-form.ninjatpl @@ -0,0 +1,77 @@ +{# auth-form y2k override — reproduces the builtin's security-critical wiring: hardcoded HTMX endpoints /api/auth/login + /api/auth/register (target #auth-message), /forgot-password link, OAuth start URLs gated on context.site.*, and the exact Cap widget markup when captchaEnabled. Only the chrome is themed. #} +{% if context.isPublicLoggedIn %} +
+

Session active as {{ context.publicUsername }}

+ My Account +
+{% else %} +{% set loginActive = "border-primary text-primary" %} +{% set registerActive = "border-transparent text-muted-foreground" %} +{% set loginHidden = "" %} +{% set registerHidden = "hidden" %} +{% if default_tab == "register" %}{% set loginActive = "border-transparent text-muted-foreground" %}{% set registerActive = "border-primary text-primary" %}{% set loginHidden = "hidden" %}{% set registerHidden = "" %}{% endif %} +{% set tabbase = "flex-1 py-2 text-center y2k-mono text-sm font-medium uppercase tracking-wider border-b-2 transition-colors" %} +{% macro captcha() %} + + +{% endmacro %} +{% macro social() %}{% if show_social != false and (context.site.google_oauth_client_id or context.site.facebook_app_id) %}
+

or
+ {% if context.site.google_oauth_client_id %}Continue with Google{% endif %} + {% if context.site.facebook_app_id %}Continue with Facebook{% endif %} +
{% endif %}{% endmacro %} +
+
+
+ + +
+
+
+
+
+ {% if captchaEnabled %}{{ captcha() }}{% endif %} + +

Forgot password?

+ + {{ social() }} +
+
+
+
+
+
+ {% if captchaEnabled %}{{ captcha() }}{% endif %} + + + {{ social() }} +
+
+ +{% endif %} diff --git a/templates/overrides/y2k/auth-status.ninjatpl b/templates/overrides/y2k/auth-status.ninjatpl new file mode 100644 index 0000000..e1f2258 --- /dev/null +++ b/templates/overrides/y2k/auth-status.ninjatpl @@ -0,0 +1,36 @@ +{# auth-status y2k override — reproduces the builtin's hardcoded wiring: /login + /login?tab=register when logged out; /account, /account/reviews, and hx-post /api/auth/logout dropdown when logged in; hx-post /api/auth/resend-verification banner when unverified. State from context.*. #} +{% if not context.isPublicLoggedIn %} +
+ Login + Sign Up +
+{% else %} +{% if not context.publicEmailVerified %}
+ Verify your email address. +
{% endif %} +
+ + + +
+{% endif %} diff --git a/templates/overrides/y2k/author-bio-hero.ninjatpl b/templates/overrides/y2k/author-bio-hero.ninjatpl new file mode 100644 index 0000000..7ec8b04 --- /dev/null +++ b/templates/overrides/y2k/author-bio-hero.ninjatpl @@ -0,0 +1,22 @@ +{# author-bio-hero (y2k override) — author-page header from the `authors` provider. #} +{% if author %} +
+
+
+{% if showAvatar and author.avatar_url %}
{% img author.avatar_url alt=author.display_name class="h-full w-full object-cover" %}
{% endif %} +

{{ author.display_name }}

+{% if author.job_title or author.company %}

{{ author.job_title }}{% if author.job_title and author.company %} · {% endif %}{{ author.company }}

{% endif %} +{% if author.bio %}

{{ author.bio }}

{% endif %} +
+{% if showPostCount %}{{ author.post_count|default:0 }} post{% if author.post_count != 1 %}s{% endif %}{% endif %} +{% if author.location %}{{ author.location }}{% endif %} +
+{% if showSocial %}
+{% if author.website_url %}{% endif %} +{% if author.twitter_handle %}{% endif %} +{% if author.linkedin_url %}{% endif %} +{% if author.github_url %}{% endif %} +
{% endif %} +
+
+{% endif %} diff --git a/templates/overrides/y2k/author-bio.ninjatpl b/templates/overrides/y2k/author-bio.ninjatpl new file mode 100644 index 0000000..8a369ab --- /dev/null +++ b/templates/overrides/y2k/author-bio.ninjatpl @@ -0,0 +1,13 @@ +{# author-bio (y2k override) — byline card sourced from the `post` provider's author_* fields. #} +{% if post.author_name %} + +{% endif %} diff --git a/templates/overrides/y2k/blog-hero.ninjatpl b/templates/overrides/y2k/blog-hero.ninjatpl new file mode 100644 index 0000000..dc9bdbd --- /dev/null +++ b/templates/overrides/y2k/blog-hero.ninjatpl @@ -0,0 +1,24 @@ +{# blog-hero (y2k override) — blog-index header + featured posts from the `posts` provider. #} +
+
+
+
+{% if title %}

{{ title }}

{% endif %} +{% if subtitle %}

{{ subtitle }}

{% endif %} +
+{% if posts %} +
+{% for post in posts %} + +{% endfor %} +
+{% endif %} +
+
diff --git a/templates/overrides/y2k/breadcrumbs.ninjatpl b/templates/overrides/y2k/breadcrumbs.ninjatpl new file mode 100644 index 0000000..d054c37 --- /dev/null +++ b/templates/overrides/y2k/breadcrumbs.ninjatpl @@ -0,0 +1,15 @@ +{# breadcrumbs (y2k override) — trail from the `breadcrumbs` provider (page ancestry). #} +{# showHome prepends a Home link; separator is text ("/") for default or a chevron #} +{# for the arrows template. The trailing is_current item renders as plain text. #} +{% set arrows = template == "arrows" %} + diff --git a/templates/overrides/y2k/button.ninjatpl b/templates/overrides/y2k/button.ninjatpl index 61b7004..8c57c2c 100644 --- a/templates/overrides/y2k/button.ninjatpl +++ b/templates/overrides/y2k/button.ninjatpl @@ -1 +1,2 @@ -{{ text|default:"button" }} +{# y2k button — keep {% button %} tag intact; y2k chrome on hugging wrapper #} +{% with align=align|default:"left" %}
{% if newTab %}{% button style|default:"primary" text=label|default:"Learn more" href=link|default:"#" size=size|default:"md" icon=icon|default:"" icon_position=iconPosition|default:"left" target="_blank" %}{% else %}{% button style|default:"primary" text=label|default:"Learn more" href=link|default:"#" size=size|default:"md" icon=icon|default:"" icon_position=iconPosition|default:"left" %}{% endif %}
{% endwith %} diff --git a/templates/overrides/y2k/card.ninjatpl b/templates/overrides/y2k/card.ninjatpl new file mode 100644 index 0000000..028a246 --- /dev/null +++ b/templates/overrides/y2k/card.ninjatpl @@ -0,0 +1,2 @@ +{# y2k card — dashed neon panel; fields layout/link/media/title/text/linkLabel #} +{% with layout=layout|default:"vertical" %}{% endwith %} diff --git a/templates/overrides/y2k/category-list.ninjatpl b/templates/overrides/y2k/category-list.ninjatpl new file mode 100644 index 0000000..521367e --- /dev/null +++ b/templates/overrides/y2k/category-list.ninjatpl @@ -0,0 +1,18 @@ +{# category-list (y2k override) — from the `categories` provider. list or pills. #} +{% set pills = style == "pills" %} +
+{% if heading %}

{{ heading }}

{% endif %} +{% if pills %} +
+{% for cat in categories %} +{{ cat.name }}{% if showCount %}{{ cat.post_count }}{% endif %} +{% endfor %} +
+{% else %} + +{% endif %} +
diff --git a/templates/overrides/y2k/contact-card.ninjatpl b/templates/overrides/y2k/contact-card.ninjatpl new file mode 100644 index 0000000..2e9d36a --- /dev/null +++ b/templates/overrides/y2k/contact-card.ninjatpl @@ -0,0 +1,18 @@ +{# contact-card y2k override: neon channel panels. All field reads + control flow preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if description %}

{{ description }}

{% endif %} +
+ {% for channel in channels %} +
+ {% if channel.icon %}{% endif %} +
+ {% if channel.label %}
{{ channel.label }}
{% endif %} + {% if channel.value %}
{% if channel.link %}{{ channel.value }}{% else %}{{ channel.value }}{% endif %}
{% endif %} +
+
+ {% endfor %} +
+
+
diff --git a/templates/overrides/y2k/contact-form.ninjatpl b/templates/overrides/y2k/contact-form.ninjatpl new file mode 100644 index 0000000..821fb77 --- /dev/null +++ b/templates/overrides/y2k/contact-form.ninjatpl @@ -0,0 +1,40 @@ +{# contact-form y2k override: terminal console chrome. The {% form %} block, honeypot, field loop, form.submitted/errors reads and _block_id/bid submit wiring are preserved byte-for-byte — only presentational classes change. #} +{% with bid=_block_id|default:context.blockId %} +
+
+ {% if form.submitted %} +
+
+

{{ form.message|default:successMessage|default:"Thanks — your message has been sent." }}

+
+ {% else %} + {% if heading %}

{{ heading }}

{% endif %} + {% if description %}

{{ description }}

{% endif %} + {% if form.errors._form %}
{{ form.errors._form }}
{% endif %} + {% with formAction="/api/blocks/"|add:bid|add:"/submit" formTarget="#contact-form-"|add:bid %} + {% form action=formAction hx_target=formTarget hx_swap="outerHTML" class="mt-8 grid grid-cols-1 gap-5 sm:grid-cols-2" %} + {% for field in fields %} +
+ {% if field.label %}{% endif %} + {% if field.type == "textarea" %} + + {% elif field.type == "select" %} + + {% else %} + + {% endif %} +
+ {% endfor %} + +
+ +
+ {% endform %} + {% endwith %} + {% endif %} +
+
+{% endwith %} diff --git a/templates/overrides/y2k/countdown.ninjatpl b/templates/overrides/y2k/countdown.ninjatpl new file mode 100644 index 0000000..bcc4711 --- /dev/null +++ b/templates/overrides/y2k/countdown.ninjatpl @@ -0,0 +1,50 @@ +{# countdown y2k override: neon HUD digit boxes, glitch heading. Timer script + data attributes preserved byte-for-byte. #} +
+ +
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} +
+
+
--{{ labelDays|default:"Days" }}
+
--{{ labelHours|default:"Hours" }}
+
--{{ labelMinutes|default:"Minutes" }}
+
--{{ labelSeconds|default:"Seconds" }}
+
+ +
+ +
+
+ diff --git a/templates/overrides/y2k/cta.ninjatpl b/templates/overrides/y2k/cta.ninjatpl new file mode 100644 index 0000000..325705f --- /dev/null +++ b/templates/overrides/y2k/cta.ninjatpl @@ -0,0 +1,16 @@ +{# cta y2k override: scanline overlay, glitch heading, clip-cut rgb-split CTAs. All field reads preserved. #} +
+ +
+
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if text %}

{{ text }}

{% endif %} +
+
+ {% if primaryUrl and primaryLabel %}{{ primaryLabel }}{% endif %} + {% if secondaryUrl and secondaryLabel %}{{ secondaryLabel }}{% endif %} +
+
+
+
diff --git a/templates/overrides/y2k/divider.ninjatpl b/templates/overrides/y2k/divider.ninjatpl new file mode 100644 index 0000000..23dc2f1 --- /dev/null +++ b/templates/overrides/y2k/divider.ninjatpl @@ -0,0 +1,2 @@ +{# y2k divider — neon gradient rule / primary-tinted variants #} +{% with variant=variant|default:"line" weight=weight|default:"thin" width=width|default:"full" %}{% if variant == "ornament" %}{% elif variant == "spacer-rule" %}{% else %}{% endif %}{% endwith %} diff --git a/templates/overrides/y2k/event-list.ninjatpl b/templates/overrides/y2k/event-list.ninjatpl new file mode 100644 index 0000000..acc0a1a --- /dev/null +++ b/templates/overrides/y2k/event-list.ninjatpl @@ -0,0 +1,41 @@ +{# event-list y2k override: neon cards, mono datetime. All field reads + control flow preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} + {% if layout == "cards" %} +
+ {% for event in events %}{% if not (hidePast and event.past) %} +
+ {% if event.media %}
{% img event.media alt=event.title class="h-full w-full object-cover" %}
{% endif %} +
+
{{ event.start }}{% if event.end %} – {{ event.end }}{% endif %}{% if event.timezone %} {{ event.timezone }}{% endif %}
+

{{ event.title }}

+ {% if event.venue %}

{{ event.venue }}

{% endif %} + {% if event.description %}

{{ event.description }}

{% endif %} + {% if event.link %}{{ event.linkLabel|default:"Details" }}{% endif %} +
+
+ {% endif %}{% endfor %} +
+ {% else %} +
    + {% for event in events %}{% if not (hidePast and event.past) %} +
  • +
    +
    {{ event.start }}
    + {% if event.end %}
    to {{ event.end }}
    {% endif %} + {% if event.timezone %}
    {{ event.timezone }}
    {% endif %} +
    +
    +

    {{ event.title }}

    + {% if event.venue %}

    {{ event.venue }}

    {% endif %} + {% if event.description %}

    {{ event.description }}

    {% endif %} +
    + {% if event.link %}{{ event.linkLabel|default:"Details" }}{% endif %} +
  • + {% endif %}{% endfor %} +
+ {% endif %} +
+
diff --git a/templates/overrides/y2k/faq.ninjatpl b/templates/overrides/y2k/faq.ninjatpl new file mode 100644 index 0000000..f37973b --- /dev/null +++ b/templates/overrides/y2k/faq.ninjatpl @@ -0,0 +1,18 @@ +{# faq y2k override: glitch heading, neon-tinted dividers, display questions. Chevron svg, {% for %}, and answer prose preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} +
+ {% for item in items %} +
+ + {{ item.question }} + + +
{{ item.answer|safe }}
+
+ {% endfor %} +
+
+
diff --git a/templates/overrides/y2k/feature-grid.ninjatpl b/templates/overrides/y2k/feature-grid.ninjatpl new file mode 100644 index 0000000..e54d3e6 --- /dev/null +++ b/templates/overrides/y2k/feature-grid.ninjatpl @@ -0,0 +1,19 @@ +{# feature_grid y2k override: scanline hero overlay, mono eyebrow, glitch heading, neon HUD cards. All field reads + control flow preserved. #} +
+ +
+ {% if eyebrow %}

{{ eyebrow }}

{% endif %} + {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} +
+ {% for item in items %} +
+ {% if item.icon %}
{% endif %} +

{% if item.linkUrl %}{{ item.title }}{% else %}{{ item.title }}{% endif %}

+ {% if item.text %}

{{ item.text }}

{% endif %} + {% if item.linkUrl and item.linkLabel %}{{ item.linkLabel }}{% endif %} +
+ {% endfor %} +
+
+
diff --git a/templates/overrides/y2k/featured-posts.ninjatpl b/templates/overrides/y2k/featured-posts.ninjatpl new file mode 100644 index 0000000..b0b09a7 --- /dev/null +++ b/templates/overrides/y2k/featured-posts.ninjatpl @@ -0,0 +1,23 @@ +{# featured-posts (y2k override) — grid of cards from the `posts` provider #} +{# (params: limit, featured=true). Neon HUD card: image, title, excerpt, meta. #} +
+
+{% if heading %}

{{ heading }}

{% endif %} +
+{% for post in posts %} +
+{% if showFeaturedImage and post.featured_image_url %}{% img post.featured_image_url alt=post.title class="h-full w-full object-cover transition-transform duration-300 group-hover:scale-105" %}{% endif %} +
+

{{ post.title }}

+{% if showExcerpt and post.excerpt %}

{{ post.excerpt }}

{% endif %} +
+{% if showAuthor and post.author_name %}{{ post.author_name }}{% endif %} +{% if showDate and post.published_at %}{{ post.published_at|date:"M j, Y" }}{% endif %} +{% if post.reading_time %}{{ post.reading_time }} min read{% endif %} +
+
+
+{% endfor %} +
+
+
diff --git a/templates/overrides/y2k/footer.ninjatpl b/templates/overrides/y2k/footer.ninjatpl new file mode 100644 index 0000000..ccf1f15 --- /dev/null +++ b/templates/overrides/y2k/footer.ninjatpl @@ -0,0 +1,39 @@ +{# footer (WO-TF-006): layouts columns|minimal|centered; menu columns, legal bar, and social row from the `footer_menus` provider; newsletter section reuses the {% signup_form %} core (no duplicated form). Y2K override: mono column headers, neon top rule, display wordmark. #} +{% set fl = layout %} +{% if not fl %}{% if template == "centered" %}{% set fl = "centered" %}{% else %}{% set fl = "columns" %}{% endif %}{% endif %} +{% set fbg = backgroundColor|default:"bg-muted/50" %} +{% macro sociallinks(cls) %}{% if social_items %}{% endif %}{% endmacro %} +{% macro newsletter() %}{% if showNewsletter %}

{{ newsletterTitle|default:"Stay in the loop" }}

{% if newsletterDescription %}

{{ newsletterDescription }}

{% endif %}
{% signup_form list=newsletterListSlug|default:"main" source="footer" submit=newsletterButtonText|default:"Subscribe" %}
{% endif %}{% endmacro %} +{% macro legalbar() %}

{% if copyright %}© {{ copyright }}{% else %}© {{ companyName }}{% endif %}

{% if legal_items %}{% endif %}
{% endmacro %} +
+
+{% if fl == "minimal" %} +
+
{% if logo %}{{ companyName }}{% elif companyName %}{{ companyName }}{% endif %}{% if tagline %}{% endif %}
+{{ sociallinks("") }} +
+{{ legalbar() }} +{% elif fl == "centered" %} +
+{% if logo %}{{ companyName }}{% elif companyName %}{{ companyName }}{% endif %} +{% if tagline %}

{{ tagline }}

{% endif %} +{% if columns %}{% endif %} +{{ newsletter() }} +{{ sociallinks("justify-center") }} +
+{{ legalbar() }} +{% else %} +
+
+{% if logo %}{{ companyName }}{% elif companyName %}{{ companyName }}{% endif %} +{% if tagline %}

{{ tagline }}

{% endif %} +{% if address or email or phone %}
{% if address %}
{{ address }}
{% endif %}{% if email %}{% endif %}{% if phone %}{% endif %}
{% endif %} +{{ sociallinks("mt-5") }} +
+{% for col in columns %}

{{ col.header }}

{% endfor %} +{% if showNewsletter %}
{{ newsletter() }}
{% endif %} +
+{{ legalbar() }} +{% endif %} +
+
diff --git a/templates/overrides/y2k/gallery.ninjatpl b/templates/overrides/y2k/gallery.ninjatpl new file mode 100644 index 0000000..058cf2a --- /dev/null +++ b/templates/overrides/y2k/gallery.ninjatpl @@ -0,0 +1,52 @@ +{# gallery y2k override: neon-edged tiles. Lightbox script + data-bn-* attributes + {% img %} preserved byte-for-byte. #} +{% with layout=layout|default:"grid" gap=gap|default:"md" cols=columns|default:3 %}{% if lightbox %}{% endif %}{% endwith %} diff --git a/templates/overrides/y2k/heading.ninjatpl b/templates/overrides/y2k/heading.ninjatpl index 06b1470..27ebb29 100644 --- a/templates/overrides/y2k/heading.ninjatpl +++ b/templates/overrides/y2k/heading.ninjatpl @@ -1 +1,7 @@ -{% if level|integer == 1 %}

{{ text }}

{% elif level|integer == 3 %}

{{ text }}

{% elif level|integer == 4 %}

{{ text }}

{% elif level|integer == 5 %}
{{ text }}
{% elif level|integer == 6 %}
{{ text }}
{% else %}

{{ text }}

{% endif %} +{# y2k heading — y2k-display everywhere; glitch on h1/h2 only #} +{% if level == 1 %}

{{ text }}

+{% elif level == 3 %}

{{ text }}

+{% elif level == 4 %}

{{ text }}

+{% elif level == 5 %}
{{ text }}
+{% elif level == 6 %}
{{ text }}
+{% else %}

{{ text }}

{% endif %} diff --git a/templates/overrides/y2k/hero.ninjatpl b/templates/overrides/y2k/hero.ninjatpl new file mode 100644 index 0000000..4ce0a75 --- /dev/null +++ b/templates/overrides/y2k/hero.ninjatpl @@ -0,0 +1,107 @@ +{# hero y2k override (GO-BIG showpiece): canvas liquid-chrome metaballs behind a beveled chrome headline over a sparkle glitter field. All builtin field reads + macro + branch structure preserved from the frozen hero default. The metaball canvas shows when there is no backgroundImage; it lazy-inits on view, honors prefers-reduced-motion, degrades to a static chrome-mesh gradient with JS off, and is dependency-free (hero/landing only per motion tier). #} +{% set eyebrowText = eyebrow.text|default:eyebrow %} +{% set headlineText = headline.text|default:headline %} +{% set subText = subheadline.text|default:subheadline %} +{% set descText = description.text|default:description %} +{% set pText = ctaPrimary.text %} +{% set pUrl = ctaPrimary.url %} +{% set sText = ctaSecondary.text %} +{% set sUrl = ctaSecondary.url %} +{% set variant = template|default:"centered" %} +{% set hasImage = backgroundImage %} +{% set showOverlay = hasImage and overlayColor != "none" and overlayColor != "" %} +{% macro herocontent(align) %} +{% if eyebrowText %}{{ eyebrowText }} {% endif %} +{% if headlineText %}

{{ headlineText }}

{% endif %} +{% if subText %}

{{ subText }}

{% endif %} +{% if descText %}

{{ descText }}

{% endif %} +{% if pText or sText %}
+{% if pText and pUrl %}{{ pText }}{% endif %} +{% if sText and sUrl %}{{ sText }}{% endif %} +
{% endif %} +{% if trustLogos %}
+

Heard on

+
{% for logo in trustLogos %}{% if logo.url %}{% img logo.image alt=logo.alt class="h-8 w-auto" %}{% else %}{% img logo.image alt=logo.alt class="h-8 w-auto opacity-60" %}{% endif %}{% endfor %}
+
{% endif %} +{% endmacro %} +{% if variant == "split" %} +
+
{% if hasImage %}{% img backgroundImage alt=headlineText class="absolute inset-0 h-full w-full object-cover" layout="hero" %}{% else %}
{% endif %}
+
{{ herocontent("left") }}
+
+{% else %} +{% set align = "center" %} +{% if variant == "left-aligned" %}{% set align = "left" %}{% endif %} +
+{% if hasImage %}
{% img backgroundImage alt=headlineText class="h-full w-full object-cover" layout="hero" %}{% if showOverlay %}
{% endif %}
{% else %}
{% endif %} + +
{{ herocontent(align) }}
+
+{% endif %} + diff --git a/templates/overrides/y2k/hours-location.ninjatpl b/templates/overrides/y2k/hours-location.ninjatpl new file mode 100644 index 0000000..d7da77b --- /dev/null +++ b/templates/overrides/y2k/hours-location.ninjatpl @@ -0,0 +1,31 @@ +{# hours-location y2k override: mono readout table, neon map panel. Map lazy-load button + inline handler preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} +
+
+ {% if hours %} +
+ + {% for row in hours %} + + + + + {% endfor %} + +
{{ row.day }}{% if row.closed %}Closed{% else %}{{ row.hours }}{% endif %}
+ {% endif %} + {% if specialHoursNote %}

{{ specialHoursNote }}

{% endif %} + {% if address %}
Address
{{ address }}
{% endif %} + {% if phone %}{% endif %} + {% if directionsUrl %}Get directions{% endif %} + + {% if mapEmbedUrl %} +
+ +
+ {% endif %} + + + diff --git a/templates/overrides/y2k/image.ninjatpl b/templates/overrides/y2k/image.ninjatpl index eef295d..28a1e5a 100644 --- a/templates/overrides/y2k/image.ninjatpl +++ b/templates/overrides/y2k/image.ninjatpl @@ -1 +1,2 @@ -{% if url %}
{{ alt }}{% if caption %}
{{ caption }}
{% endif %}
{% endif %} +{# y2k image — cyan neon-edge frame, mono caption; all size/fit branches preserved #} +{% with size=size|default:"full" align=align|default:"center" aspectRatio=aspectRatio|default:"auto" objectFit=objectFit|default:"cover" rounded=rounded|default:"lg" shadow=shadow|default:"none" %}
{% if objectFit == "contain" %}{% img src alt=alt class="w-full h-full object-contain" layout=size %}{% elif objectFit == "fill" %}{% img src alt=alt class="w-full h-full object-fill" layout=size %}{% elif objectFit == "none" %}{% img src alt=alt class="w-full h-full object-none" layout=size %}{% else %}{% img src alt=alt class="w-full h-full object-cover" layout=size %}{% endif %}
{% if caption %}
{{ caption }}
{% endif %}
{% endwith %} diff --git a/templates/overrides/y2k/logo-strip.ninjatpl b/templates/overrides/y2k/logo-strip.ninjatpl new file mode 100644 index 0000000..4756bb8 --- /dev/null +++ b/templates/overrides/y2k/logo-strip.ninjatpl @@ -0,0 +1,16 @@ +{# logo_strip y2k override: mono kicker. Marquee classes, {% img %}, and keyframes {% endif %} diff --git a/templates/overrides/y2k/menu-list.ninjatpl b/templates/overrides/y2k/menu-list.ninjatpl new file mode 100644 index 0000000..3758fa8 --- /dev/null +++ b/templates/overrides/y2k/menu-list.ninjatpl @@ -0,0 +1,30 @@ +{# menu-list y2k override: mono price readouts, neon section rules. All field reads + control flow preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} +
+ {% for section in sections %} + + {% endfor %} +
+ {% if footnote %}

{{ footnote }}

{% endif %} +
+
diff --git a/templates/overrides/y2k/navbar.ninjatpl b/templates/overrides/y2k/navbar.ninjatpl new file mode 100644 index 0000000..cc8acf0 --- /dev/null +++ b/templates/overrides/y2k/navbar.ninjatpl @@ -0,0 +1,104 @@ +{# navbar (WO-TF-005): variants standard|centered-logo|split|transparent-over-hero; sticky none|sticky|shrink-on-scroll; dependency-free off-canvas drawer (pure-CSS baseline + progressive-JS focus-trap/Escape/scroll-lock); menu data from the `menus` provider. Y2K override: mono nav links, neon dropdown panels, clip-cut CTA — drawer ids/style/script/data-bn-* preserved byte-for-byte. #} +{% set nv = variant %} +{% if not nv %}{% if template == "centered" %}{% set nv = "centered-logo" %}{% elif template == "transparent" %}{% set nv = "transparent-over-hero" %}{% else %}{% set nv = "standard" %}{% endif %}{% endif %} +{% set sb = stickyBehavior %} +{% if not sb %}{% if sticky %}{% set sb = "sticky" %}{% else %}{% set sb = "none" %}{% endif %}{% endif %} +{% set use_mega = megaMenu or template == "mega" %} +{% set is_transparent = nv == "transparent-over-hero" %} +{% set cta_text = cta.text|default:ctaLabel %} +{% set cta_url = cta.url|default:ctaUrl %} +{% set bg = backgroundColor|default:"bg-background" %} +{% macro navlink(item) %}{{ item.label }}{% endmacro %} +{% macro dropdown(item) %}
+ +{% if use_mega %} +{% else %}{% endif %} +
{% endmacro %} +{% macro brand(size) %}{% if logoType == "text" and logoText.text %}{{ logoText.text }}{% elif logo %}{{ alt|default:companyName }}{% elif companyName %}{{ companyName }}{% endif %}{% endmacro %} +{% macro ctabtn(extra) %}{% if cta_text %}{{ cta_text }}{% endif %}{% endmacro %} + + + diff --git a/templates/overrides/y2k/newsletter-signup.ninjatpl b/templates/overrides/y2k/newsletter-signup.ninjatpl new file mode 100644 index 0000000..52e7aea --- /dev/null +++ b/templates/overrides/y2k/newsletter-signup.ninjatpl @@ -0,0 +1,12 @@ +{# newsletter-signup (y2k override) — the {% signup_form %} tag carries the #} +{# untouchable subscribe wiring (/api/lists/subscribe HTMX post + honeypot); #} +{# this template supplies only the y2k chrome around it. #} +
+
+
+{% if title %}

{{ title }}

{% endif %} +{% if description %}

{{ description }}

{% endif %} +
{% signup_form list=listSlug source=source submit=submitText success_message=successMessage placeholder=placeholder %}
+
+
+
diff --git a/templates/overrides/y2k/page-suggestions.ninjatpl b/templates/overrides/y2k/page-suggestions.ninjatpl new file mode 100644 index 0000000..7e11df3 --- /dev/null +++ b/templates/overrides/y2k/page-suggestions.ninjatpl @@ -0,0 +1,13 @@ +{# page-suggestions y2k override — themed 404. NOTE: the builtin builds its "did you mean" list from a Postgres full-text query (SearchSimilarPages), which a codeless template cannot run — so this override renders the on-theme 404 console (title / message / requested path / home CTA) without the dynamic suggestion list. Recorded as a known deviation in WO-TF-015. Content fields honored: title, emptyMessage. #} +
+ +
+

Error 404

+

{{ title|default:"Signal lost" }}

+

{{ emptyMessage|default:"That route returned nothing. The page moved, shipped, or never existed." }}

+ {% if context.path %}

requested: {{ context.path }}

{% endif %} + +
+
diff --git a/templates/overrides/y2k/password-reset-form.ninjatpl b/templates/overrides/y2k/password-reset-form.ninjatpl new file mode 100644 index 0000000..c0bee42 --- /dev/null +++ b/templates/overrides/y2k/password-reset-form.ninjatpl @@ -0,0 +1,13 @@ +{# password-reset-form y2k override — reproduces the builtin's hardcoded wiring: hx-post /api/auth/request-password-reset (target #reset-message), back-to-login link /login. Only the chrome is themed. #} +
+
+
+

Reset Password

+

Enter your email address and we will send you a link to reset your password.

+
+
+ +
+

Back to login

+
+
diff --git a/templates/overrides/y2k/popular-posts.ninjatpl b/templates/overrides/y2k/popular-posts.ninjatpl new file mode 100644 index 0000000..c4ba5c2 --- /dev/null +++ b/templates/overrides/y2k/popular-posts.ninjatpl @@ -0,0 +1,16 @@ +{# popular-posts (y2k override) — compact ranked list from the `posts` provider. #} +
+{% if heading %}

{{ heading }}

{% endif %} +
    +{% for post in posts %} +
  1. +{% if showRank %}{{ forloop.Counter }}{% endif %} +{% if showThumbnail and post.featured_image_url %}{% img post.featured_image_url alt=post.title class="h-full w-full object-cover" %}{% endif %} +
    +

    {{ post.title }}

    +{% if post.published_at %}

    {{ post.published_at|date:"M j, Y" }}

    {% endif %} +
    +
  2. +{% endfor %} +
+
diff --git a/templates/overrides/y2k/post-hero.ninjatpl b/templates/overrides/y2k/post-hero.ninjatpl new file mode 100644 index 0000000..9ca5f16 --- /dev/null +++ b/templates/overrides/y2k/post-hero.ninjatpl @@ -0,0 +1,23 @@ +{# post-hero (y2k override) — current post from the `post` provider. When the post #} +{# has a featured image it becomes an overlaid banner; otherwise a clean text hero. #} +{% set center = align != "left" %} +{% if post.featured_image_url %} +
+
{% img post.featured_image_url alt=post.featured_image_alt class="h-full w-full object-cover" layout="hero" %}
+
+
+

{{ post.title }}

+{% if showExcerpt and post.excerpt %}

{{ post.excerpt }}

{% endif %} +{% if showMeta %}
{% if post.author_name %}{{ post.author_name }}{% endif %}{% if post.published_at %}{{ post.published_at|date:"F j, Y" }}{% endif %}{% if post.reading_time %}{{ post.reading_time }} min read{% endif %}
{% endif %} +
+
+{% else %} +
+
+
+

{{ post.title }}

+{% if showExcerpt and post.excerpt %}

{{ post.excerpt }}

{% endif %} +{% if showMeta %}
{% if post.author_name %}{{ post.author_name }}{% endif %}{% if post.published_at %}{{ post.published_at|date:"F j, Y" }}{% endif %}{% if post.reading_time %}{{ post.reading_time }} min read{% endif %}
{% endif %} +
+
+{% endif %} diff --git a/templates/overrides/y2k/post-metadata.ninjatpl b/templates/overrides/y2k/post-metadata.ninjatpl new file mode 100644 index 0000000..f3dc5aa --- /dev/null +++ b/templates/overrides/y2k/post-metadata.ninjatpl @@ -0,0 +1,9 @@ +{# post-metadata (y2k override) — author/date/reading-time row from the `post` provider. #} +{% if post %} +
+{% if showAuthor and post.author_name %}{% if showAvatar and post.author_avatar %}{% img post.author_avatar alt=post.author_name class="h-7 w-7 rounded-full border border-primary/40 object-cover" %}{% endif %}{{ post.author_name }}{% endif %} +{% if showDate and post.published_at %}{% endif %} +{% if showReadingTime and post.reading_time %}{{ post.reading_time }} min read{% endif %} +{% if post.categories %}{% for cat in post.categories %}{{ cat.name }}{% endfor %}{% endif %} +
+{% endif %} diff --git a/templates/overrides/y2k/pricing.ninjatpl b/templates/overrides/y2k/pricing.ninjatpl new file mode 100644 index 0000000..bb9af2f --- /dev/null +++ b/templates/overrides/y2k/pricing.ninjatpl @@ -0,0 +1,26 @@ +{# pricing y2k override: scanline overlay, glitch heading, HUD neon tier panels (magenta highlight / cyan standard), clip-cut CTAs. Field reads + control flow preserved. #} +
+ +
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} +
+ {% for tier in tiers %} +
+ {% if tier.badge %}{{ tier.badge }}{% endif %} +

{{ tier.name }}

+

+ {% if currency %}{{ currency }}{% else %}${% endif %}{{ tier.price }} + {% if tier.period %}{{ tier.period }}{% endif %} +

+ {% if tier.description %}

{{ tier.description }}

{% endif %} + {% if tier.features %}
    + {% for feature in tier.features %}
  • {{ feature }}
  • {% endfor %} +
{% endif %} + {% if tier.ctaUrl and tier.ctaLabel %}{{ tier.ctaLabel }}{% endif %} +
+ {% endfor %} +
+ {% if billingNote %}

{{ billingNote }}

{% endif %} +
+
diff --git a/templates/overrides/y2k/quote.ninjatpl b/templates/overrides/y2k/quote.ninjatpl new file mode 100644 index 0000000..6355d8a --- /dev/null +++ b/templates/overrides/y2k/quote.ninjatpl @@ -0,0 +1,2 @@ +{# y2k quote — dashed neon left border; y2k-display for pull; mono figcaption #} +{% with style=style|default:"block" align=align|default:"left" %}
{{ quote|safe }}{% if attribution or role or cite %}
{% if attribution %}{{ attribution }}{% endif %}{% if role %}{{ role }}{% endif %}{% if cite %}Source{% endif %}
{% endif %}
{% endwith %} diff --git a/templates/overrides/y2k/related-posts.ninjatpl b/templates/overrides/y2k/related-posts.ninjatpl new file mode 100644 index 0000000..0892c7c --- /dev/null +++ b/templates/overrides/y2k/related-posts.ninjatpl @@ -0,0 +1,17 @@ +{# related-posts (y2k override) — grid from the `posts` provider. #} +{% if posts %} +
+
+

{{ heading|default:"Related posts" }}

+
+{% for post in posts %} + +{% endfor %} +
+
+
+{% endif %} diff --git a/templates/overrides/y2k/rich-section.ninjatpl b/templates/overrides/y2k/rich-section.ninjatpl new file mode 100644 index 0000000..ccd5687 --- /dev/null +++ b/templates/overrides/y2k/rich-section.ninjatpl @@ -0,0 +1,14 @@ +{# rich_section y2k override: mono eyebrow, glitch heading, HUD-framed media, clip-cut CTA. Field reads + {% img %} preserved. #} +
+
+
+ {% if eyebrow %}

{{ eyebrow }}

{% endif %} + {% if heading %}

{{ heading }}

{% endif %} + {% if body %}
{{ body|safe }}
{% endif %} + {% if ctaUrl and ctaLabel %}{{ ctaLabel }}{% endif %} +
+
+ {% if media %}
{% img media alt=mediaAlt class="w-full h-auto object-cover" %}
{% endif %} +
+
+
diff --git a/templates/overrides/y2k/rich-text.ninjatpl b/templates/overrides/y2k/rich-text.ninjatpl new file mode 100644 index 0000000..b6dbfb2 --- /dev/null +++ b/templates/overrides/y2k/rich-text.ninjatpl @@ -0,0 +1,2 @@ +{# y2k rich-text — body font via y2k-prose #} +{% if html %}
{{ html|safe }}
{% endif %} diff --git a/templates/overrides/y2k/sidebar-nav.ninjatpl b/templates/overrides/y2k/sidebar-nav.ninjatpl new file mode 100644 index 0000000..72c7cee --- /dev/null +++ b/templates/overrides/y2k/sidebar-nav.ninjatpl @@ -0,0 +1,15 @@ +{# sidebar-nav (y2k override) — vertical menu from the `menus` provider. Variants #} +{# default | compact. highlight-current carried as a progressive-enhancement #} +{# script keyed on data-bn-sidebar-link (marks the anchor matching location.pathname). #} +{% set compact = template == "compact" %} + +{% if highlightCurrent %}{% endif %} diff --git a/templates/overrides/y2k/social-links.ninjatpl b/templates/overrides/y2k/social-links.ninjatpl new file mode 100644 index 0000000..c7ae79b --- /dev/null +++ b/templates/overrides/y2k/social-links.ninjatpl @@ -0,0 +1,14 @@ +{# y2k social-links — mono label, sharp neon-bordered chips; icon sprites preserved #} + diff --git a/templates/overrides/y2k/stats.ninjatpl b/templates/overrides/y2k/stats.ninjatpl new file mode 100644 index 0000000..a1d88bf --- /dev/null +++ b/templates/overrides/y2k/stats.ninjatpl @@ -0,0 +1,50 @@ +{# stats y2k override: scanline overlay, glitch heading, HUD stat tiles, mono labels. data-stats + countUp script/attrs preserved byte-for-byte. #} +
+ +
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} +
+ {% for item in items %} +
+
{% if item.prefix %}{{ item.prefix }}{% endif %}{{ item.value }}{% if item.suffix %}{{ item.suffix }}{% endif %}
+ {% if item.label %}
{{ item.label }}
{% endif %} +
+ {% endfor %} +
+
+
+{% if countUp %}{% endif %} diff --git a/templates/overrides/y2k/team.ninjatpl b/templates/overrides/y2k/team.ninjatpl new file mode 100644 index 0000000..13d883a --- /dev/null +++ b/templates/overrides/y2k/team.ninjatpl @@ -0,0 +1,22 @@ +{# team y2k override: glitch heading, dashed-neon HUD member panels (lime), mono roles. {% img %} + social icon svgs + control flow preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} +
+ {% for member in members %} +
+ {% if member.photo %}{% img member.photo alt=member.name class="h-full w-full object-cover" %}{% endif %} +
+ {% if member.name %}

{{ member.name }}

{% endif %} + {% if member.role %}

{{ member.role }}

{% endif %} + {% if member.bio %}

{{ member.bio }}

{% endif %} + {% if member.socials %}
+ {% for social in member.socials %}{% if social.url %}{% endif %}{% endfor %} +
{% endif %} +
+
+ {% endfor %} +
+
+
diff --git a/templates/overrides/y2k/testimonial.ninjatpl b/templates/overrides/y2k/testimonial.ninjatpl new file mode 100644 index 0000000..cc6635c --- /dev/null +++ b/templates/overrides/y2k/testimonial.ninjatpl @@ -0,0 +1,27 @@ +{# testimonial y2k override: glitch heading, dashed-neon HUD quote cards. Rating svgs, {% img %}, and control flow preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} +
+ {% for q in quotes %} +
+ {% if q.rating %}
+ + + + + +
{% endif %} +
{% if q.quote %}“{{ q.quote }}”{% endif %}
+
+ {% if q.avatar %}{% img q.avatar alt=q.name class="h-full w-full object-cover" %}{% endif %} + + {% if q.name %}{{ q.name }}{% endif %} + {% if q.role %}{{ q.role }}{% endif %} + +
+
+ {% endfor %} +
+
+
diff --git a/templates/overrides/y2k/text.ninjatpl b/templates/overrides/y2k/text.ninjatpl index adf7858..04e7f9b 100644 --- a/templates/overrides/y2k/text.ninjatpl +++ b/templates/overrides/y2k/text.ninjatpl @@ -1 +1,2 @@ -
{{ text|safe }}
+{# y2k text — body font via y2k-prose #} +
{{ text|safe }}
diff --git a/templates/overrides/y2k/timeline.ninjatpl b/templates/overrides/y2k/timeline.ninjatpl new file mode 100644 index 0000000..a78c39d --- /dev/null +++ b/templates/overrides/y2k/timeline.ninjatpl @@ -0,0 +1,22 @@ +{# timeline y2k override: glitch heading, mono step labels, neon markers. All field reads + control flow preserved. #} +
+
+ {% if heading %}

{{ heading }}

{% endif %} + {% if intro %}

{{ intro }}

{% endif %} +
    + {% for step in steps %} +
  1. + + {% if marker == "numbered" %}{{ forloop.counter }}{% elif marker == "dot" %}{% else %}{% endif %} + +
    + {% if step.label %}

    {{ step.label }}

    {% endif %} + {% if step.title %}

    {{ step.title }}

    {% endif %} + {% if step.text %}

    {{ step.text }}

    {% endif %} + {% if step.media %}
    {% img step.media alt=step.title class="w-full h-auto object-cover" %}
    {% endif %} +
    +
  2. + {% endfor %} +
+
+
diff --git a/templates/overrides/y2k/utility-bar.ninjatpl b/templates/overrides/y2k/utility-bar.ninjatpl new file mode 100644 index 0000000..4340e4d --- /dev/null +++ b/templates/overrides/y2k/utility-bar.ninjatpl @@ -0,0 +1,10 @@ +{# utility_bar (WO-TF-006): thin top contact strip; left/right item lists {icon,label,link}; icons render through the sprite pipeline via the ::pack:name:: shorthand. Y2K override: mono terminal readout, neon underline. #} +{% set ubcls = "bg-muted text-muted-foreground border-b border-primary/30" %} +{% if variant == "primary" %}{% set ubcls = "bg-primary text-primary-foreground" %}{% elif variant == "dark" %}{% set ubcls = "bg-foreground text-background" %}{% endif %} +{% macro ubitem(it) %}{% if it.link %}{% if it.icon %}::{{ it.icon }}::{% endif %}{% if it.label %}{{ it.label }}{% endif %}{% else %}{% if it.icon %}::{{ it.icon }}::{% endif %}{% if it.label %}{{ it.label }}{% endif %}{% endif %}{% endmacro %} +
+
+
{% for it in leftItems %}{{ ubitem(it) }}{% endfor %}
+
{% for it in rightItems %}{{ ubitem(it) }}{% endfor %}
+
+
diff --git a/templates/overrides/y2k/video-embed.ninjatpl b/templates/overrides/y2k/video-embed.ninjatpl new file mode 100644 index 0000000..ff815c6 --- /dev/null +++ b/templates/overrides/y2k/video-embed.ninjatpl @@ -0,0 +1,23 @@ +{# video-embed y2k override: neon HUD frame, clip-cut play button. Facade script + data-bn-* attributes + {% img %} preserved byte-for-byte. #} +{% with aspect=aspect|default:"16:9" %}
{% if caption %}
{{ caption }}
{% endif %}
{% endwith %} diff --git a/templates/y2k/article.ninjatpl b/templates/y2k/article.ninjatpl index e6fc052..9dc8fce 100644 --- a/templates/y2k/article.ninjatpl +++ b/templates/y2k/article.ninjatpl @@ -1,22 +1,15 @@ - + {{ head_html|safe }} - + {{ admin_banner_html|safe }} -
- {{ slots.header|safe }} -
-
-
- {% if slots.main %}
{{ slots.main|safe }}
{% else %}

No content blocks assigned to this page.

{% endif %} -
- -
-
- {{ slots.footer|safe }} -
+ +
{{ slots.header|safe }}
+
+
{{ slots.main|safe }}
+ {% if slots.aside %}{% endif %} +
+
{{ slots.footer|safe }}
{{ body_end_html|safe }} diff --git a/templates/y2k/auth.ninjatpl b/templates/y2k/auth.ninjatpl new file mode 100644 index 0000000..5297429 --- /dev/null +++ b/templates/y2k/auth.ninjatpl @@ -0,0 +1,14 @@ + + +{{ head_html|safe }} + + {{ admin_banner_html|safe }} + +
{{ slots.header|safe }}
+
+
{% if slots.main %}{{ slots.main|safe }}{% else %}

Sign in

{% endif %}
+
+
{{ slots.footer|safe }}
+ {{ body_end_html|safe }} + + diff --git a/templates/y2k/blog-index.ninjatpl b/templates/y2k/blog-index.ninjatpl new file mode 100644 index 0000000..8d999e0 --- /dev/null +++ b/templates/y2k/blog-index.ninjatpl @@ -0,0 +1,12 @@ + + +{{ head_html|safe }} + + {{ admin_banner_html|safe }} + +
{{ slots.header|safe }}
+
{% if slots.main %}{{ slots.main|safe }}{% else %}

No posts yet

{% endif %}
+
{{ slots.footer|safe }}
+ {{ body_end_html|safe }} + + diff --git a/templates/y2k/contact.ninjatpl b/templates/y2k/contact.ninjatpl new file mode 100644 index 0000000..31efb90 --- /dev/null +++ b/templates/y2k/contact.ninjatpl @@ -0,0 +1,12 @@ + + +{{ head_html|safe }} + + {{ admin_banner_html|safe }} + +
{{ slots.header|safe }}
+
{% if slots.main %}{{ slots.main|safe }}{% else %}

Add a contact form here

{% endif %}
+
{{ slots.footer|safe }}
+ {{ body_end_html|safe }} + + diff --git a/templates/y2k/default.ninjatpl b/templates/y2k/default.ninjatpl index ed6617a..953764d 100644 --- a/templates/y2k/default.ninjatpl +++ b/templates/y2k/default.ninjatpl @@ -1,17 +1,12 @@ - + {{ head_html|safe }} - + {{ admin_banner_html|safe }} -
- {{ slots.header|safe }} -
-
- {% if slots.main %}{{ slots.main|safe }}{% else %}

No content blocks assigned to this page.

{% endif %} -
-
- {{ slots.footer|safe }} -
+ +
{{ slots.header|safe }}
+
{% if slots.main %}{{ slots.main|safe }}{% else %}

Nothing on this page yet

{% endif %}
+
{{ slots.footer|safe }}
{{ body_end_html|safe }} diff --git a/templates/y2k/full-width.ninjatpl b/templates/y2k/full-width.ninjatpl index 3d1222b..c2e1e45 100644 --- a/templates/y2k/full-width.ninjatpl +++ b/templates/y2k/full-width.ninjatpl @@ -1,17 +1,12 @@ - + {{ head_html|safe }} - + {{ admin_banner_html|safe }} -
- {{ slots.header|safe }} -
-
- {% if slots.main %}{{ slots.main|safe }}{% else %}

No content blocks assigned to this page.

{% endif %} -
-
- {{ slots.footer|safe }} -
+ +
{{ slots.header|safe }}
+
{% if slots.main %}{{ slots.main|safe }}{% else %}

Nothing on this page yet

{% endif %}
+
{{ slots.footer|safe }}
{{ body_end_html|safe }} diff --git a/templates/y2k/landing.ninjatpl b/templates/y2k/landing.ninjatpl index d53f13e..cfd7934 100644 --- a/templates/y2k/landing.ninjatpl +++ b/templates/y2k/landing.ninjatpl @@ -1,23 +1,12 @@ - + {{ head_html|safe }} - + {{ admin_banner_html|safe }} -
- {{ slots.hero|safe }} -
-
- {{ slots.marquee|safe }} -
-
- {% if slots.main %}
{{ slots.main|safe }}
{% endif %} -
-
- {{ slots.cta|safe }} -
-
- {{ slots.footer|safe }} -
+ +
{{ slots.header|safe }}
+
{% if slots.main %}{{ slots.main|safe }}{% else %}

Nothing on this page yet

{% endif %}
+ {{ body_end_html|safe }}