Compare commits

..

No commits in common. "b05ecf145d0cbd1d6d952411471ff41c281baada" and "a86a54639aa93e4296ae607e9a9921d3d359b937" have entirely different histories.

94 changed files with 734 additions and 2447 deletions

View File

@ -1,25 +0,0 @@
# Y2K
A millennium-web theme built for musicians and bands. Liquid-chrome bevels, frosted plastic, gradient mesh, marquees, and a little glitter bring back the turn-of-the-2000s music site. Frosted iMac plastic in light mode, rave blacklight in dark.
## Good for
We built Y2K for artists who want a page with personality. Treat these as starting points, not limits. If the mood fits your act, use it:
- Musicians and bands
- Record and tour pages
- Indie labels and collectives
- Creators leaning into nostalgia
## What you get
- Five color presets, each with a tuned light mode and a tuned dark mode: Chrome Candy, Bubblegum, Lime Fizz, Graphite, and Tangerine.
- Orbitron display type, Inter body, and Share Tech Mono, bundled and ready. Fonts stay admin-controlled, so you can swap them from the typography settings.
- Five persona blocks: a discography, tour dates, a tracklist, a waveform player, and a webring badge.
- A liquid-chrome hero that renders metaballs on a canvas at the top of the page. It respects reduced-motion and falls back to a static chrome backdrop with JavaScript off.
- A demo site that installs when you activate the theme: a home page, an about page, a short blog, and a contact page whose form emails your admins.
- Every core block restyled to match, plus a themed login screen and a themed 404 page.
## Install
Install from the BlockNinja registry, then pick a preset and assign your fonts in the admin. Activate the demo content if you want a fully dressed starting point.

View File

@ -1,36 +1,50 @@
# Recommended fonts for the Y2K theme
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.
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.
## Bundled (works out of the box)
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):
| 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. |
## Heading (display)
Assign each to its slot in the typography panel (they appear under the bundled /
template source). Until then, the fallback stacks below apply automatically.
- **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.
## Fallback stacks (declared in `manifest.yaml` CSS)
## Body
- 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)`
- **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.
## Alternatives from the Google Fonts tab
## Mono
- **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.
- **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.
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.
## 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.

View File

@ -1,15 +0,0 @@
# Recommended icon packs for the Y2K theme
The theme renders icons only as sprite references
(`<svg><use href="/icons/<pack>.svg#<name>"/></svg>`) 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.

View File

@ -1,25 +0,0 @@
# 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 500900) | 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 400700) | 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.

Binary file not shown.

Binary file not shown.

5
assets/placeholder.txt Normal file
View File

@ -0,0 +1,5 @@
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).

View File

@ -1,43 +1,66 @@
# Y2K theme persona block definitions (codeless .bnp, WO-TF-026).
# Y2K theme block definitions (codeless .bnp, WO-WZ-021).
# Keys are fully qualified (y2k:<key>) 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
# 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.
# hardcoded data-block-key attributes and the master_pages.json references.
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: Track card with cover art, a static chrome equalizer strip, and a native audio player.
description: Soundcloud-style audio player with a canvas waveform.
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: Numbered tracklist styled like a CD jewel-case insert with durations.
description: Tabular tracklist styled like a CD jewel case insert.
category: content
schema: tracklist.schema.json
template: tracklist.ninjatpl
- key: y2k:tour_dates
title: Tour Dates
description: Live dates with venue, city, sold-out state, and a ticket link.
- key: y2k:merch_card
title: Merch Card
description: Plastic-bevel product card with a corner sticker badge.
category: content
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
schema: merch_card.schema.json
template: merch_card.ninjatpl
- key: y2k:webring_badge
title: Webring Badge
description: Canonical 88x31 webring button row with prev / ring / next links.
description: Canonical 88x31 webring button row with prev/next/ring 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

View File

@ -0,0 +1,12 @@
<nav class="y2k-chrome-bg y2k-bevel relative w-full" data-block-key="y2k:chrome_navbar" data-menu="{{ menuName|default:"main" }}">
<div class="max-w-6xl mx-auto flex items-center justify-between gap-4 px-4 py-3">
<a href="/" class="flex items-center gap-2 text-foreground no-underline">
{% if logoImage %}<img src="{{ logoImage }}" alt="{{ logoText }}" class="h-8 w-auto y2k-image-frame">{% else %}<span class="y2k-heading text-xl uppercase">{{ logoText|default:"y2k.fm" }}</span>{% endif %}
</a>
<button type="button" class="md:hidden y2k-button text-sm" aria-controls="y2k-nav-{{ menuName|default:"main" }}" aria-expanded="false" data-y2k-nav-toggle>Menu</button>
<div id="y2k-nav-{{ menuName|default:"main" }}" class="hidden md:flex items-center gap-4" data-menu-target="{{ menuName|default:"main" }}">
<span class="text-muted-foreground text-sm uppercase tracking-wider">menu:{{ menuName|default:"main" }}</span>
</div>
</div>
<span class="y2k-sparkle absolute right-3 top-1 text-accent">*</span>
</nav>

View File

@ -0,0 +1,29 @@
{
"$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": ""
}
}
}

View File

@ -1,29 +0,0 @@
{# y2k:discography — grid of releases with cover art, year, format, and a link. #}
<section data-block="y2k:discography" class="py-12 md:py-16 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-6xl px-4">
{% if heading %}<h2 class="y2k-display glitch text-2xl font-semibold tracking-tight md:text-3xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
{% if releases %}
<div class="mt-8 y2k-grid-records">
{% for release in releases %}
<article class="y2k-bevel y2k-card y2k-glow group overflow-hidden bg-card text-card-foreground">
{% if release.cover %}
<div class="aspect-square overflow-hidden bg-muted">
{% if release.link %}<a href="{{ release.link }}" class="block h-full w-full">{% img release.cover alt=release.title class="h-full w-full object-cover transition-transform duration-300 group-hover:scale-105" %}</a>{% else %}{% img release.cover alt=release.title class="h-full w-full object-cover" %}{% endif %}
</div>
{% else %}
<div class="y2k-media-empty y2k-chrome-bg text-muted-foreground">
{% if release.link %}<a href="{{ release.link }}" class="flex h-full w-full items-center justify-center" aria-label="{{ release.title }}"><svg class="h-12 w-12" aria-hidden="true"><use href="/icons/lucide.svg#disc"></use></svg></a>{% else %}<svg class="h-12 w-12" aria-hidden="true"><use href="/icons/lucide.svg#disc"></use></svg>{% endif %}
</div>
{% endif %}
<div class="p-3">
{% if release.title %}<h3 class="y2k-heading truncate text-sm text-foreground">{% if release.link %}<a href="{{ release.link }}" class="hover:underline">{{ release.title }}</a>{% else %}{{ release.title }}{% endif %}</h3>{% endif %}
<p class="y2k-mono mt-1 flex items-center gap-2 text-xs uppercase tracking-wide text-muted-foreground">{% if release.year %}<span>{{ release.year }}</span>{% endif %}{% if release.format %}<span class="text-primary">{{ release.format }}</span>{% endif %}</p>
</div>
</article>
{% endfor %}
</div>
{% else %}
<p class="y2k-mono mt-8 text-muted-foreground">No releases yet.</p>
{% endif %}
</div>
</section>

View File

@ -1,9 +0,0 @@
{
"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": "#" }
]
}

View File

@ -1,30 +0,0 @@
{
"$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": []
}
}
}

View File

@ -0,0 +1,7 @@
<div class="y2k-chrome-bg y2k-bevel mt-8" data-block-key="y2k:footer_chrome">
<div class="max-w-6xl mx-auto px-4 py-4 flex flex-wrap items-center justify-between gap-3">
{% if menuName %}<nav class="flex gap-3 text-sm uppercase tracking-wider" data-menu-target="{{ menuName }}"><span class="text-muted-foreground">menu:{{ menuName }}</span></nav>{% endif %}
{% if showWebring != "no" and showWebring != false %}<div class="flex items-center gap-2"><a href="#" class="y2k-webring-badge">prev</a><a href="#" class="y2k-webring-badge">ring</a><a href="#" class="y2k-webring-badge">next</a></div>{% endif %}
{% if copyright %}<span class="text-xs text-muted-foreground">{{ copyright }}</span>{% endif %}
</div>
</div>

View File

@ -0,0 +1,30 @@
{
"$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": ""
}
}
}

View File

@ -0,0 +1,6 @@
<hr class="border-0 my-6" role="separator" data-block-key="y2k:glitter_divider" data-variant="{{ variant|default:"sparkle" }}">
<div class="flex items-center justify-center gap-2 -mt-4 mb-4 text-accent" aria-hidden="true">
<span class="y2k-sparkle">{% if variant == "hearts" %}&lt;3{% elif variant == "chrome" %}~{% else %}*{% endif %}</span>
<span class="y2k-sparkle" style="animation-delay: 200ms">{% if variant == "hearts" %}&lt;3{% elif variant == "chrome" %}~{% else %}*{% endif %}</span>
<span class="y2k-sparkle" style="animation-delay: 400ms">{% if variant == "hearts" %}&lt;3{% elif variant == "chrome" %}~{% else %}*{% endif %}</span>
</div>

View File

@ -0,0 +1,15 @@
{
"$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"
}
}
}

5
blocks/marquee.ninjatpl Normal file
View File

@ -0,0 +1,5 @@
<div class="y2k-marquee y2k-chrome-bg" data-block-key="y2k:marquee" data-direction="{{ direction|default:"left" }}" tabindex="0" aria-label="ticker">
<div class="y2k-marquee-track py-2 px-4 text-sm uppercase tracking-wider text-foreground" data-speed="{{ speed|default:"medium" }}">
{% if items %}{% for item in items %}<span class="inline-flex items-center gap-2"><span class="text-accent">*</span>{{ item }}</span>{% endfor %}{% for item in items %}<span class="inline-flex items-center gap-2" aria-hidden="true"><span class="text-accent">*</span>{{ item }}</span>{% endfor %}{% else %}<span class="inline-flex items-center gap-2"><span class="text-accent">*</span>now playing</span><span class="inline-flex items-center gap-2"><span class="text-accent">*</span>new drop friday</span><span class="inline-flex items-center gap-2"><span class="text-accent">*</span>subscribe to the zine</span><span class="inline-flex items-center gap-2" aria-hidden="true"><span class="text-accent">*</span>now playing</span><span class="inline-flex items-center gap-2" aria-hidden="true"><span class="text-accent">*</span>new drop friday</span><span class="inline-flex items-center gap-2" aria-hidden="true"><span class="text-accent">*</span>subscribe to the zine</span>{% endif %}
</div>
</div>

View File

@ -0,0 +1,32 @@
{
"$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"
}
}
}

View File

@ -0,0 +1,13 @@
<article class="relative y2k-bevel rounded-2xl overflow-hidden bg-card flex flex-col" data-block-key="y2k:merch_card">
{% if sticker %}<span class="absolute top-3 right-3 z-10 y2k-button text-xs uppercase tracking-widest">{% if sticker == "sold-out" %}sold out{% else %}{{ sticker }}{% endif %}</span>{% endif %}
<div class="aspect-square bg-muted overflow-hidden y2k-image-frame border-0 rounded-none">
{% if image %}<img src="{{ image }}" alt="{{ title }}" class="w-full h-full object-cover">{% else %}<div class="w-full h-full flex items-center justify-center text-muted-foreground text-xs uppercase tracking-widest">no image</div>{% endif %}
</div>
<div class="p-4 flex flex-col gap-2 flex-1">
<h3 class="y2k-heading text-foreground text-lg">{{ title|default:"untitled" }}</h3>
<div class="flex items-center justify-between gap-3 mt-auto">
<span class="text-foreground font-bold">{{ price|default:"$--" }}</span>
{% if buyHref %}<a href="{{ buyHref }}" class="y2k-button text-sm">buy</a>{% endif %}
</div>
</div>
</article>

View File

@ -0,0 +1,41 @@
{
"$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": ""
}
}
}

View File

@ -0,0 +1,32 @@
<section class="relative overflow-hidden y2k-mesh-bg py-24 md:py-32" data-block-key="y2k:metaball_hero" data-bg-preset="{{ bgPreset|default:"magenta-teal" }}">
<svg class="absolute inset-0 w-full h-full opacity-80" viewBox="0 0 600 400" preserveAspectRatio="none" aria-hidden="true">
<defs>
<filter id="y2k-metaball" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur in="SourceGraphic" stdDeviation="18" result="b1"></feGaussianBlur>
<feGaussianBlur in="b1" stdDeviation="8" result="b2"></feGaussianBlur>
<feColorMatrix in="b2" type="matrix" values="
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 22 -10" result="meta"></feColorMatrix>
<feComposite in="meta" in2="SourceGraphic" operator="atop"></feComposite>
</filter>
<linearGradient id="y2k-mb-grad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="currentColor"></stop>
<stop offset="100%" stop-color="currentColor" stop-opacity="0.7"></stop>
</linearGradient>
</defs>
<g filter="url(#y2k-metaball)" fill="url(#y2k-mb-grad)" class="text-primary">
<circle class="y2k-metaball" cx="180" cy="200" r="80"></circle>
<circle class="y2k-metaball" cx="320" cy="160" r="70"></circle>
<circle class="y2k-metaball" cx="430" cy="240" r="90"></circle>
</g>
</svg>
<div class="relative z-10 max-w-4xl mx-auto px-4 text-center">
<div class="inline-block px-6 py-4 rounded-2xl bg-card/70 backdrop-blur-md y2k-bevel">
<h1 class="y2k-heading text-[4rem] md:text-[5rem] leading-tight font-bold text-foreground">{{ headline|default:"new EP out now" }}</h1>
{% if sub %}<p class="text-lg md:text-xl text-foreground/80 mt-3 mb-6">{{ sub }}</p>{% endif %}
{% if ctaLabel %}<a href="{{ ctaHref|default:"#" }}" class="y2k-button text-lg">{{ ctaLabel }}</a>{% endif %}
</div>
</div>
</section>

View File

@ -0,0 +1,44 @@
{
"$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"
}
}
}

View File

@ -0,0 +1,9 @@
<section class="my-6" data-block-key="y2k:nft_gallery">
{% if not items %}<p class="text-center text-muted-foreground py-12">No items yet.</p>{% else %}<div class="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">{% for item in items %}<a href="{{ item.href|default:"#" }}" class="y2k-foil y2k-bevel block bg-card rounded-xl overflow-hidden no-underline text-foreground">
<div class="aspect-square bg-muted overflow-hidden">{% if item.image %}<img src="{{ item.image }}" alt="{{ item.title }}" class="w-full h-full object-cover">{% else %}<div class="w-full h-full flex items-center justify-center text-muted-foreground text-xs uppercase tracking-widest">no image</div>{% endif %}</div>
<div class="p-3 flex items-center justify-between gap-2">
<span class="font-semibold truncate">{{ item.title|default:"untitled" }}</span>
{% if item.chain %}<span class="text-xs uppercase tracking-widest text-muted-foreground">{{ item.chain }}</span>{% endif %}
</div>
</a>{% endfor %}</div>{% endif %}
</section>

View File

@ -0,0 +1,41 @@
{
"$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": []
}
}
}

View File

@ -1,23 +0,0 @@
{# y2k:tour_dates — live dates with venue, city, and a ticket link. Chrome-bevel rows. #}
<section data-block="y2k:tour_dates" class="py-12 md:py-16 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-4xl px-4">
{% if heading %}<h2 class="y2k-display glitch text-2xl font-semibold tracking-tight md:text-3xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mt-3 max-w-2xl text-muted-foreground">{{ intro }}</p>{% endif %}
{% if dates %}
<ul class="mt-8 space-y-3">
{% for show in dates %}
<li class="y2k-bevel y2k-card flex flex-wrap items-center gap-x-4 gap-y-2 bg-card p-4 text-card-foreground">
{% if show.date %}<span class="y2k-mono w-28 shrink-0 text-sm uppercase tracking-wide text-primary">{{ show.date }}</span>{% endif %}
<span class="min-w-0 flex-1">
{% if show.venue %}<span class="y2k-heading block truncate text-base text-foreground">{{ show.venue }}</span>{% endif %}
{% if show.city %}<span class="block text-sm text-muted-foreground">{{ show.city }}</span>{% endif %}
</span>
{% if show.soldOut %}<span class="y2k-mono shrink-0 rounded-md border border-border bg-muted px-3 py-1.5 text-xs uppercase tracking-wide text-muted-foreground">Sold out</span>{% elif show.ticketUrl %}<a href="{{ show.ticketUrl }}" class="y2k-button rgb-split shrink-0 text-sm">Tickets</a>{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<p class="y2k-mono mt-8 text-muted-foreground">No dates announced yet.</p>
{% endif %}
</div>
</section>

View File

@ -1,9 +0,0 @@
{
"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 }
]
}

View File

@ -1,36 +0,0 @@
{
"$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": []
}
}
}

View File

@ -1,17 +1,13 @@
{% if audioUrl or trackTitle %}<section class="py-8 bg-background" data-block-key="y2k:waveform_player">
<div class="mx-auto max-w-4xl px-4">
<figure class="y2k-bevel y2k-chrome-bg rounded-xl overflow-hidden flex flex-col gap-4 p-4 sm:flex-row sm:items-stretch">
{% if audioUrl or trackTitle %}<figure class="y2k-bevel y2k-chrome-bg rounded-xl overflow-hidden flex items-stretch gap-4 p-3 my-4" data-block-key="y2k:waveform_player">
<div class="w-20 h-20 shrink-0 y2k-image-frame overflow-hidden bg-muted">
{% if coverImage %}{% img coverImage alt=trackTitle class="w-full h-full object-cover" %}{% else %}<span class="flex h-full w-full items-center justify-center text-muted-foreground"><svg class="h-8 w-8" aria-hidden="true"><use href="/icons/lucide.svg#music"></use></svg></span>{% endif %}
{% if coverImage %}<img src="{{ coverImage }}" alt="{{ trackTitle }}" class="w-full h-full object-cover">{% else %}<svg viewBox="0 0 24 24" class="w-full h-full text-muted-foreground p-4" fill="currentColor" aria-hidden="true"><path d="M9 19V6l11-3v13"></path></svg>{% endif %}
</div>
<div class="flex-1 min-w-0 flex flex-col justify-between gap-2">
<figcaption class="flex flex-wrap items-baseline justify-between gap-x-3 gap-y-1 min-w-0">
<figcaption class="flex items-baseline justify-between gap-3 min-w-0">
<span class="y2k-heading text-base truncate text-foreground">{{ trackTitle|default:"untitled track" }}</span>
{% if artist %}<span class="y2k-mono text-xs uppercase tracking-wider text-muted-foreground shrink-0">{{ artist }}</span>{% endif %}
{% if artist %}<span class="text-xs uppercase tracking-wider text-muted-foreground shrink-0">{{ artist }}</span>{% endif %}
</figcaption>
<div class="w-full h-12 y2k-waveform" aria-hidden="true"></div>
{% if audioUrl %}<audio controls preload="none" class="w-full" src="{{ audioUrl }}">Your browser does not support the audio element.</audio>{% else %}<p class="y2k-mono text-xs text-muted-foreground">No audio source set yet.</p>{% endif %}
<canvas class="w-full h-12 bg-muted/40 rounded" data-y2k-waveform data-audio-url="{{ audioUrl }}" aria-label="audio waveform"></canvas>
{% if audioUrl %}<audio controls preload="none" class="w-full" src="{{ audioUrl }}">Your browser does not support the audio element.</audio>{% else %}<p class="text-xs text-muted-foreground">No audio source configured.</p>{% endif %}
</div>
</figure>
</div>
</section>{% endif %}
</figure>{% endif %}

View File

@ -1,28 +1 @@
[
{
"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" }
]
}
]
[]

View File

@ -1,390 +1,118 @@
# Y2K theme — codeless .bnp manifest (WO-TF-026, Wave A).
# Y2K theme — codeless .bnp manifest (WO-WZ-021 Phase B).
# 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: "Millennium-web band theme — liquid-chrome bevels, frosted plastic, marquees, glitter, a webring, and a canvas metaball hero."
description: "Liquid-chrome Y2K revival theme for indie musicians, zines, and merch shops."
page_templates:
- system: y2k
key: default
title: Default
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"
description: "Chrome navbar + content + footer."
slots: [header, main, footer]
- system: y2k
key: landing
title: Landing
description: "Canvas metaball hero, chrome feature grid, glitter CTA, chrome footer"
slots: [header, main, footer]
description: "Hero metaball + marquee + CTAs."
slots: [hero, marquee, main, cta, footer]
- system: y2k
key: article
title: Article / Liner Notes
description: "Prose column with a chrome sidebar aside — liner-notes layout"
title: Article / Zine Page
description: "Narrow column with sidebar tracklist."
slots: [header, main, aside, footer]
- system: y2k
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"
key: full-width
title: Full Width
description: "Edge-to-edge gradient mesh canvas."
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: 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 }
- { template: y2k, block: image }
email_wrappers:
- y2k
css:
input_css_append: |
/* ============================================================
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 ----------------- */
/* === y2k theme — chrome/bevel/mesh tokens === */
:root {
--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%;
--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%;
--bevel-light: 0 0% 100%;
--bevel-dark: 250 45% 22%;
--bevel-dark: 280 60% 12%;
}
.dark {
--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%;
--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%;
}
/* --- Keyframes -------------------------------------------- */
@keyframes y2k-marquee-x {
/* === keyframes === */
@keyframes marquee-x {
from { transform: translate3d(0, 0, 0); }
to { transform: translate3d(-50%, 0, 0); }
}
@keyframes y2k-sparkle {
0%, 100% { opacity: 0; transform: scale(0.4) rotate(0deg); }
@keyframes sparkle {
0%, 100% { opacity: 0; transform: scale(0.5) rotate(0deg); }
50% { opacity: 1; transform: scale(1) rotate(180deg); }
}
@keyframes y2k-shimmer-sweep {
0% { background-position: -150% 0; }
100% { background-position: 250% 0; }
}
@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); }
@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"); }
}
/* --- 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;
/* Subtle chrome bevel only — the old wide primary halo read as a
stray rendering artifact behind every heading, so it is gone. */
text-shadow:
0 1px 0 hsl(var(--bevel-light) / 0.55),
0 1px 1px hsl(var(--bevel-dark) / 0.22);
}
/* Chrome shimmer sweep across big headlines (motion, reduced-motion-safe).
Toned to a faint glint so it never smears the letters in a still frame. */
.glitch {
position: relative;
background-image: linear-gradient(
100deg,
transparent 35%,
hsl(var(--bevel-light) / 0.3) 48%,
hsl(var(--accent) / 0.22) 50%,
transparent 62%);
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 -------------------------- */
/* === chrome surface utilities === */
.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 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%),
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%),
linear-gradient(135deg, hsl(var(--background)) 0%, hsl(var(--card)) 100%);
}
/* 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;
.y2k-bevel {
border: 2px solid hsl(var(--border));
box-shadow:
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);
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);
}
.y2k-image-frame {
border-radius: 0.75rem;
border: 2px solid hsl(var(--border));
.y2k-bevel:active {
box-shadow:
inset 0 1px 0 hsl(var(--bevel-light) / 0.5),
0 4px 16px hsl(var(--bevel-dark) / 0.35);
inset 0 2px 4px hsl(var(--bevel-dark) / 0.45),
inset 0 -1px 0 hsl(var(--bevel-light) / 0.40);
}
.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;
}
.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;
.y2k-glow {
box-shadow: 0 0 24px hsl(var(--primary) / 0.45);
}
/* --- 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);
}
/* --- Responsive persona grids ----------------------------- */
/* Explicit media queries so column counts do NOT depend on the host
Tailwind build having scanned these theme templates (persona blocks
use responsive class combos no builtin emits, so the JIT drops them
and the grid collapses to its base count). 1 col on phones. */
.y2k-grid-records {
display: grid;
gap: 1.5rem;
grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 640px) {
.y2k-grid-records { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
.y2k-grid-records { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* Compact chrome tile for absent album/track art — a fixed, modest
height so an empty media field never balloons into a blank box. */
.y2k-media-empty {
display: flex;
align-items: center;
justify-content: center;
height: 11rem;
}
/* --- Marquee ---------------------------------------------- */
/* === marquee === */
.y2k-marquee {
overflow: hidden;
position: relative;
@ -395,10 +123,9 @@ css:
display: inline-flex;
gap: 3rem;
white-space: nowrap;
animation-name: y2k-marquee-x;
animation-name: 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; }
@ -408,116 +135,130 @@ css:
animation-play-state: paused;
}
/* --- Sparkle / glitter atmosphere ------------------------- */
/* === sparkle decoration === */
.y2k-sparkle {
pointer-events: none;
animation: y2k-sparkle 1.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;
animation: sparkle 1.6s ease-in-out infinite;
}
/* --- 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;
/* === metaball morph === */
.y2k-metaball {
animation: metaball-morph 6s ease-in-out infinite;
}
/* --- Webring 88x31 badge ---------------------------------- */
/* === webring 88x31 badge === */
.y2k-webring-badge {
display: inline-flex;
align-items: center;
justify-content: center;
display: inline-block;
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(--primary-foreground));
color: hsl(var(--primaryForeground));
text-decoration: none;
box-shadow: inset 0 1px 0 hsl(var(--bevel-light) / 0.6);
}
/* --- 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;
/* === holographic foil for NFT gallery === */
.y2k-foil {
position: relative;
isolation: isolate;
}
/* --- 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 <canvas> lazy-inits from the hero's inline script; this styles the
container plus the static no-JS chrome-mesh fallback. */
.y2k-orb-wrap {
.y2k-foil::after {
content: "";
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-orb-canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
opacity: 0.65;
filter: blur(2px) contrast(1.15);
}
.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-foil:hover::after,
.y2k-foil:focus-within::after {
opacity: 1;
filter: hue-rotate(45deg);
}
/* --- Reduced motion: kill all motion ---------------------- */
/* === 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-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 === */
@media (prefers-reduced-motion: reduce) {
.y2k-marquee-track,
.y2k-sparkle,
.glitch,
.caret-blink,
.y2k-status-dot,
.y2k-orb-canvas {
.y2k-metaball {
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; }
}

View File

@ -1,22 +1,24 @@
[
{
"key": "y2k:site-master",
"title": "Y2K Site Master",
"page_templates": ["default", "full-width", "landing", "article", "blog-index", "contact"],
"key": "y2k:default-master",
"title": "Y2K Default Master",
"page_templates": ["default", "article"],
"blocks": [
{ "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 }
{ "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 }
]
},
{
"key": "y2k:auth-master",
"title": "Y2K Auth Master",
"page_templates": ["auth"],
"key": "y2k:landing-master",
"title": "Y2K Landing Master",
"page_templates": ["landing", "full-width"],
"blocks": [
{ "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 }
{ "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 }
]
}
]

View File

@ -2,8 +2,8 @@
name = "y2k"
display_name = "Y2K"
scope = "@themes"
version = "0.2.1"
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."
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."
kind = "theme"
categories = ["templates"]
tags = ["retro", "chrome", "vaporwave", "music", "band", "creator", "nostalgia", "dark"]
tags = ["retro", "chrome", "vaporwave", "pastel", "music", "creator", "nostalgia", "web3"]

View File

@ -1,251 +1,109 @@
[
{
"id": "chrome-candy",
"name": "Chrome Candy",
"description": "Bondi-blue frosted plastic in light, UV magenta rave in dark — the Y2K default",
"id": "chrome-dream",
"name": "Liquid Chrome Dream",
"description": "Magenta-to-teal mesh on tinted glass cards. Works in both light and dark modes.",
"theme": {
"mode": "both",
"lightColors": {
"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%",
"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%",
"primaryForeground": "0 0% 100%",
"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%",
"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": "205 35% 84%",
"input": "205 35% 84%",
"ring": "270 80% 58%"
"border": "300 40% 88%",
"input": "300 30% 92%",
"ring": "320 95% 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%"
"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": "bubblegum",
"name": "Bubblegum",
"description": "Cotton-candy pink plastic in light, hot-pink blacklight in dark",
"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": "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%"
},
"mode": "dark",
"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%"
"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": "lime-fizz",
"name": "Lime Fizz",
"description": "Frosted lime soda in light, radioactive green rave in dark",
"id": "bubblegum-trapper",
"name": "Bubblegum Trapper",
"description": "Lisa Frank Trapper-Keeper energy — pastel candy and sour apple accents.",
"theme": {
"mode": "both",
"mode": "light",
"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%",
"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": "150 35% 90%",
"secondaryForeground": "170 35% 12%",
"muted": "150 28% 92%",
"mutedForeground": "170 18% 40%",
"accent": "320 85% 55%",
"accentForeground": "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%",
"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%"
"border": "330 50% 85%",
"input": "330 40% 90%",
"ring": "330 95% 60%"
}
}
}

View File

@ -1,222 +0,0 @@
{
"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": "<p>Chrome Static started in a share house with a borrowed drum machine and a broken amp. We kept the sound and fixed the amp.</p><p>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.</p>" } },
{ "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": "<p>Tape makes us commit. Here is what that costs and what we get back.</p>", "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": "<p>Three cities in a van. What went right and what broke.</p>", "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": "<p>A cheap chorus pedal from 1999. It is the whole sound.</p>", "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": "<p>Tape does not let you undo. You play it, you keep it, or you play it again. That pressure shows up in the take.</p><p>It is slower and it costs more. We think the trade is worth it. The record sounds like a room, not a grid.</p>" } }
]
},
{
"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": "<p>Three cities, one van, four amps. The van held. One amp did not.</p><p>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.</p>" } }
]
},
{
"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": "<p>It is a plastic chorus pedal from 1999. It hisses and the knob is loose. It is also the whole guitar sound.</p><p>We have tried to replace it twice. Both times the record sounded wrong, so the old one went back on the board.</p>" } }
]
},
{
"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 }
]
}

View File

@ -1,19 +0,0 @@
{
"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" }
}
]
}
]
}

View File

@ -18,7 +18,7 @@
}
</style>
</head>
<body style="background-color: {{ colors.background|default:"#0c0820" }}; margin: 0; padding: 0; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">
<body style="background-color: {{ colors.background|default:"#0c0820" }}; margin: 0; padding: 0; font-family: &amp;#39;Inter Tight&amp;#39;,&amp;#39;Inter&amp;#39;,-apple-system,BlinkMacSystemFont,&amp;#39;Segoe UI&amp;#39;,Roboto,sans-serif;">
{% if preview_text %}<div style="display:none;max-height:0;overflow:hidden;mso-hide:all;">{{ preview_text }}</div>{% endif %}
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
<tr>

View File

@ -1,21 +0,0 @@
{# 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 %}
<div data-bn-announcement data-bn-annkey="{{ variant|default:'info' }}:{{ message|slugify }}" class="bn-announcement w-full {{ anncls }} {{ class }}">
<div class="max-w-7xl mx-auto px-10 py-2 relative flex items-center justify-center gap-2 text-sm text-center">
<p class="y2k-mono uppercase tracking-wide font-medium">{{ message }}{% if linkText and linkUrl %} <a href="{{ linkUrl }}" class="underline underline-offset-2 hover:opacity-80">{{ linkText }}</a>{% endif %}</p>
{% if dismissable %}<button type="button" data-bn-ann-dismiss aria-label="Dismiss announcement" class="absolute right-3 top-1/2 -translate-y-1/2 inline-flex items-center justify-center p-1 rounded hover:bg-black/10 transition-colors"><svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg></button>{% endif %}
</div>
</div>
{% if dismissable %}<script>
(function(){
var bars=document.querySelectorAll('[data-bn-announcement]:not([data-bn-ann-wired])');
bars.forEach(function(bar){
bar.setAttribute('data-bn-ann-wired','1');
var key='bnann:'+bar.getAttribute('data-bn-annkey');
try{if(localStorage.getItem(key)==='1'){bar.style.display='none';return;}}catch(e){}
var btn=bar.querySelector('[data-bn-ann-dismiss]');
if(btn)btn.addEventListener('click',function(){bar.style.display='none';try{localStorage.setItem(key,'1');}catch(e){}});
});
})();
</script>{% endif %}

View File

@ -1,16 +0,0 @@
{# audio-embed y2k override: neon panel, mono meta. Facade script + data-bn-* attributes + {% img %} preserved byte-for-byte. #}
<figure class="bn-audio my-6 overflow-hidden rounded-md border border-primary/40 bg-card text-card-foreground neon-edge-magenta{% if class %} {{ class }}{% endif %}" data-bn-audio data-audio-url="{{ url|default:"" }}" data-audio-media="{% if media %}{{ media|mediaURL }}{% endif %}"><div class="flex items-center gap-4 p-4">{% if artwork %}<div class="h-16 w-16 shrink-0 overflow-hidden rounded-md border border-primary/30 bg-muted">{% img artwork alt=title|default:"" class="h-full w-full object-cover" %}</div>{% endif %}<div class="min-w-0 flex-1">{% if title %}<p class="y2k-display truncate font-medium text-foreground">{{ title }}</p>{% endif %}{% if artist %}<p class="y2k-mono truncate text-sm text-muted-foreground">{{ artist }}</p>{% endif %}<div class="mt-2 flex items-center gap-3" data-bn-audio-slot><button type="button" data-bn-audio-play class="y2k-mono clip-cut inline-flex items-center gap-2 rounded-md bg-primary px-4 py-1.5 text-sm font-medium uppercase tracking-wide text-primary-foreground transition-colors hover:bg-primary/90 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring" aria-label="Play{% if title %}: {{ title }}{% endif %}">::lucide:play:sm:: Play</button>{% if download %}<a href="{% if media %}{{ media|mediaURL }}{% else %}{{ url }}{% endif %}" download class="y2k-mono inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground">::lucide:download:sm:: Download</a>{% endif %}</div></div></div></figure><script>
(function(){
if(window.__bnAudioFacade)return;window.__bnAudioFacade=true;
document.addEventListener('click',function(e){
var btn=e.target.closest('[data-bn-audio-play]');if(!btn)return;
var fig=btn.closest('[data-bn-audio]');if(!fig)return;
e.preventDefault();
var src=fig.getAttribute('data-audio-media')||fig.getAttribute('data-audio-url')||'';
if(!src)return;
var slot=btn.closest('[data-bn-audio-slot]');
var a=document.createElement('audio');a.src=src;a.controls=true;a.autoplay=true;a.preload='none';a.style.cssText='width:100%;min-width:12rem;height:2.25rem;';
btn.remove();slot.insertBefore(a,slot.firstChild);
});
})();
</script>

View File

@ -1,77 +0,0 @@
{# 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 %}
<div class="auth-form-logged-in mx-auto max-w-md text-center py-6">
<p class="y2k-mono text-sm uppercase tracking-wide text-muted-foreground">Session active as <strong class="text-primary">{{ context.publicUsername }}</strong></p>
<a href="/account" class="y2k-mono clip-cut mt-3 inline-flex items-center rounded-md bg-primary px-4 py-2 text-sm font-medium uppercase tracking-wide text-primary-foreground hover:bg-primary/90">My Account</a>
</div>
{% 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() %}<script>
window.CAP_CUSTOM_WASM_URL = "/assets/captcha/cap_wasm_bg.wasm";
window.CAP_PAKO_URL = "/assets/captcha/pako_inflate.min.js";
</script>
<cap-widget data-cap-api-endpoint="/api/captcha/" data-cap-worker-count="2"></cap-widget>
<script src="/assets/captcha/cap.min.js" defer></script>
<script>
(function(){
var s = document.currentScript;
var form = s && s.closest ? s.closest('form') : null;
if (!form) return;
form.addEventListener('htmx:afterRequest', function(){
var w = form.querySelector('cap-widget');
if (w && typeof w.reset === 'function') w.reset();
});
})();
</script>{% endmacro %}
{% macro social() %}{% if show_social != false and (context.site.google_oauth_client_id or context.site.facebook_app_id) %}<div class="auth-social my-4">
<div class="relative my-4"><hr class="border-border"/><span class="y2k-mono absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-background px-3 text-xs uppercase tracking-widest text-muted-foreground">or</span></div>
{% if context.site.google_oauth_client_id %}<a href="/api/auth/google/start" class="y2k-mono mb-2 flex w-full items-center justify-center gap-3 rounded-md border border-border px-4 py-2.5 uppercase tracking-wide transition-colors hover:bg-accent/10"><svg class="h-5 w-5" aria-hidden="true"><use href="/icons/simple-icons.svg#google"></use></svg><span class="text-sm font-medium text-foreground/80">Continue with Google</span></a>{% endif %}
{% if context.site.facebook_app_id %}<a href="/api/auth/facebook/start" class="y2k-mono flex w-full items-center justify-center gap-3 rounded-md border border-border px-4 py-2.5 uppercase tracking-wide transition-colors hover:bg-accent/10"><svg class="h-5 w-5" aria-hidden="true"><use href="/icons/simple-icons.svg#facebook"></use></svg><span class="text-sm font-medium text-foreground/80">Continue with Facebook</span></a>{% endif %}
</div>{% endif %}{% endmacro %}
<div class="auth-form hud-frame mx-auto max-w-md rounded-md border border-primary/40 bg-card p-6 text-card-foreground neon-edge-magenta">
<div id="auth-message"></div>
<div class="mb-5 flex border-b border-border">
<button type="button" data-auth-tab="login" data-tabbase="{{ tabbase }}" class="{{ tabbase }} {{ loginActive }}">Login</button>
<button type="button" data-auth-tab="register" data-tabbase="{{ tabbase }}" class="{{ tabbase }} {{ registerActive }}">Register</button>
</div>
<div id="login-panel" class="{{ loginHidden }}">
<form hx-post="/api/auth/login" hx-target="#auth-message" hx-swap="innerHTML">
<div class="mb-3"><label class="y2k-mono mb-1 block text-xs font-medium uppercase tracking-wide text-accent">Email</label><input type="email" name="email" required class="w-full rounded-md border border-primary/40 bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"/></div>
<div class="mb-4"><label class="y2k-mono mb-1 block text-xs font-medium uppercase tracking-wide text-accent">Password</label><input type="password" name="password" required class="w-full rounded-md border border-primary/40 bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"/></div>
{% if captchaEnabled %}{{ captcha() }}{% endif %}
<button type="submit" class="y2k-mono clip-cut rgb-split w-full rounded-md bg-primary px-4 py-2.5 font-semibold uppercase tracking-wider text-primary-foreground hover:bg-primary/90" hx-disabled-elt="this">Login</button>
<p class="mt-3 text-center text-sm"><a href="/forgot-password" class="y2k-mono uppercase tracking-wide text-primary hover:underline">Forgot password?</a></p>
</form>
{{ social() }}
</div>
<div id="register-panel" class="{{ registerHidden }}">
<form hx-post="/api/auth/register" hx-target="#auth-message" hx-swap="innerHTML">
<div class="mb-3"><label class="y2k-mono mb-1 block text-xs font-medium uppercase tracking-wide text-accent">Username</label><input type="text" name="username" required pattern="[a-zA-Z0-9_-]{3,30}" class="w-full rounded-md border border-primary/40 bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"/></div>
<div class="mb-3"><label class="y2k-mono mb-1 block text-xs font-medium uppercase tracking-wide text-accent">Email</label><input type="email" name="email" required class="w-full rounded-md border border-primary/40 bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"/></div>
<div class="mb-4"><label class="y2k-mono mb-1 block text-xs font-medium uppercase tracking-wide text-accent">Password</label><input type="password" name="password" required minlength="8" class="w-full rounded-md border border-primary/40 bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"/></div>
{% if captchaEnabled %}{{ captcha() }}{% endif %}
<button type="submit" class="y2k-mono clip-cut rgb-split w-full rounded-md bg-primary px-4 py-2.5 font-semibold uppercase tracking-wider text-primary-foreground hover:bg-primary/90" hx-disabled-elt="this">Create Account</button>
</form>
{{ social() }}
</div>
</div>
<script>
(function(){
var f=document.currentScript.closest('.auth-form')||document.querySelector('.auth-form');
if(!f)return;
var tabs=f.querySelectorAll('[data-auth-tab]');
tabs.forEach(function(btn){btn.addEventListener('click',function(){
var t=btn.getAttribute('data-auth-tab');
var lp=f.querySelector('#login-panel'),rp=f.querySelector('#register-panel');
if(lp)lp.classList.toggle('hidden',t!=='login');
if(rp)rp.classList.toggle('hidden',t!=='register');
tabs.forEach(function(b){var on=b===btn;b.className=b.getAttribute('data-tabbase')+(on?' border-primary text-primary':' border-transparent text-muted-foreground');});
});});
})();
</script>
{% endif %}

View File

@ -1,36 +0,0 @@
{# 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 %}
<div class="auth-status flex items-center gap-2">
<a href="/login" class="y2k-mono rounded-md px-3 py-1.5 text-sm font-medium uppercase tracking-wide opacity-80 transition-colors hover:bg-primary/10 hover:opacity-100">Login</a>
<a href="/login?tab=register" class="y2k-mono clip-cut rounded-md bg-accent px-3 py-1.5 text-sm font-medium uppercase tracking-wide text-accent-foreground hover:bg-accent/90">Sign Up</a>
</div>
{% else %}
{% if not context.publicEmailVerified %}<div class="fixed top-16 inset-x-0 z-40 border-y border-destructive/50 bg-destructive/15 px-4 py-2 text-center text-sm text-destructive">
<span class="y2k-mono uppercase tracking-wide">Verify your email address.</span> <button hx-post="/api/auth/resend-verification" hx-swap="outerHTML" class="underline font-medium">Resend verification email</button>
</div>{% endif %}
<div class="auth-status relative">
<button type="button" onclick="this.nextElementSibling.classList.toggle('hidden')" class="flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium opacity-90 transition-colors hover:bg-primary/10 hover:opacity-100">
<span class="clip-cut flex h-7 w-7 items-center justify-center rounded-md bg-accent text-accent-foreground"><svg class="h-4 w-4" aria-hidden="true"><use href="/icons/lucide.svg#user"></use></svg></span>
<span class="y2k-mono uppercase tracking-wide">{{ context.publicDisplayName|default:context.publicUsername }}</span>
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<div id="auth-dropdown-menu" class="hud-frame hidden absolute right-0 top-full mt-1 w-48 rounded-md border border-primary/40 bg-card py-1 text-card-foreground neon-edge-magenta z-50">
<a href="/account" class="y2k-mono block px-4 py-2 text-sm uppercase tracking-wide text-card-foreground hover:bg-primary/10">My Profile</a>
<a href="/account/reviews" class="y2k-mono block px-4 py-2 text-sm uppercase tracking-wide text-card-foreground hover:bg-primary/10">My Reviews</a>
<hr class="my-1 border-border"/>
<button hx-post="/api/auth/logout" hx-swap="none" class="y2k-mono block w-full px-4 py-2 text-left text-sm uppercase tracking-wide text-destructive hover:bg-primary/10">Logout</button>
</div>
<script>
(function(){
document.addEventListener('click', function(e) {
var menu = document.getElementById('auth-dropdown-menu');
var btn = document.querySelector('.auth-status > button');
if (!menu || !btn) return;
if (!btn.contains(e.target) && !menu.contains(e.target)) {
menu.classList.add('hidden');
}
});
})();
</script>
</div>
{% endif %}

View File

@ -1,22 +0,0 @@
{# author-bio-hero (y2k override) — author-page header from the `authors` provider. #}
{% if author %}
<section class="relative overflow-hidden border-b border-primary/30 bg-background py-12 text-foreground md:py-16{% if class %} {{ class }}{% endif %}">
<div class="scanlines pointer-events-none absolute inset-0"></div>
<div class="relative z-10 mx-auto flex max-w-3xl flex-col items-center px-4 text-center">
{% if showAvatar and author.avatar_url %}<div class="mb-5 h-24 w-24 overflow-hidden rounded-full bg-muted ring-2 ring-primary neon-edge-magenta">{% img author.avatar_url alt=author.display_name class="h-full w-full object-cover" %}</div>{% endif %}
<h1 class="y2k-display glitch text-3xl font-bold tracking-tight md:text-4xl" data-text="{{ author.display_name }}">{{ author.display_name }}</h1>
{% if author.job_title or author.company %}<p class="y2k-mono mt-1 text-sm uppercase tracking-wide text-accent">{{ author.job_title }}{% if author.job_title and author.company %} · {% endif %}{{ author.company }}</p>{% endif %}
{% if author.bio %}<p class="y2k-body mt-4 max-w-2xl leading-relaxed text-muted-foreground">{{ author.bio }}</p>{% endif %}
<div class="mt-5 flex flex-wrap items-center justify-center gap-4 y2k-mono text-sm uppercase tracking-wide text-accent">
{% if showPostCount %}<span>{{ author.post_count|default:0 }} post{% if author.post_count != 1 %}s{% endif %}</span>{% endif %}
{% if author.location %}<span>{{ author.location }}</span>{% endif %}
</div>
{% if showSocial %}<div class="mt-5 flex items-center justify-center gap-3">
{% if author.website_url %}<a href="{{ author.website_url }}" target="_blank" rel="noopener noreferrer" class="text-muted-foreground transition-colors hover:text-primary" title="Website"><svg class="h-5 w-5" aria-hidden="true"><use href="/icons/lucide.svg#globe"></use></svg></a>{% endif %}
{% if author.twitter_handle %}<a href="https://twitter.com/{{ author.twitter_handle|cut:"@" }}" target="_blank" rel="noopener noreferrer" class="text-muted-foreground transition-colors hover:text-primary" title="Twitter"><svg class="h-5 w-5" aria-hidden="true"><use href="/icons/simple-icons.svg#x"></use></svg></a>{% endif %}
{% if author.linkedin_url %}<a href="{{ author.linkedin_url }}" target="_blank" rel="noopener noreferrer" class="text-muted-foreground transition-colors hover:text-primary" title="LinkedIn"><svg class="h-5 w-5" aria-hidden="true"><use href="/icons/simple-icons.svg#linkedin"></use></svg></a>{% endif %}
{% if author.github_url %}<a href="{{ author.github_url }}" target="_blank" rel="noopener noreferrer" class="text-muted-foreground transition-colors hover:text-primary" title="GitHub"><svg class="h-5 w-5" aria-hidden="true"><use href="/icons/simple-icons.svg#github"></use></svg></a>{% endif %}
</div>{% endif %}
</div>
</section>
{% endif %}

View File

@ -1,13 +0,0 @@
{# author-bio (y2k override) — byline card sourced from the `post` provider's author_* fields. #}
{% if post.author_name %}
<aside class="hud-frame rounded-md border border-dashed border-primary/40 bg-card p-6 text-card-foreground neon-edge-magenta{% if class %} {{ class }}{% endif %}">
{% if heading %}<p class="y2k-eyebrow y2k-mono mb-4 text-xs font-semibold uppercase tracking-widest text-accent">{{ heading }}</p>{% endif %}
<div class="flex items-start gap-4">
{% if showAvatar and post.author_avatar %}<div class="h-14 w-14 flex-shrink-0 overflow-hidden rounded-full border border-primary/40 bg-muted">{% img post.author_avatar alt=post.author_name class="h-full w-full object-cover" %}</div>{% endif %}
<div class="min-w-0">
<h3 class="y2k-display text-base font-semibold">{% if post.author_slug %}<a href="/author/{{ post.author_slug }}" class="transition-colors hover:text-primary">{{ post.author_name }}</a>{% else %}{{ post.author_name }}{% endif %}</h3>
{% if post.author_bio %}<p class="y2k-body mt-1 text-sm text-muted-foreground">{{ post.author_bio }}</p>{% endif %}
</div>
</div>
</aside>
{% endif %}

View File

@ -1,24 +0,0 @@
{# blog-hero (y2k override) — blog-index header + featured posts from the `posts` provider. #}
<section class="relative overflow-hidden bg-background py-12 text-foreground md:py-16{% if class %} {{ class }}{% endif %}">
<div class="scanlines pointer-events-none absolute inset-0"></div>
<div class="relative z-10 mx-auto max-w-6xl px-4">
<div class="max-w-2xl">
{% if title %}<h1 class="y2k-display glitch text-4xl font-bold tracking-tight md:text-5xl" data-text="{{ title }}">{{ title }}</h1>{% endif %}
{% if subtitle %}<p class="y2k-body mt-3 text-lg text-muted-foreground">{{ subtitle }}</p>{% endif %}
</div>
{% if posts %}
<div class="mt-10 grid grid-cols-1 gap-6 md:grid-cols-3">
{% for post in posts %}
<article class="hud-frame group flex flex-col rounded-md border border-dashed border-primary/40 bg-card p-4 text-card-foreground neon-edge-magenta{% if forloop.First %} md:col-span-3 md:flex-row md:items-stretch md:gap-6{% endif %}">
{% if post.featured_image_url %}<a href="{{ post.url }}" class="mb-3 block overflow-hidden rounded-md bg-muted{% if forloop.First %} md:mb-0 md:w-1/2{% endif %}">{% img post.featured_image_url alt=post.title class="aspect-video h-full w-full object-cover transition-transform duration-300 group-hover:scale-105" %}</a>{% endif %}
<div class="flex flex-col justify-center{% if forloop.First %} md:w-1/2{% endif %}">
<h2 class="y2k-display {% if forloop.First %}text-2xl{% else %}text-lg{% endif %} font-semibold leading-snug"><a href="{{ post.url }}" class="transition-colors hover:text-primary">{{ post.title }}</a></h2>
{% if showExcerpt and post.excerpt %}<p class="y2k-body mt-2 text-sm text-muted-foreground">{{ post.excerpt }}</p>{% endif %}
<div class="mt-3 flex flex-wrap items-center gap-x-3 y2k-mono text-xs uppercase tracking-wide text-accent">{% if post.author_name %}<span class="font-medium text-foreground">{{ post.author_name }}</span>{% endif %}{% if post.published_at %}<span>{{ post.published_at|date:"M j, Y" }}</span>{% endif %}</div>
</div>
</article>
{% endfor %}
</div>
{% endif %}
</div>
</section>

View File

@ -1,15 +0,0 @@
{# 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" %}
<nav aria-label="Breadcrumb" class="py-3{% if class %} {{ class }}{% endif %}">
<ol class="flex flex-wrap items-center gap-1 y2k-mono text-sm uppercase tracking-wide">
{% if showHome %}<li class="flex items-center"><a href="/" class="text-accent transition-colors hover:text-primary">{{ homeLabel|default:"Home" }}</a></li>{% endif %}
{% for item in breadcrumbs %}
<li class="flex items-center">
{% if showHome or not forloop.First %}{% if arrows %}<svg class="mx-2 h-4 w-4 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>{% else %}<span class="mx-2 text-primary">{{ separator|default:"/" }}</span>{% endif %}{% endif %}
{% if item.is_current %}{% if showCurrent %}<span class="font-medium text-foreground" aria-current="page">{{ item.label }}</span>{% endif %}{% else %}<a href="{{ item.url }}" class="text-accent transition-colors hover:text-primary">{{ item.label }}</a>{% endif %}
</li>
{% endfor %}
</ol>
</nav>

View File

@ -1,2 +1 @@
{# y2k button — keep {% button %} tag intact; y2k chrome on hugging wrapper #}
{% with align=align|default:"left" %}<div class="bn-button flex {% if align == "center" %}justify-center{% elif align == "right" %}justify-end{% else %}justify-start{% endif %}{% if class %} {{ class }}{% endif %}"><span class="y2k-btn y2k-mono inline-flex uppercase tracking-wider font-semibold clip-cut rgb-split">{% 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 %}</span></div>{% endwith %}
<a href="{{ url|default:"#" }}" class="y2k-button {{ class }}" data-variant="{{ variant|default:"primary" }}">{{ text|default:"button" }}</a>

View File

@ -1,2 +0,0 @@
{# y2k card — dashed neon panel; fields layout/link/media/title/text/linkLabel #}
{% with layout=layout|default:"vertical" %}<article class="bn-card y2k-card overflow-hidden rounded-md border border-dashed border-primary/40 bg-card text-card-foreground neon-edge-magenta {% if layout == "horizontal" %}flex flex-col sm:flex-row{% endif %}{% if class %} {{ class }}{% endif %}">{% if link %}<a href="{{ link }}" class="contents">{% endif %}{% if media %}<div class="overflow-hidden bg-muted {% if layout == "horizontal" %}sm:w-2/5 sm:shrink-0{% endif %}">{% if layout == "horizontal" %}{% img media alt=title|default:"" class="h-full w-full object-cover" %}{% else %}{% img media alt=title|default:"" class="aspect-video h-full w-full object-cover" %}{% endif %}</div>{% endif %}<div class="flex flex-col gap-2 p-5 md:p-6 {% if layout == "horizontal" %}justify-center{% endif %}">{% if title %}<h3 class="text-lg font-semibold tracking-tight text-foreground y2k-display">{{ title }}</h3>{% endif %}{% if text %}<div class="text-sm leading-relaxed text-muted-foreground y2k-body">{{ text|safe }}</div>{% endif %}{% if linkLabel and link %}<span class="mt-2 inline-flex items-center gap-1 text-sm font-medium text-primary y2k-mono uppercase tracking-wide">{{ linkLabel }}::lucide:arrow-right:sm::</span>{% endif %}</div>{% if link %}</a>{% endif %}</article>{% endwith %}

View File

@ -1,18 +0,0 @@
{# category-list (y2k override) — from the `categories` provider. list or pills. #}
{% set pills = style == "pills" %}
<section class="bg-background text-foreground{% if class %} {{ class }}{% endif %}">
{% if heading %}<h2 class="y2k-eyebrow y2k-mono mb-4 text-xs font-semibold uppercase tracking-widest text-accent">{{ heading }}</h2>{% endif %}
{% if pills %}
<div class="flex flex-wrap gap-2">
{% for cat in categories %}
<a href="{{ cat.url }}" class="clip-cut inline-flex items-center gap-1.5 border border-primary/50 bg-card px-3 py-1 y2k-mono text-sm uppercase tracking-wide text-card-foreground neon-edge-magenta transition-colors hover:text-primary">{{ cat.name }}{% if showCount %}<span class="text-xs text-muted-foreground">{{ cat.post_count }}</span>{% endif %}</a>
{% endfor %}
</div>
{% else %}
<ul class="space-y-1">
{% for cat in categories %}
<li><a href="{{ cat.url }}" class="flex items-center justify-between rounded-md px-3 py-2 y2k-mono text-sm uppercase tracking-wide text-muted-foreground transition-colors hover:bg-accent hover:text-primary"><span>{{ cat.name }}</span>{% if showCount %}<span class="rounded-full bg-muted px-2 py-0.5 text-xs text-muted-foreground">{{ cat.post_count }}</span>{% endif %}</a></li>
{% endfor %}
</ul>
{% endif %}
</section>

View File

@ -1,18 +0,0 @@
{# contact-card y2k override: neon channel panels. All field reads + control flow preserved. #}
<section class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-4xl px-4">
{% if heading %}<h2 class="y2k-display glitch text-3xl font-semibold tracking-tight md:text-4xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
{% if description %}<p class="mt-3 max-w-2xl text-lg text-muted-foreground">{{ description }}</p>{% endif %}
<div class="mt-10 grid grid-cols-1 gap-4 {% if columns == 3 %}sm:grid-cols-2 lg:grid-cols-3{% elif columns == 1 %}{% else %}sm:grid-cols-2{% endif %}">
{% for channel in channels %}
<div class="hud-frame flex items-start gap-4 rounded-md border border-primary/40 bg-card p-5 text-card-foreground neon-edge-magenta">
{% if channel.icon %}<span class="clip-cut inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary"><svg class="h-5 w-5" aria-hidden="true"><use href="/icons/{{ channel.icon|split:":"|first }}.svg#{{ channel.icon|split:":"|last }}"></use></svg></span>{% endif %}
<div class="min-w-0">
{% if channel.label %}<div class="y2k-mono text-sm font-medium uppercase tracking-wide text-accent">{{ channel.label }}</div>{% endif %}
{% if channel.value %}<div class="mt-0.5 break-words font-medium">{% if channel.link %}<a href="{{ channel.link }}" class="transition-colors hover:text-primary">{{ channel.value }}</a>{% else %}{{ channel.value }}{% endif %}</div>{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
</section>

View File

@ -1,40 +0,0 @@
{# 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 %}
<section id="contact-form-{{ bid }}" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-2xl px-4">
{% if form.submitted %}
<div class="hud-frame rounded-md border border-primary/50 bg-card p-8 text-center text-card-foreground neon-edge-magenta">
<div class="clip-cut mx-auto mb-4 inline-flex h-12 w-12 items-center justify-center rounded-md bg-primary/10 text-primary"><svg class="h-6 w-6" aria-hidden="true"><use href="/icons/lucide.svg#check"></use></svg></div>
<p class="y2k-display text-lg font-medium">{{ form.message|default:successMessage|default:"Thanks — your message has been sent." }}</p>
</div>
{% else %}
{% if heading %}<h2 class="y2k-display glitch text-3xl font-semibold tracking-tight md:text-4xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
{% if description %}<p class="mt-3 text-lg text-muted-foreground">{{ description }}</p>{% endif %}
{% if form.errors._form %}<div class="mt-6 rounded-md border border-destructive/50 bg-destructive/10 px-4 py-3 text-sm text-destructive">{{ form.errors._form }}</div>{% 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 %}
<div class="{% if field.width == "half" %}sm:col-span-1{% else %}sm:col-span-2{% endif %}">
{% if field.label %}<label for="cf-{{ bid }}-{{ field.name }}" class="y2k-mono mb-1.5 block text-sm font-medium uppercase tracking-wide text-accent">{{ field.label }}{% if field.required %} <span class="text-destructive">*</span>{% endif %}</label>{% endif %}
{% if field.type == "textarea" %}
<textarea id="cf-{{ bid }}-{{ field.name }}" name="{{ field.name }}" rows="5"{% if field.required %} required{% endif %}{% if field.placeholder %} placeholder="{{ field.placeholder }}"{% endif %} class="w-full rounded-md border border-primary/40 bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"></textarea>
{% elif field.type == "select" %}
<select id="cf-{{ bid }}-{{ field.name }}" name="{{ field.name }}"{% if field.required %} required{% endif %} class="w-full rounded-md border border-primary/40 bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring">
{% if field.placeholder %}<option value="">{{ field.placeholder }}</option>{% endif %}
{% for opt in field.options %}<option value="{{ opt.value }}">{{ opt.label|default:opt.value }}</option>{% endfor %}
</select>
{% else %}
<input id="cf-{{ bid }}-{{ field.name }}" name="{{ field.name }}" type="{{ field.type|default:"text" }}"{% if field.required %} required{% endif %}{% if field.placeholder %} placeholder="{{ field.placeholder }}"{% endif %} class="w-full rounded-md border border-primary/40 bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring">
{% endif %}
</div>
{% endfor %}
<div class="hidden" aria-hidden="true"><label>Leave this field empty<input type="text" name="website" tabindex="-1" autocomplete="off"></label></div>
<div class="sm:col-span-2">
<button type="submit" class="y2k-mono clip-cut rgb-split inline-flex items-center justify-center rounded-md bg-primary px-6 py-2.5 text-sm font-medium uppercase tracking-wider text-primary-foreground transition-colors hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2">{{ submitLabel|default:"Send message" }}</button>
</div>
{% endform %}
{% endwith %}
{% endif %}
</div>
</section>
{% endwith %}

View File

@ -1,50 +0,0 @@
{# countdown y2k override: neon HUD digit boxes, glitch heading. Timer script + data attributes preserved byte-for-byte. #}
<section class="relative overflow-hidden py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div aria-hidden="true" class="scanlines pointer-events-none absolute inset-0"></div>
<div class="relative z-10 mx-auto max-w-4xl px-4 text-center">
{% if heading %}<h2 class="y2k-display glitch text-3xl font-semibold tracking-tight md:text-4xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-lg text-muted-foreground">{{ intro }}</p>{% endif %}
<div class="mt-10" data-countdown>
<div class="flex items-start justify-center gap-3 sm:gap-6" data-countdown-timer data-target="{{ target }}" role="timer" aria-label="Time remaining">
<div class="flex flex-col items-center {% if variant == "boxes" %}clip-cut min-w-[4.5rem] rounded-md border border-primary/50 bg-card px-3 py-4 text-card-foreground neon-edge-magenta{% endif %}"><span class="y2k-mono text-4xl font-bold tabular-nums sm:text-5xl text-primary" data-unit="days">--</span><span class="y2k-mono mt-1 text-xs font-medium uppercase tracking-widest text-muted-foreground">{{ labelDays|default:"Days" }}</span></div>
<div class="flex flex-col items-center {% if variant == "boxes" %}clip-cut min-w-[4.5rem] rounded-md border border-primary/50 bg-card px-3 py-4 text-card-foreground neon-edge-magenta{% endif %}"><span class="y2k-mono text-4xl font-bold tabular-nums sm:text-5xl text-primary" data-unit="hours">--</span><span class="y2k-mono mt-1 text-xs font-medium uppercase tracking-widest text-muted-foreground">{{ labelHours|default:"Hours" }}</span></div>
<div class="flex flex-col items-center {% if variant == "boxes" %}clip-cut min-w-[4.5rem] rounded-md border border-primary/50 bg-card px-3 py-4 text-card-foreground neon-edge-magenta{% endif %}"><span class="y2k-mono text-4xl font-bold tabular-nums sm:text-5xl text-primary" data-unit="minutes">--</span><span class="y2k-mono mt-1 text-xs font-medium uppercase tracking-widest text-muted-foreground">{{ labelMinutes|default:"Minutes" }}</span></div>
<div class="flex flex-col items-center {% if variant == "boxes" %}clip-cut min-w-[4.5rem] rounded-md border border-primary/50 bg-card px-3 py-4 text-card-foreground neon-edge-magenta{% endif %}"><span class="y2k-mono text-4xl font-bold tabular-nums sm:text-5xl text-primary" data-unit="seconds">--</span><span class="y2k-mono mt-1 text-xs font-medium uppercase tracking-widest text-muted-foreground">{{ labelSeconds|default:"Seconds" }}</span></div>
</div>
<div class="mt-8 hidden" data-countdown-expired>
<p class="y2k-display text-xl font-medium">{{ expiredMessage|default:"This has started." }}</p>
{% if expiredUrl and expiredLabel %}<a href="{{ expiredUrl }}" class="y2k-mono clip-cut rgb-split mt-4 inline-flex items-center gap-2 rounded-md bg-primary px-5 py-3 font-semibold uppercase tracking-wider text-primary-foreground transition-opacity hover:opacity-90">{{ expiredLabel }}<svg class="h-4 w-4" aria-hidden="true"><use href="/icons/lucide.svg#arrow-right"></use></svg></a>{% endif %}
</div>
</div>
<noscript><p class="mt-6 text-muted-foreground">Counting down to <time datetime="{{ target }}">{{ target }}</time>.</p></noscript>
</div>
</section>
<script>
(function(){
var timers = document.querySelectorAll('[data-countdown-timer]');
timers.forEach(function(el){
var target = new Date(el.getAttribute('data-target')).getTime();
if (isNaN(target)) return;
var wrap = el.closest('[data-countdown]');
var expired = wrap ? wrap.querySelector('[data-countdown-expired]') : null;
function pad(n){ return (n < 10 ? '0' : '') + n; }
function set(unit, value){ var node = el.querySelector('[data-unit="' + unit + '"]'); if (node) node.textContent = value; }
var interval;
function tick(){
var diff = target - Date.now();
if (diff <= 0){
el.style.display = 'none';
if (expired) expired.classList.remove('hidden');
if (interval) clearInterval(interval);
return;
}
set('days', Math.floor(diff / 86400000));
set('hours', pad(Math.floor(diff % 86400000 / 3600000)));
set('minutes', pad(Math.floor(diff % 3600000 / 60000)));
set('seconds', pad(Math.floor(diff % 60000 / 1000)));
}
tick();
interval = setInterval(tick, 1000);
});
})();
</script>

View File

@ -1,16 +0,0 @@
{# cta y2k override: scanline overlay, glitch heading, clip-cut rgb-split CTAs. All field reads preserved. #}
<section class="relative overflow-hidden py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div aria-hidden="true" class="scanlines pointer-events-none absolute inset-0"></div>
<div class="relative z-10 mx-auto max-w-6xl px-4">
<div class="hud-frame {% if background == "band" %}clip-cut rounded-md bg-primary px-6 py-14 text-center text-primary-foreground md:px-12{% elif background == "card" %}rounded-md border border-primary/50 bg-card px-6 py-14 text-center text-card-foreground neon-edge-magenta md:px-12{% else %}flex flex-col items-center gap-8 rounded-md border border-primary/30 bg-muted px-6 py-12 text-foreground md:flex-row md:justify-between md:px-12 md:text-left{% endif %}">
<div class="{% if background != "split" %}mx-auto max-w-2xl{% endif %}">
{% if heading %}<h2 class="y2k-display glitch text-3xl font-semibold tracking-tight md:text-4xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
{% if text %}<p class="mt-3 text-lg {% if background == "band" %}text-primary-foreground/80{% else %}text-muted-foreground{% endif %}">{{ text }}</p>{% endif %}
</div>
<div class="flex flex-wrap items-center justify-center gap-3 {% if background != "split" %}mt-8{% else %}shrink-0{% endif %}">
{% if primaryUrl and primaryLabel %}<a href="{{ primaryUrl }}" class="y2k-mono clip-cut rgb-split inline-flex items-center gap-2 rounded-md px-6 py-3 font-semibold uppercase tracking-wider transition-opacity hover:opacity-90 {% if background == "band" %}bg-background text-foreground{% else %}bg-primary text-primary-foreground{% endif %}">{{ primaryLabel }}<svg class="h-4 w-4" aria-hidden="true"><use href="/icons/lucide.svg#arrow-right"></use></svg></a>{% endif %}
{% if secondaryUrl and secondaryLabel %}<a href="{{ secondaryUrl }}" class="y2k-mono inline-flex items-center rounded-md border px-6 py-3 font-semibold uppercase tracking-wider transition-colors {% if background == "band" %}border-primary-foreground/40 text-primary-foreground hover:bg-primary-foreground/10{% else %}border-accent/60 text-accent hover:bg-accent/10{% endif %}">{{ secondaryLabel }}</a>{% endif %}
</div>
</div>
</div>
</section>

View File

@ -1,2 +0,0 @@
{# y2k divider — neon gradient rule / primary-tinted variants #}
{% with variant=variant|default:"line" weight=weight|default:"thin" width=width|default:"full" %}{% if variant == "ornament" %}<div class="bn-divider mx-auto my-8 flex items-center gap-4 {% if width == "narrow" %}max-w-[200px]{% elif width == "wide" %}max-w-3xl{% else %}w-full{% endif %}{% if class %} {{ class }}{% endif %}" role="separator"><span class="h-px flex-1 bg-gradient-to-r from-transparent via-primary to-transparent"></span><span class="shrink-0 text-primary">::{{ icon|default:"lucide:asterisk" }}:md text-primary::</span><span class="h-px flex-1 bg-gradient-to-l from-transparent via-primary to-transparent"></span></div>{% elif variant == "spacer-rule" %}<div class="bn-divider mx-auto py-10 {% if width == "narrow" %}max-w-[200px]{% elif width == "wide" %}max-w-3xl{% else %}w-full{% endif %}{% if class %} {{ class }}{% endif %}" role="separator"><hr class="mx-auto w-16 border-0 border-t {% if weight == "thick" %}border-t-4{% elif weight == "medium" %}border-t-2{% endif %} border-primary/60"></div>{% else %}<hr class="bn-divider mx-auto my-8 border-0 border-t {% if weight == "thick" %}border-t-4{% elif weight == "medium" %}border-t-2{% endif %} border-primary/60 {% if width == "narrow" %}max-w-[200px]{% elif width == "wide" %}max-w-3xl{% else %}w-full{% endif %}{% if class %} {{ class }}{% endif %}" role="separator">{% endif %}{% endwith %}

View File

@ -1,41 +0,0 @@
{# event-list y2k override: neon cards, mono datetime. All field reads + control flow preserved. #}
<section class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-6xl px-4">
{% if heading %}<h2 class="y2k-display glitch text-3xl font-semibold tracking-tight md:text-4xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mt-3 max-w-2xl text-lg text-muted-foreground">{{ intro }}</p>{% endif %}
{% if layout == "cards" %}
<div class="mt-10 grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{% for event in events %}{% if not (hidePast and event.past) %}
<article class="hud-frame flex flex-col overflow-hidden rounded-md border border-primary/40 bg-card text-card-foreground neon-edge-magenta{% if event.past %} opacity-60{% endif %}">
{% if event.media %}<div class="aspect-video overflow-hidden bg-muted">{% img event.media alt=event.title class="h-full w-full object-cover" %}</div>{% endif %}
<div class="flex flex-1 flex-col gap-2 p-5">
<div class="y2k-mono text-sm font-medium uppercase tracking-wide text-primary"><time{% if event.datetime %} datetime="{{ event.datetime }}"{% endif %}>{{ event.start }}{% if event.end %} &ndash; {{ event.end }}{% endif %}</time>{% if event.timezone %} <span class="text-muted-foreground">{{ event.timezone }}</span>{% endif %}</div>
<h3 class="y2k-display text-lg font-semibold">{{ event.title }}</h3>
{% if event.venue %}<p class="text-sm text-muted-foreground">{{ event.venue }}</p>{% endif %}
{% if event.description %}<p class="text-sm text-muted-foreground">{{ event.description }}</p>{% endif %}
{% if event.link %}<a href="{{ event.link }}" class="y2k-mono mt-auto inline-flex items-center gap-1 pt-2 text-sm font-medium uppercase tracking-wide text-primary hover:underline">{{ event.linkLabel|default:"Details" }}<svg class="h-4 w-4" aria-hidden="true"><use href="/icons/lucide.svg#arrow-right"></use></svg></a>{% endif %}
</div>
</article>
{% endif %}{% endfor %}
</div>
{% else %}
<ul class="mt-10 divide-y divide-primary/30 border-y border-primary/30">
{% for event in events %}{% if not (hidePast and event.past) %}
<li class="flex flex-col gap-3 py-5 sm:flex-row sm:items-center sm:gap-6{% if event.past %} opacity-60{% endif %}">
<div class="shrink-0 sm:w-52">
<div class="y2k-mono text-sm font-semibold text-primary"><time{% if event.datetime %} datetime="{{ event.datetime }}"{% endif %}>{{ event.start }}</time></div>
{% if event.end %}<div class="y2k-mono text-xs text-muted-foreground">to {{ event.end }}</div>{% endif %}
{% if event.timezone %}<div class="y2k-mono text-xs text-muted-foreground">{{ event.timezone }}</div>{% endif %}
</div>
<div class="min-w-0 flex-1">
<h3 class="y2k-display text-base font-semibold">{{ event.title }}</h3>
{% if event.venue %}<p class="text-sm text-muted-foreground">{{ event.venue }}</p>{% endif %}
{% if event.description %}<p class="mt-1 text-sm text-muted-foreground">{{ event.description }}</p>{% endif %}
</div>
{% if event.link %}<a href="{{ event.link }}" class="y2k-mono clip-cut inline-flex shrink-0 items-center rounded-md border border-primary/50 px-3 py-1.5 text-sm font-medium uppercase tracking-wide text-primary hover:bg-primary/10">{{ event.linkLabel|default:"Details" }}</a>{% endif %}
</li>
{% endif %}{% endfor %}
</ul>
{% endif %}
</div>
</section>

View File

@ -1,18 +0,0 @@
{# faq y2k override: glitch heading, neon-tinted dividers, display questions. Chevron svg, {% for %}, and answer prose preserved. #}
<section class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-3xl px-4">
{% if heading %}<h2 class="y2k-display glitch text-center text-3xl font-semibold tracking-tight md:text-4xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground">{{ intro }}</p>{% endif %}
<div class="mt-10 divide-y divide-primary/20 border-y border-primary/30">
{% for item in items %}
<details class="group py-2"{% if variant == "open-list" %} open{% endif %}>
<summary class="flex cursor-pointer list-none items-center justify-between gap-4 py-3 text-lg font-medium marker:content-none [&::-webkit-details-marker]:hidden">
<span class="y2k-display">{{ item.question }}</span>
<svg class="h-5 w-5 shrink-0 text-primary transition-transform duration-200 group-open:rotate-180" aria-hidden="true"><use href="/icons/lucide.svg#chevron-down"></use></svg>
</summary>
<div class="pb-4 text-muted-foreground [&_a]:text-primary [&_a]:underline">{{ item.answer|safe }}</div>
</details>
{% endfor %}
</div>
</div>
</section>

View File

@ -1,19 +0,0 @@
{# feature_grid y2k override: scanline hero overlay, mono eyebrow, glitch heading, neon HUD cards. All field reads + control flow preserved. #}
<section class="relative overflow-hidden py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div aria-hidden="true" class="scanlines pointer-events-none absolute inset-0"></div>
<div class="relative z-10 mx-auto max-w-6xl px-4">
{% if eyebrow %}<p class="y2k-mono text-center text-xs font-semibold uppercase tracking-widest text-accent">{{ eyebrow }}</p>{% endif %}
{% if heading %}<h2 class="y2k-display glitch mt-2 text-center text-3xl font-semibold tracking-tight md:text-4xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground">{{ intro }}</p>{% endif %}
<div class="mt-12 grid grid-cols-1 gap-6 sm:grid-cols-2 {% if columns == 2 %}lg:grid-cols-2{% elif columns == 4 %}lg:grid-cols-4{% else %}lg:grid-cols-3{% endif %}">
{% for item in items %}
<div class="{% if layout == "card" %}hud-frame rounded-md border border-primary/40 bg-card p-6 text-card-foreground neon-edge-magenta transition-transform hover:-translate-y-0.5{% elif layout == "centered" %}text-center{% endif %}">
{% if item.icon %}<div class="clip-cut mb-4 inline-flex h-11 w-11 items-center justify-center rounded-md bg-primary/10 text-primary{% if layout == "centered" %} mx-auto{% endif %}"><svg class="h-6 w-6" aria-hidden="true"><use href="/icons/{{ item.icon|split:":"|first }}.svg#{{ item.icon|split:":"|last }}"></use></svg></div>{% endif %}
<h3 class="y2k-display text-lg font-semibold">{% if item.linkUrl %}<a href="{{ item.linkUrl }}" class="transition-colors hover:text-primary">{{ item.title }}</a>{% else %}{{ item.title }}{% endif %}</h3>
{% if item.text %}<p class="mt-2 text-muted-foreground">{{ item.text }}</p>{% endif %}
{% if item.linkUrl and item.linkLabel %}<a href="{{ item.linkUrl }}" class="y2k-mono uppercase tracking-wide mt-3 inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline">{{ item.linkLabel }}<svg class="h-4 w-4" aria-hidden="true"><use href="/icons/lucide.svg#arrow-right"></use></svg></a>{% endif %}
</div>
{% endfor %}
</div>
</div>
</section>

View File

@ -1,23 +0,0 @@
{# featured-posts (y2k override) — grid of cards from the `posts` provider #}
{# (params: limit, featured=true). Neon HUD card: image, title, excerpt, meta. #}
<section class="py-12 md:py-16 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-6xl px-4">
{% if heading %}<h2 class="y2k-display glitch mb-8 text-2xl font-semibold tracking-tight md:text-3xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 {% if columns == 2 %}lg:grid-cols-2{% elif columns == 4 %}lg:grid-cols-4{% else %}lg:grid-cols-3{% endif %}">
{% for post in posts %}
<article class="hud-frame group flex flex-col overflow-hidden rounded-md border border-dashed border-primary/40 bg-card text-card-foreground neon-edge-magenta transition-transform hover:-translate-y-0.5">
{% if showFeaturedImage and post.featured_image_url %}<a href="{{ post.url }}" class="block aspect-video overflow-hidden bg-muted">{% img post.featured_image_url alt=post.title class="h-full w-full object-cover transition-transform duration-300 group-hover:scale-105" %}</a>{% endif %}
<div class="flex flex-1 flex-col p-5">
<h3 class="y2k-display text-lg font-semibold leading-snug"><a href="{{ post.url }}" class="transition-colors hover:text-primary">{{ post.title }}</a></h3>
{% if showExcerpt and post.excerpt %}<p class="y2k-body mt-2 flex-1 text-sm text-muted-foreground">{{ post.excerpt }}</p>{% endif %}
<div class="mt-4 flex flex-wrap items-center gap-x-3 gap-y-1 y2k-mono text-xs uppercase tracking-wide text-accent">
{% if showAuthor and post.author_name %}<span class="font-medium text-foreground">{{ post.author_name }}</span>{% endif %}
{% if showDate and post.published_at %}<span>{{ post.published_at|date:"M j, Y" }}</span>{% endif %}
{% if post.reading_time %}<span>{{ post.reading_time }} min read</span>{% endif %}
</div>
</div>
</article>
{% endfor %}
</div>
</div>
</section>

View File

@ -1,39 +0,0 @@
{# 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 %}<div class="flex items-center gap-3 {{ cls }}">{% for s in social_items %}<a href="{{ s.url }}" target="_blank" rel="noopener" title="{{ s.label }}" class="text-muted-foreground hover:text-primary transition-colors"><span class="sr-only">{{ s.label }}</span>{% if s.icon %}::{{ s.icon }}::{% else %}{{ s.label }}{% endif %}</a>{% endfor %}</div>{% endif %}{% endmacro %}
{% macro newsletter() %}{% if showNewsletter %}<div class="max-w-sm"><h3 class="y2k-mono uppercase tracking-widest text-xs font-semibold text-accent">{{ newsletterTitle|default:"Stay in the loop" }}</h3>{% if newsletterDescription %}<p class="mt-1 text-sm text-muted-foreground">{{ newsletterDescription }}</p>{% endif %}<div class="mt-3">{% signup_form list=newsletterListSlug|default:"main" source="footer" submit=newsletterButtonText|default:"Subscribe" %}</div></div>{% endif %}{% endmacro %}
{% macro legalbar() %}<div class="mt-10 pt-6 border-t border-primary/30 flex flex-col sm:flex-row items-center justify-between gap-3 text-sm text-muted-foreground"><p class="y2k-mono">{% if copyright %}&copy; {{ copyright }}{% else %}&copy; {{ companyName }}{% endif %}</p>{% if legal_items %}<nav class="flex flex-wrap items-center gap-x-4 gap-y-1">{% for l in legal_items %}<a href="{{ l.url|default:'#' }}"{% if l.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="hover:text-primary transition-colors">{{ l.label }}</a>{% endfor %}</nav>{% endif %}</div>{% endmacro %}
<footer class="w-full border-t border-primary/30 {{ fbg }} {{ class }}">
<div class="{% if fullWidth %}w-full px-4 sm:px-6 lg:px-8{% else %}max-w-7xl mx-auto px-4 sm:px-6 lg:px-8{% endif %} py-16">
{% if fl == "minimal" %}
<div class="flex flex-col sm:flex-row items-center justify-between gap-6">
<div class="flex items-center gap-3">{% if logo %}<img src="{{ logo }}" alt="{{ companyName }}" class="h-8 w-auto"/>{% elif companyName %}<span class="y2k-display text-lg font-bold text-foreground">{{ companyName }}</span>{% endif %}{% if tagline %}<span class="text-sm text-muted-foreground hidden sm:inline">{{ tagline }}</span>{% endif %}</div>
{{ sociallinks("") }}
</div>
{{ legalbar() }}
{% elif fl == "centered" %}
<div class="flex flex-col items-center text-center gap-5">
{% if logo %}<img src="{{ logo }}" alt="{{ companyName }}" class="h-10 w-auto"/>{% elif companyName %}<span class="y2k-display text-xl font-bold text-foreground">{{ companyName }}</span>{% endif %}
{% if tagline %}<p class="max-w-md text-sm text-muted-foreground">{{ tagline }}</p>{% endif %}
{% if columns %}<nav class="flex flex-wrap items-center justify-center gap-x-6 gap-y-2">{% for col in columns %}{% for item in col.items %}<a href="{{ item.url|default:'#' }}"{% if item.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="y2k-mono uppercase tracking-wide text-xs text-muted-foreground hover:text-primary transition-colors">{{ item.label }}</a>{% endfor %}{% endfor %}</nav>{% endif %}
{{ newsletter() }}
{{ sociallinks("justify-center") }}
</div>
{{ legalbar() }}
{% else %}
<div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-8">
<div class="col-span-2 lg:col-span-2">
{% if logo %}<img src="{{ logo }}" alt="{{ companyName }}" class="h-9 w-auto"/>{% elif companyName %}<span class="y2k-display text-xl font-bold text-foreground">{{ companyName }}</span>{% endif %}
{% if tagline %}<p class="mt-3 max-w-xs text-sm text-muted-foreground">{{ tagline }}</p>{% endif %}
{% if address or email or phone %}<address class="mt-4 not-italic text-sm text-muted-foreground space-y-1">{% if address %}<div>{{ address }}</div>{% endif %}{% if email %}<div><a href="mailto:{{ email }}" class="hover:text-primary transition-colors">{{ email }}</a></div>{% endif %}{% if phone %}<div><a href="tel:{{ phone }}" class="hover:text-primary transition-colors">{{ phone }}</a></div>{% endif %}</address>{% endif %}
{{ sociallinks("mt-5") }}
</div>
{% for col in columns %}<div><h3 class="y2k-mono uppercase tracking-widest text-xs font-semibold text-accent">{{ col.header }}</h3><ul class="mt-3 space-y-2">{% for item in col.items %}<li><a href="{{ item.url|default:'#' }}"{% if item.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="text-sm text-muted-foreground hover:text-primary transition-colors">{{ item.label }}</a></li>{% endfor %}</ul></div>{% endfor %}
{% if showNewsletter %}<div class="col-span-2 lg:col-span-1">{{ newsletter() }}</div>{% endif %}
</div>
{{ legalbar() }}
{% endif %}
</div>
</footer>

View File

@ -1,52 +0,0 @@
{# 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 %}<div class="bn-gallery my-6{% if class %} {{ class }}{% endif %}" data-bn-gallery data-lightbox="{% if lightbox %}true{% else %}false{% endif %}"><div class="{% if layout == "carousel" %}flex snap-x snap-mandatory overflow-x-auto pb-2 {% if gap == "sm" %}gap-2{% elif gap == "lg" %}gap-6{% else %}gap-4{% endif %}{% elif layout == "masonry" %}columns-2 {% if cols == 4 %}md:columns-4{% elif cols == 2 %}md:columns-2{% else %}md:columns-3{% endif %} {% if gap == "sm" %}gap-2{% elif gap == "lg" %}gap-6{% else %}gap-4{% endif %}{% else %}grid grid-cols-2 {% if cols == 4 %}md:grid-cols-4{% elif cols == 2 %}md:grid-cols-2{% else %}md:grid-cols-3{% endif %} {% if gap == "sm" %}gap-2{% elif gap == "lg" %}gap-6{% else %}gap-4{% endif %}{% endif %}">{% for image in images %}<a href="{{ image.src|mediaURL }}" data-bn-gallery-item data-caption="{{ image.caption|default:"" }}" class="group relative block overflow-hidden rounded-md border border-primary/30 bg-muted neon-edge-magenta focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 {% if layout == "carousel" %}w-64 shrink-0 snap-start sm:w-80{% elif layout == "masonry" %}mb-4 w-full break-inside-avoid{% endif %}">{% if layout == "grid" %}{% img image.src alt=image.alt|default:"" class="aspect-square w-full object-cover" %}{% else %}{% img image.src alt=image.alt|default:"" class="h-auto w-full" %}{% endif %}{% if image.caption %}<span class="sr-only">{{ image.caption }}</span>{% endif %}</a>{% endfor %}</div></div>{% if lightbox %}<script>
(function(){
if(window.__bnLightbox)return;window.__bnLightbox=true;
var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var ov,imgEl,capEl,items=[],idx=0,lastFocus=null;
function build(){
ov=document.createElement('div');
ov.setAttribute('role','dialog');ov.setAttribute('aria-modal','true');ov.setAttribute('aria-label','Image viewer');ov.tabIndex=-1;
ov.style.cssText='position:fixed;inset:0;z-index:9999;display:none;align-items:center;justify-content:center;background:rgba(0,0,0,0.9);padding:2rem;';
var btn='position:absolute;width:2.75rem;height:2.75rem;display:inline-flex;align-items:center;justify-content:center;border-radius:9999px;border:none;background:hsl(var(--background));color:hsl(var(--foreground));cursor:pointer;';
ov.innerHTML=
'<button type="button" data-lb-close aria-label="Close" style="'+btn+'top:1rem;right:1rem;"><svg style="width:1.25rem;height:1.25rem" aria-hidden="true"><use href="/icons/lucide.svg#x"/></svg></button>'+
'<button type="button" data-lb-prev aria-label="Previous image" style="'+btn+'left:1rem;top:50%;transform:translateY(-50%);"><svg style="width:1.25rem;height:1.25rem" aria-hidden="true"><use href="/icons/lucide.svg#chevron-left"/></svg></button>'+
'<figure style="margin:0;max-width:92vw;max-height:92vh;display:flex;flex-direction:column;align-items:center;gap:0.75rem;"><img data-lb-img alt="" style="max-width:92vw;max-height:82vh;object-fit:contain;border-radius:0.5rem;'+(reduce?'':'transition:opacity .2s ease;')+'"><figcaption data-lb-cap style="color:hsl(var(--background));opacity:.85;font-size:.875rem;text-align:center;max-width:60ch;"></figcaption></figure>'+
'<button type="button" data-lb-next aria-label="Next image" style="'+btn+'right:1rem;top:50%;transform:translateY(-50%);"><svg style="width:1.25rem;height:1.25rem" aria-hidden="true"><use href="/icons/lucide.svg#chevron-right"/></svg></button>';
document.body.appendChild(ov);
imgEl=ov.querySelector('[data-lb-img]');capEl=ov.querySelector('[data-lb-cap]');
ov.addEventListener('click',function(e){if(e.target===ov||e.target.closest('[data-lb-close]'))close();});
ov.querySelector('[data-lb-prev]').addEventListener('click',function(e){e.stopPropagation();show(idx-1);});
ov.querySelector('[data-lb-next]').addEventListener('click',function(e){e.stopPropagation();show(idx+1);});
}
function show(i){
if(!items.length)return;
idx=(i+items.length)%items.length;
var a=items[idx];var im=a.querySelector('img');
imgEl.src=(im&&(im.currentSrc||im.src))||a.getAttribute('href');
imgEl.alt=im?im.alt:'';
var cap=a.getAttribute('data-caption')||'';capEl.textContent=cap;capEl.style.display=cap?'block':'none';
}
function open(g,link){
if(!ov)build();
items=Array.prototype.slice.call(g.querySelectorAll('[data-bn-gallery-item]'));
lastFocus=document.activeElement;ov.style.display='flex';
var multi=items.length>1;
ov.querySelector('[data-lb-prev]').style.display=multi?'inline-flex':'none';
ov.querySelector('[data-lb-next]').style.display=multi?'inline-flex':'none';
show(items.indexOf(link));ov.focus();
}
function close(){if(ov)ov.style.display='none';if(lastFocus&&lastFocus.focus)lastFocus.focus();}
document.addEventListener('click',function(e){
var link=e.target.closest('[data-bn-gallery][data-lightbox="true"] [data-bn-gallery-item]');
if(!link)return;e.preventDefault();open(link.closest('[data-bn-gallery]'),link);
});
document.addEventListener('keydown',function(e){
if(!ov||ov.style.display==='none')return;
if(e.key==='Escape'){e.preventDefault();close();}
else if(e.key==='ArrowLeft')show(idx-1);
else if(e.key==='ArrowRight')show(idx+1);
});
})();
</script>{% endif %}{% endwith %}

View File

@ -1,7 +1 @@
{# y2k heading — y2k-display everywhere; glitch on h1/h2 only #}
{% if level == 1 %}<h1 class="text-4xl font-bold tracking-tight y2k-display glitch {{ textClass }} flex-1">{{ text }}</h1>
{% elif level == 3 %}<h3 class="text-2xl font-semibold y2k-display {{ textClass }} flex-1">{{ text }}</h3>
{% elif level == 4 %}<h4 class="text-xl font-semibold y2k-display {{ textClass }} flex-1">{{ text }}</h4>
{% elif level == 5 %}<h5 class="text-lg font-medium y2k-display {{ textClass }} flex-1">{{ text }}</h5>
{% elif level == 6 %}<h6 class="text-base font-medium y2k-display {{ textClass }} flex-1">{{ text }}</h6>
{% else %}<h2 class="text-3xl font-semibold tracking-tight y2k-display glitch {{ textClass }} flex-1">{{ text }}</h2>{% endif %}
{% if level|integer == 1 %}<h1 class="y2k-heading font-bold text-foreground text-5xl md:text-6xl {{ textClass }}">{{ text }}</h1>{% elif level|integer == 3 %}<h3 class="y2k-heading font-semibold text-foreground text-2xl {{ textClass }}">{{ text }}</h3>{% elif level|integer == 4 %}<h4 class="y2k-heading font-semibold text-foreground text-xl {{ textClass }}">{{ text }}</h4>{% elif level|integer == 5 %}<h5 class="y2k-heading font-semibold text-foreground text-lg {{ textClass }}">{{ text }}</h5>{% elif level|integer == 6 %}<h6 class="y2k-heading font-semibold text-foreground text-base {{ textClass }}">{{ text }}</h6>{% else %}<h2 class="y2k-heading font-bold text-foreground text-3xl md:text-4xl {{ textClass }}">{{ text }}</h2>{% endif %}

View File

@ -1,107 +0,0 @@
{# 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 %}<span class="y2k-mono clip-cut y2k-bevel inline-block mb-4 px-3 py-1 bg-primary/10 text-primary text-xs font-semibold uppercase tracking-widest">{{ eyebrowText }} <span aria-hidden="true" class="caret-blink">_</span></span>{% endif %}
{% if headlineText %}<h1 class="y2k-display glitch text-4xl sm:text-5xl lg:text-6xl font-bold leading-tight text-foreground" data-text="{{ headlineText }}">{{ headlineText }}</h1>{% endif %}
{% if subText %}<h2 class="mt-4 text-xl sm:text-2xl text-muted-foreground max-w-2xl{% if align == "center" %} mx-auto{% endif %}">{{ subText }}</h2>{% endif %}
{% if descText %}<p class="mt-4 max-w-xl leading-relaxed text-muted-foreground{% if align == "center" %} mx-auto{% endif %}">{{ descText }}</p>{% endif %}
{% if pText or sText %}<div class="mt-8 flex flex-col sm:flex-row gap-4{% if align == "center" %} justify-center{% endif %}">
{% if pText and pUrl %}<a href="{{ pUrl }}" class="y2k-button rgb-split">{{ pText }}</a>{% endif %}
{% if sText and sUrl %}<a href="{{ sUrl }}" class="y2k-mono clip-cut y2k-bevel inline-flex items-center justify-center px-6 py-3 font-semibold uppercase tracking-wider text-accent bg-card transition-colors hover:bg-accent/10">{{ sText }}</a>{% endif %}
</div>{% endif %}
{% if trustLogos %}<div class="mt-10">
<p class="y2k-mono text-xs uppercase tracking-widest text-muted-foreground mb-3">Heard on</p>
<div class="flex flex-wrap items-center gap-6{% if align == "center" %} justify-center{% endif %}">{% for logo in trustLogos %}{% if logo.url %}<a href="{{ logo.url }}" target="_blank" rel="noopener noreferrer" class="opacity-60 transition-opacity hover:opacity-100">{% img logo.image alt=logo.alt class="h-8 w-auto" %}</a>{% else %}{% img logo.image alt=logo.alt class="h-8 w-auto opacity-60" %}{% endif %}{% endfor %}</div>
</div>{% endif %}
{% endmacro %}
{% if variant == "split" %}
<section class="hero grid grid-cols-1 lg:grid-cols-2 overflow-hidden bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="relative min-h-[320px] bg-muted {% if mediaPosition == "left" %}order-1 lg:order-1{% else %}order-1 lg:order-2{% endif %}">{% if hasImage %}{% img backgroundImage alt=headlineText class="absolute inset-0 h-full w-full object-cover" layout="hero" %}{% else %}<div class="y2k-orb-wrap"><div class="y2k-orb-fallback"></div><canvas class="y2k-orb-canvas" aria-hidden="true"></canvas></div>{% endif %}<div aria-hidden="true" class="scanlines pointer-events-none absolute inset-0"></div></div>
<div class="flex flex-col justify-center px-6 py-16 lg:px-12 {% if mediaPosition == "left" %}order-2 lg:order-2{% else %}order-2 lg:order-1{% endif %}"><div class="max-w-lg text-left">{{ herocontent("left") }}</div></div>
</section>
{% else %}
{% set align = "center" %}
{% if variant == "left-aligned" %}{% set align = "left" %}{% endif %}
<section class="hero relative flex flex-col overflow-hidden bg-background text-foreground {% if minHeight == "screen" %}min-h-screen{% elif minHeight == "50vh" %}min-h-[50vh]{% elif minHeight == "auto" %}min-h-[400px]{% else %}min-h-[75vh]{% endif %} {% if verticalAlign == "top" %}justify-start pt-20{% elif verticalAlign == "bottom" %}justify-end pb-20{% else %}justify-center{% endif %}{% if class %} {{ class }}{% endif %}">
{% if hasImage %}<div class="absolute inset-0 z-0">{% img backgroundImage alt=headlineText class="h-full w-full object-cover" layout="hero" %}{% if showOverlay %}<div class="absolute inset-0 {% if overlayColor == "light" %}bg-background/60{% elif overlayColor == "primary" %}bg-primary/50{% elif overlayOpacity <= 30 %}bg-black/30{% elif overlayOpacity <= 50 %}bg-black/50{% elif overlayOpacity <= 70 %}bg-black/70{% else %}bg-black/80{% endif %}"></div>{% endif %}</div>{% else %}<div class="y2k-orb-wrap z-0"><div class="y2k-orb-fallback"></div><canvas class="y2k-orb-canvas" aria-hidden="true"></canvas></div>{% endif %}
<div aria-hidden="true" class="scanlines pointer-events-none absolute inset-0 z-0"></div>
<div class="relative z-10 mx-auto w-full max-w-4xl px-4 sm:px-6 lg:px-8 {% if variant == "left-aligned" %}text-left{% else %}text-center{% endif %}">{{ herocontent(align) }}</div>
</section>
{% endif %}
<script>
(function(){
if(window.__y2kOrbs)return;window.__y2kOrbs=1;
var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;
function initCanvas(cv){
if(cv.__init)return;cv.__init=1;
var ctx=cv.getContext&&cv.getContext('2d');if(!ctx)return;
var wrap=cv.parentElement;
var cs=getComputedStyle(document.documentElement);
function raw(n,f){var v=cs.getPropertyValue(n).trim();return 'hsl('+(v||f)+')';}
var cols=[raw('--mesh-a','320 95% 62%'),raw('--mesh-b','190 90% 60%'),raw('--mesh-c','55 95% 68%')];
var W=0,H=0,dpr=1,blobs=[],N=6;
function seed(){
blobs=[];
for(var i=0;i<N;i++){
blobs.push({
x:Math.random(),y:Math.random(),
r:0.16+Math.random()*0.22,
vx:(Math.random()-0.5)*0.00035,
vy:(Math.random()-0.5)*0.00035,
c:cols[i%cols.length]
});
}
}
function resize(){
dpr=Math.min(window.devicePixelRatio||1,2);
W=wrap.clientWidth;H=wrap.clientHeight;
if(W===0||H===0)return;
cv.width=W*dpr;cv.height=H*dpr;ctx.setTransform(dpr,0,0,dpr,0,0);
}
seed();resize();
var last=0,fps=1000/30;
function frame(t){
requestAnimationFrame(frame);
if(t-last<fps)return;last=t;
if(W===0||H===0){resize();return;}
ctx.clearRect(0,0,W,H);
ctx.globalCompositeOperation='lighter';
var base=Math.min(W,H);
for(var i=0;i<blobs.length;i++){
var b=blobs[i];
b.x+=b.vx;b.y+=b.vy;
if(b.x<0.05||b.x>0.95)b.vx*=-1;
if(b.y<0.05||b.y>0.95)b.vy*=-1;
var cx=b.x*W,cy=b.y*H,rad=b.r*base;
var g=ctx.createRadialGradient(cx,cy,0,cx,cy,rad);
g.addColorStop(0,b.c);
g.addColorStop(1,'transparent');
ctx.fillStyle=g;
ctx.beginPath();ctx.arc(cx,cy,rad,0,Math.PI*2);ctx.fill();
}
ctx.globalCompositeOperation='source-over';
}
if(window.ResizeObserver){var ro=new ResizeObserver(resize);ro.observe(wrap);}else{window.addEventListener('resize',resize);}
requestAnimationFrame(frame);
}
function boot(){
if(reduce)return;
var cvs=document.querySelectorAll('.y2k-orb-canvas');
if(!cvs.length)return;
if(!('IntersectionObserver' in window)){cvs.forEach(initCanvas);return;}
var io=new IntersectionObserver(function(es){es.forEach(function(e){if(e.isIntersecting){initCanvas(e.target);io.unobserve(e.target);}});},{threshold:0.05});
cvs.forEach(function(c){io.observe(c);});
}
if(document.readyState!='loading')boot();else document.addEventListener('DOMContentLoaded',boot);
})();
</script>

View File

@ -1,31 +0,0 @@
{# hours-location y2k override: mono readout table, neon map panel. Map lazy-load button + inline handler preserved. #}
<section class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-5xl px-4">
{% if heading %}<h2 class="y2k-display glitch text-3xl font-semibold tracking-tight md:text-4xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
<div class="mt-10 grid grid-cols-1 gap-10 md:grid-cols-2">
<div>
{% if hours %}
<table class="y2k-mono w-full text-sm">
<tbody class="divide-y divide-primary/30">
{% for row in hours %}
<tr>
<th scope="row" class="py-2 pr-4 text-left font-medium uppercase tracking-wide">{{ row.day }}</th>
<td class="py-2 text-right {% if row.closed %}text-muted-foreground{% else %}text-primary{% endif %} tabular-nums">{% if row.closed %}Closed{% else %}{{ row.hours }}{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% if specialHoursNote %}<p class="mt-4 rounded-md border border-primary/40 bg-muted px-3 py-2 text-sm text-muted-foreground">{{ specialHoursNote }}</p>{% endif %}
{% if address %}<div class="mt-6"><div class="y2k-mono text-sm font-medium uppercase tracking-wide text-accent">Address</div><address class="mt-1 whitespace-pre-line not-italic">{{ address }}</address></div>{% endif %}
{% if phone %}<div class="mt-4"><div class="y2k-mono text-sm font-medium uppercase tracking-wide text-accent">Phone</div><a href="tel:{{ phone }}" class="y2k-mono mt-1 inline-block font-medium transition-colors hover:text-primary">{{ phone }}</a></div>{% endif %}
{% if directionsUrl %}<a href="{{ directionsUrl }}" target="_blank" rel="noopener noreferrer" class="y2k-mono clip-cut mt-6 inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium uppercase tracking-wide text-primary-foreground transition-colors hover:bg-primary/90"><svg class="h-4 w-4" aria-hidden="true"><use href="/icons/lucide.svg#navigation"></use></svg>Get directions</a>{% endif %}
</div>
{% if mapEmbedUrl %}
<div class="hud-frame aspect-square w-full overflow-hidden rounded-md border border-primary/40 bg-muted neon-edge-cyan md:aspect-auto">
<button type="button" data-map-src="{{ mapEmbedUrl }}" onclick="var f=document.createElement('iframe');f.src=this.getAttribute('data-map-src');f.setAttribute('class','h-full w-full border-0');f.setAttribute('loading','lazy');f.setAttribute('referrerpolicy','no-referrer-when-downgrade');f.setAttribute('title','Map');this.parentNode.replaceChild(f,this);" class="y2k-mono flex h-full min-h-[16rem] w-full items-center justify-center gap-2 text-sm font-medium uppercase tracking-wide transition-colors hover:bg-accent/20 hover:text-foreground"><svg class="h-5 w-5" aria-hidden="true"><use href="/icons/lucide.svg#map-pin"></use></svg>{{ mapButtonLabel|default:"Load map" }}</button>
</div>
{% endif %}
</div>
</div>
</section>

View File

@ -1,2 +1 @@
{# 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" %}<figure class="flex-1 {% if class %}{{ class }} {% endif %}{% if align == "left" %}flex justify-start{% elif align == "right" %}flex justify-end{% else %}flex justify-center{% endif %}"><div class="relative overflow-hidden neon-edge-cyan {% if size == "xs" %}max-w-[200px] w-full{% elif size == "sm" %}max-w-[400px] w-full{% elif size == "md" %}max-w-[600px] w-full{% elif size == "lg" %}max-w-[800px] w-full{% else %}w-full{% endif %}{% if aspectRatio == "square" %} aspect-square{% elif aspectRatio == "video" %} aspect-video{% elif aspectRatio == "portrait" %} aspect-[3/4]{% elif aspectRatio == "wide" %} aspect-[21/9]{% endif %}{% if rounded == "sm" %} rounded-sm{% elif rounded == "md" %} rounded-md{% elif rounded == "lg" %} rounded-lg{% elif rounded == "xl" %} rounded-xl{% elif rounded == "full" %} rounded-full{% endif %}{% if shadow == "sm" %} shadow-sm{% elif shadow == "md" %} shadow-md{% elif shadow == "lg" %} shadow-lg{% elif shadow == "xl" %} shadow-xl{% endif %}">{% 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 %}</div>{% if caption %}<figcaption class="mt-2 text-sm text-muted-foreground text-center y2k-mono">{{ caption }}</figcaption>{% endif %}</figure>{% endwith %}
{% if url %}<figure class="my-4 {{ class }}"><img src="{{ url }}" alt="{{ alt }}" class="y2k-image-frame w-full h-auto">{% if caption %}<figcaption class="mt-2 text-sm text-muted-foreground text-center">{{ caption }}</figcaption>{% endif %}</figure>{% endif %}

View File

@ -1,16 +0,0 @@
{# logo_strip y2k override: mono kicker. Marquee classes, {% img %}, and keyframes <style> preserved byte-for-byte. #}
<section class="py-12 md:py-16 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-6xl px-4">
{% if heading %}<p class="y2k-mono text-center text-sm font-semibold uppercase tracking-widest text-accent">{{ heading }}</p>{% endif %}
<div class="mt-8 {% if marquee %}group relative overflow-hidden [mask-image:linear-gradient(to_right,transparent,black_8%,black_92%,transparent)]{% endif %}">
<div class="flex flex-wrap items-center justify-center gap-x-10 gap-y-6 {% if marquee %}flex-nowrap [animation:logo-marquee_30s_linear_infinite] motion-reduce:[animation:none] group-hover:[animation-play-state:paused]{% endif %}">
{% for logo in logos %}
{% if logo.url %}<a href="{{ logo.url }}" class="block shrink-0 opacity-80 transition hover:opacity-100">{% endif %}
<span class="inline-flex h-8 items-center{% if grayscale %} grayscale transition-[filter] hover:grayscale-0{% endif %}">{% img logo.image alt=logo.alt class="h-8 w-auto object-contain" %}</span>
{% if logo.url %}</a>{% endif %}
{% endfor %}
</div>
</div>
</div>
</section>
{% if marquee %}<style>@keyframes logo-marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}</style>{% endif %}

View File

@ -1,30 +0,0 @@
{# menu-list y2k override: mono price readouts, neon section rules. All field reads + control flow preserved. #}
<section class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-5xl px-4">
{% if heading %}<h2 class="y2k-display glitch text-3xl font-semibold tracking-tight md:text-4xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mt-3 max-w-2xl text-lg text-muted-foreground">{{ intro }}</p>{% endif %}
<div class="mt-10 grid grid-cols-1 gap-x-12 gap-y-10 {% if columns == 2 %}md:grid-cols-2{% endif %}">
{% for section in sections %}
<div class="menu-section">
{% if section.title %}<h3 class="y2k-display border-b border-primary/40 pb-2 text-xl font-semibold">{{ section.title }}</h3>{% endif %}
{% if section.note %}<p class="mt-2 text-sm text-muted-foreground">{{ section.note }}</p>{% endif %}
<ul class="mt-4 space-y-5">
{% for item in section.items %}
<li class="flex items-baseline gap-4">
<div class="min-w-0 flex-1">
<div class="flex flex-wrap items-baseline gap-x-2 gap-y-1">
<span class="font-medium">{{ item.name }}</span>
{% for badge in item.badges %}{% if badge.label %}<span class="y2k-mono clip-cut inline-block rounded border border-accent/50 px-2 py-0.5 text-xs uppercase tracking-wide text-accent">{{ badge.label }}</span>{% endif %}{% endfor %}
</div>
{% if item.description %}<p class="mt-0.5 text-sm text-muted-foreground">{{ item.description }}</p>{% endif %}
</div>
{% if item.price %}<span class="y2k-mono shrink-0 font-medium tabular-nums text-primary">{% if currency %}{{ currency }}{% endif %}{{ item.price }}</span>{% endif %}
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
</div>
{% if footnote %}<p class="y2k-mono mt-10 text-xs text-muted-foreground">{{ footnote }}</p>{% endif %}
</div>
</section>

View File

@ -1,104 +0,0 @@
{# 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) %}<a href="{{ item.url|default:'#' }}"{% if item.open_in_new_tab %} target="_blank" rel="noopener"{% endif %}{% if item.title %} title="{{ item.title }}"{% endif %} class="y2k-mono uppercase tracking-wider px-3 py-2 text-xs font-medium text-foreground/80 hover:text-primary transition-colors {{ item.css_class }}">{{ item.label }}</a>{% endmacro %}
{% macro dropdown(item) %}<div class="relative group">
<button type="button" class="y2k-mono uppercase tracking-wider px-3 py-2 text-xs font-medium text-foreground/80 hover:text-primary transition-colors inline-flex items-center gap-1">{{ item.label }}<svg class="h-4 w-4 transition-transform group-hover:rotate-180" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg></button>
{% if use_mega %}<div class="absolute left-1/2 -translate-x-1/2 mt-1 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50"><div class="p-6 bg-card border border-primary/40 neon-edge-magenta rounded-md shadow-xl grid gap-8" style="grid-template-columns:repeat({{ item.children|length|default:1 }},minmax(10rem,1fr));min-width:min(90vw,42rem)">{% for child in item.children %}<div class="space-y-1">{% if child.item_type == "header" %}<div class="y2k-mono px-2 py-1 text-xs font-semibold text-accent uppercase tracking-widest border-b border-primary/40 mb-2">{{ child.label }}</div>{% else %}<a href="{{ child.url|default:'#' }}"{% if child.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="block px-2 py-2 rounded hover:bg-accent transition-colors {{ child.css_class }}">{% if child.icon %}<span class="mr-1.5 text-primary">::{{ child.icon }}::</span>{% endif %}<span class="text-sm font-medium text-foreground">{{ child.label }}</span>{% if child.description %}<span class="block text-xs text-muted-foreground mt-0.5">{{ child.description }}</span>{% endif %}</a>{% endif %}</div>{% endfor %}</div></div>
{% else %}<div class="absolute left-0 mt-1 w-56 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50"><div class="py-2 bg-card border border-primary/40 neon-edge-cyan rounded-md shadow-lg">{% for child in item.children %}{% if child.item_type == "header" %}<div class="y2k-mono px-4 py-2 text-xs font-semibold text-accent uppercase tracking-widest">{{ child.label }}</div>{% else %}<a href="{{ child.url|default:'#' }}"{% if child.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="block px-4 py-2 text-sm text-foreground hover:bg-accent transition-colors {{ child.css_class }}">{{ child.label }}</a>{% endif %}{% endfor %}</div></div>{% endif %}
</div>{% endmacro %}
{% macro brand(size) %}<a href="/" class="flex-shrink-0 flex items-center">{% if logoType == "text" and logoText.text %}<span class="y2k-display text-xl font-bold text-foreground {{ logoText.color }}">{{ logoText.text }}</span>{% elif logo %}<img src="{{ logo }}" alt="{{ alt|default:companyName }}" class="{{ size }} w-auto"{% if logoMaxWidth or logoMaxHeight %} style="{% if logoMaxWidth %}max-width:{{ logoMaxWidth }};{% endif %}{% if logoMaxHeight %}max-height:{{ logoMaxHeight }};{% endif %}"{% endif %}/>{% elif companyName %}<span class="y2k-display text-xl font-bold text-foreground">{{ companyName }}</span>{% endif %}</a>{% endmacro %}
{% macro ctabtn(extra) %}{% if cta_text %}<a href="{{ cta_url|default:'#' }}" class="y2k-mono uppercase tracking-wider clip-cut rgb-split inline-flex items-center rounded-none bg-primary px-4 py-2 text-xs font-semibold text-primary-foreground hover:bg-primary/90 transition-colors {{ extra }}">{{ cta_text }}</a>{% endif %}{% endmacro %}
<nav data-bn-navbar data-bn-nav-variant="{{ nv }}" data-bn-sticky="{{ sb }}"{% if is_transparent %} data-bn-transparent="1"{% endif %} class="bn-navbar w-full {% if sb == "sticky" or sb == "shrink-on-scroll" %}sticky top-0 z-40 {% endif %}{% if is_transparent %}absolute top-0 left-0 z-40 text-white{% else %}{{ bg }}{% if borderBottom %} border-b border-primary/30{% endif %}{% endif %} {{ class }}">
{% if showUtilityBar %}<div class="y2k-mono w-full text-xs {% if not showOnMobile %}hidden sm:block{% endif %} bg-muted/60 text-muted-foreground border-b border-primary/20"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex items-center justify-between h-8"><div>{{ utilityBarText|safe }}</div><div class="flex items-center gap-4">{% if utilityBarPhone %}<a href="tel:{{ utilityBarPhone }}" class="hover:text-primary transition-colors">{{ utilityBarPhone }}</a>{% endif %}{% if utilityBarEmail %}<a href="mailto:{{ utilityBarEmail }}" class="hover:text-primary transition-colors">{{ utilityBarEmail }}</a>{% endif %}</div></div></div>{% endif %}
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="bn-navbar-row flex items-center justify-between h-16">
{% if nv == "centered-logo" %}
<div class="hidden md:flex items-center gap-1 flex-1">{% for item in menu.items %}{% if item.children %}{{ dropdown(item) }}{% else %}{{ navlink(item) }}{% endif %}{% endfor %}</div>
<div class="flex items-center justify-center">{{ brand("h-9") }}</div>
<div class="hidden md:flex items-center gap-3 flex-1 justify-end">{{ ctabtn("") }}</div>
{% elif nv == "split" %}
<div class="hidden md:flex items-center gap-1 flex-1">{% for item in menu.items %}{% if forloop.Counter0 * 2 < menu.items|length %}{% if item.children %}{{ dropdown(item) }}{% else %}{{ navlink(item) }}{% endif %}{% endif %}{% endfor %}</div>
<div class="flex items-center justify-center">{{ brand("h-9") }}</div>
<div class="hidden md:flex items-center gap-1 flex-1 justify-end">{% for item in menu.items %}{% if forloop.Counter0 * 2 >= menu.items|length %}{% if item.children %}{{ dropdown(item) }}{% else %}{{ navlink(item) }}{% endif %}{% endif %}{% endfor %}{{ ctabtn("ml-3") }}</div>
{% else %}
<div class="flex items-center">{{ brand("h-8") }}</div>
<div class="hidden md:flex items-center gap-1">{% for item in menu.items %}{% if item.children %}{{ dropdown(item) }}{% else %}{{ navlink(item) }}{% endif %}{% endfor %}{{ ctabtn("ml-3") }}</div>
{% endif %}
<div class="flex items-center md:hidden">
<label for="bn-navbar-toggle" data-bn-nav-open aria-controls="bn-navbar-drawer" aria-expanded="false" class="inline-flex items-center justify-center p-2 rounded-md cursor-pointer hover:bg-accent/50 transition-colors"><span class="sr-only">Open menu</span><svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg></label>
</div>
</div>
</div>
{# --- Mobile off-canvas drawer: pure-CSS checkbox baseline (works with no JS) --- #}
<input type="checkbox" id="bn-navbar-toggle" class="bn-navbar-toggle" aria-hidden="true"/>
<label for="bn-navbar-toggle" class="bn-navbar-overlay fixed inset-0 bg-black/50 z-40 md:hidden" aria-hidden="true"></label>
<div id="bn-navbar-drawer" class="bn-navbar-drawer fixed top-0 right-0 h-full w-80 max-w-[85vw] bg-background text-foreground z-50 shadow-xl md:hidden flex flex-col" role="dialog" aria-modal="true" aria-label="Site menu">
<div class="flex items-center justify-between p-4 border-b border-primary/30">{{ brand("h-8") }}<label for="bn-navbar-toggle" data-bn-nav-close class="p-2 rounded-md cursor-pointer text-muted-foreground hover:text-primary hover:bg-accent"><span class="sr-only">Close menu</span><svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg></label></div>
<nav class="flex-1 overflow-y-auto py-4" aria-label="Mobile">{% for item in menu.items %}{% if item.children %}<details class="group/acc"><summary class="y2k-mono uppercase tracking-wide flex items-center justify-between px-6 py-3 text-sm font-medium cursor-pointer hover:bg-accent transition-colors list-none">{{ item.label }}<svg class="h-4 w-4 transition-transform group-open/acc:rotate-180" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg></summary><div class="pl-8 pb-2">{% for child in item.children %}{% if child.item_type != "header" %}<a href="{{ child.url|default:'#' }}"{% if child.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="block px-2 py-2 text-sm text-muted-foreground hover:text-primary transition-colors">{{ child.label }}</a>{% endif %}{% endfor %}</div></details>{% else %}<a href="{{ item.url|default:'#' }}"{% if item.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="y2k-mono uppercase tracking-wide block px-6 py-3 text-sm font-medium hover:bg-accent transition-colors">{{ item.label }}</a>{% endif %}{% endfor %}</nav>
{% if cta_text %}<div class="p-4 border-t border-primary/30"><a href="{{ cta_url|default:'#' }}" class="y2k-mono uppercase tracking-wider clip-cut block w-full text-center rounded-none bg-primary px-4 py-3 text-base font-semibold text-primary-foreground hover:bg-primary/90 transition-colors">{{ cta_text }}</a></div>{% endif %}
</div>
</nav>
<style>
.bn-navbar-toggle{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none;}
.bn-navbar-drawer{transform:translateX(100%);transition:transform .3s ease;}
.bn-navbar-overlay{opacity:0;pointer-events:none;transition:opacity .3s ease;}
.bn-navbar-toggle:checked ~ .bn-navbar-drawer{transform:translateX(0);}
.bn-navbar-toggle:checked ~ .bn-navbar-overlay{opacity:1;pointer-events:auto;}
.bn-navbar-drawer details > summary::-webkit-details-marker{display:none;}
[data-bn-sticky="shrink-on-scroll"].bn-navbar-shrunk .bn-navbar-row{height:3rem;}
[data-bn-sticky="shrink-on-scroll"] .bn-navbar-row{transition:height .2s ease;}
[data-bn-transparent].bn-navbar-solid{position:sticky;color:inherit;background:hsl(var(--background));border-bottom:1px solid hsl(var(--border));}
</style>
<script>
(function(){
if(window.__bnNavInit)return;window.__bnNavInit=1;
function ready(fn){if(document.readyState!='loading')fn();else document.addEventListener('DOMContentLoaded',fn);}
ready(function(){
document.querySelectorAll('[data-bn-navbar]').forEach(function(nav){
var toggle=nav.querySelector('.bn-navbar-toggle');
var drawer=nav.querySelector('.bn-navbar-drawer');
var opener=nav.querySelector('[data-bn-nav-open]');
if(toggle&&drawer){
function focusables(){return drawer.querySelectorAll('a[href],button,label[for],input,summary,[tabindex]:not([tabindex="-1"])');}
toggle.addEventListener('change',function(){
var open=toggle.checked;
document.documentElement.style.overflow=open?'hidden':'';
document.body.style.overflow=open?'hidden':'';
if(opener)opener.setAttribute('aria-expanded',open?'true':'false');
if(open){var f=focusables();if(f.length)f[0].focus();}
else if(opener&&opener.focus)opener.focus();
});
drawer.addEventListener('keydown',function(e){
if(e.key==='Escape'){toggle.checked=false;toggle.dispatchEvent(new Event('change'));return;}
if(e.key!=='Tab')return;
var f=focusables();if(!f.length)return;
var first=f[0],last=f[f.length-1];
if(e.shiftKey&&document.activeElement===first){e.preventDefault();last.focus();}
else if(!e.shiftKey&&document.activeElement===last){e.preventDefault();first.focus();}
});
drawer.querySelectorAll('a[href]').forEach(function(a){a.addEventListener('click',function(){toggle.checked=false;toggle.dispatchEvent(new Event('change'));});});
}
var sb=nav.getAttribute('data-bn-sticky');
var transparent=nav.getAttribute('data-bn-transparent')==='1';
if(sb==='shrink-on-scroll'||transparent){
var overHero=false;
if(transparent){var next=nav.nextElementSibling;overHero=!!next&&next.getBoundingClientRect().height>=240;if(!overHero)nav.classList.add('bn-navbar-solid');}
var onScroll=function(){
var y=window.scrollY||window.pageYOffset;
if(sb==='shrink-on-scroll')nav.classList.toggle('bn-navbar-shrunk',y>40);
if(transparent&&overHero)nav.classList.toggle('bn-navbar-solid',y>40);
};
window.addEventListener('scroll',onScroll,{passive:true});onScroll();
}
});
});
})();
</script>

View File

@ -1,12 +0,0 @@
{# 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. #}
<section class="py-12 bg-background text-foreground md:py-16{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-3xl px-4">
<div class="hud-frame clip-cut rounded-md {% if layout == "inline" %}bg-primary px-6 py-10 text-primary-foreground neon-edge-cyan md:px-12{% else %}border border-dashed border-primary/40 bg-card px-6 py-10 text-card-foreground neon-edge-magenta md:px-10{% endif %} text-center">
{% if title %}<h2 class="y2k-display glitch text-2xl font-semibold tracking-tight md:text-3xl" data-text="{{ title }}">{{ title }}</h2>{% endif %}
{% if description %}<p class="y2k-body mx-auto mt-3 max-w-xl {% if layout == "inline" %}text-primary-foreground/80{% else %}text-muted-foreground{% endif %}">{{ description }}</p>{% endif %}
<div class="mx-auto mt-6 max-w-md">{% signup_form list=listSlug source=source submit=submitText success_message=successMessage placeholder=placeholder %}</div>
</div>
</div>
</section>

View File

@ -1,13 +0,0 @@
{# 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. #}
<section class="relative overflow-hidden bg-background text-foreground py-20 md:py-28{% if class %} {{ class }}{% endif %}">
<div aria-hidden="true" class="scanlines pointer-events-none absolute inset-0"></div>
<div class="relative z-10 mx-auto max-w-2xl px-4 text-center">
<p class="y2k-mono text-sm font-semibold uppercase tracking-[0.3em] text-accent">Error 404 <span aria-hidden="true" class="caret-blink">_</span></p>
<h1 class="y2k-display glitch mt-3 text-4xl font-bold tracking-tight md:text-5xl" data-text="{{ title|default:'Signal lost' }}">{{ title|default:"Signal lost" }}</h1>
<p class="mt-4 text-lg text-muted-foreground">{{ emptyMessage|default:"That route returned nothing. The page moved, shipped, or never existed." }}</p>
{% if context.path %}<p class="y2k-mono mt-3 text-sm text-muted-foreground">requested: <span class="text-primary">{{ context.path }}</span></p>{% endif %}
<div class="mt-8 flex flex-wrap items-center justify-center gap-3">
<a href="/" class="y2k-mono clip-cut rgb-split inline-flex items-center gap-2 rounded-md bg-primary px-6 py-3 font-semibold uppercase tracking-wider text-primary-foreground transition-opacity hover:opacity-90"><svg class="h-4 w-4" aria-hidden="true"><use href="/icons/lucide.svg#arrow-right"></use></svg>Back to home</a>
</div>
</div>
</section>

View File

@ -1,13 +0,0 @@
{# 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. #}
<div class="password-reset-form hud-frame mx-auto max-w-md rounded-md border border-primary/40 bg-card p-6 text-card-foreground neon-edge-magenta">
<div id="reset-message"></div>
<div id="request-reset-panel">
<h2 class="y2k-display glitch text-2xl font-bold mb-2" data-text="Reset Password">Reset Password</h2>
<p class="text-muted-foreground mb-4">Enter your email address and we will send you a link to reset your password.</p>
<form hx-post="/api/auth/request-password-reset" hx-target="#reset-message" hx-swap="innerHTML">
<div class="mb-4"><label class="y2k-mono mb-1 block text-xs font-medium uppercase tracking-wide text-accent">Email</label><input type="email" name="email" required class="w-full rounded-md border border-primary/40 bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"/></div>
<button type="submit" class="y2k-mono clip-cut rgb-split w-full rounded-md bg-primary px-4 py-2.5 font-semibold uppercase tracking-wider text-primary-foreground hover:bg-primary/90" hx-disabled-elt="this">Send Reset Link</button>
</form>
<p class="mt-4 text-center text-sm"><a href="/login" class="y2k-mono uppercase tracking-wide text-primary hover:underline">Back to login</a></p>
</div>
</div>

View File

@ -1,16 +0,0 @@
{# popular-posts (y2k override) — compact ranked list from the `posts` provider. #}
<section class="bg-background text-foreground{% if class %} {{ class }}{% endif %}">
{% if heading %}<h2 class="y2k-eyebrow y2k-mono mb-4 text-xs font-semibold uppercase tracking-widest text-accent">{{ heading }}</h2>{% endif %}
<ol class="space-y-4">
{% for post in posts %}
<li class="flex items-start gap-3">
{% if showRank %}<span class="y2k-mono mt-0.5 text-lg font-bold tabular-nums text-primary" style="text-shadow: 0 0 8px hsl(var(--primary));">{{ forloop.Counter }}</span>{% endif %}
{% if showThumbnail and post.featured_image_url %}<a href="{{ post.url }}" class="h-12 w-12 flex-shrink-0 overflow-hidden rounded-md border border-primary/40 bg-muted">{% img post.featured_image_url alt=post.title class="h-full w-full object-cover" %}</a>{% endif %}
<div class="min-w-0">
<h3 class="text-sm font-medium leading-snug"><a href="{{ post.url }}" class="transition-colors hover:text-primary">{{ post.title }}</a></h3>
{% if post.published_at %}<p class="y2k-mono mt-0.5 text-xs uppercase tracking-wide text-accent">{{ post.published_at|date:"M j, Y" }}</p>{% endif %}
</div>
</li>
{% endfor %}
</ol>
</section>

View File

@ -1,23 +0,0 @@
{# 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 %}
<section class="hero relative flex min-h-[50vh] flex-col justify-end overflow-hidden bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="absolute inset-0 z-0">{% img post.featured_image_url alt=post.featured_image_alt class="h-full w-full object-cover" layout="hero" %}<div class="absolute inset-0 bg-black/60"></div></div>
<div class="scanlines pointer-events-none absolute inset-0 z-[1]"></div>
<div class="relative z-10 mx-auto w-full max-w-4xl px-4 py-12 sm:px-6 lg:px-8 {% if center %}text-center{% endif %}">
<h1 class="y2k-display glitch text-3xl font-bold leading-tight text-white sm:text-4xl lg:text-5xl" data-text="{{ post.title }}">{{ post.title }}</h1>
{% if showExcerpt and post.excerpt %}<p class="y2k-body mt-4 max-w-2xl text-lg text-white/80{% if center %} mx-auto{% endif %}">{{ post.excerpt }}</p>{% endif %}
{% if showMeta %}<div class="mt-6 flex flex-wrap items-center gap-x-3 gap-y-1 y2k-mono text-sm uppercase tracking-wide text-white/80{% if center %} justify-center{% endif %}">{% if post.author_name %}<span class="font-medium text-white">{{ post.author_name }}</span>{% endif %}{% if post.published_at %}<span>{{ post.published_at|date:"F j, Y" }}</span>{% endif %}{% if post.reading_time %}<span>{{ post.reading_time }} min read</span>{% endif %}</div>{% endif %}
</div>
</section>
{% else %}
<section class="relative overflow-hidden bg-background py-14 text-foreground md:py-20{% if class %} {{ class }}{% endif %}">
<div class="scanlines pointer-events-none absolute inset-0"></div>
<div class="relative z-10 mx-auto w-full max-w-3xl px-4 {% if center %}text-center{% endif %}">
<h1 class="y2k-display glitch text-3xl font-bold leading-tight tracking-tight sm:text-4xl lg:text-5xl" data-text="{{ post.title }}">{{ post.title }}</h1>
{% if showExcerpt and post.excerpt %}<p class="y2k-body mt-4 text-lg text-muted-foreground{% if center %} mx-auto max-w-2xl{% endif %}">{{ post.excerpt }}</p>{% endif %}
{% if showMeta %}<div class="mt-6 flex flex-wrap items-center gap-x-3 gap-y-1 y2k-mono text-sm uppercase tracking-wide text-accent{% if center %} justify-center{% endif %}">{% if post.author_name %}<span class="font-medium text-foreground">{{ post.author_name }}</span>{% endif %}{% if post.published_at %}<span>{{ post.published_at|date:"F j, Y" }}</span>{% endif %}{% if post.reading_time %}<span>{{ post.reading_time }} min read</span>{% endif %}</div>{% endif %}
</div>
</section>
{% endif %}

View File

@ -1,9 +0,0 @@
{# post-metadata (y2k override) — author/date/reading-time row from the `post` provider. #}
{% if post %}
<div class="flex flex-wrap items-center gap-x-4 gap-y-2 y2k-mono text-sm uppercase tracking-wide text-accent{% if class %} {{ class }}{% endif %}">
{% if showAuthor and post.author_name %}<span class="flex items-center gap-2">{% 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 %}<span class="font-medium text-foreground">{{ post.author_name }}</span></span>{% endif %}
{% if showDate and post.published_at %}<time datetime="{{ post.published_at|date:"Y-m-d" }}">{{ post.published_at|date:"F j, Y" }}</time>{% endif %}
{% if showReadingTime and post.reading_time %}<span>{{ post.reading_time }} min read</span>{% endif %}
{% if post.categories %}<span class="flex flex-wrap gap-1.5">{% for cat in post.categories %}<a href="{{ cat.url|default:'#' }}" class="clip-cut border border-primary/40 bg-card px-2 py-0.5 text-xs text-card-foreground transition-colors hover:text-primary">{{ cat.name }}</a>{% endfor %}</span>{% endif %}
</div>
{% endif %}

View File

@ -1,26 +0,0 @@
{# pricing y2k override: scanline overlay, glitch heading, HUD neon tier panels (magenta highlight / cyan standard), clip-cut CTAs. Field reads + control flow preserved. #}
<section class="relative overflow-hidden py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div aria-hidden="true" class="scanlines pointer-events-none absolute inset-0"></div>
<div class="relative z-10 mx-auto max-w-6xl px-4">
{% if heading %}<h2 class="y2k-display glitch text-center text-3xl font-semibold tracking-tight md:text-4xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground">{{ intro }}</p>{% endif %}
<div class="mt-12 grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
{% for tier in tiers %}
<div class="hud-frame relative flex flex-col rounded-md border p-6 {% if tier.highlighted %}border-primary bg-card text-card-foreground neon-edge-magenta ring-1 ring-primary{% else %}border-primary/40 bg-card text-card-foreground neon-edge-cyan{% endif %}">
{% if tier.badge %}<span class="y2k-mono clip-cut absolute -top-3 left-1/2 -translate-x-1/2 rounded-none bg-primary px-3 py-1 text-xs font-semibold uppercase tracking-widest text-primary-foreground">{{ tier.badge }}</span>{% endif %}
<h3 class="y2k-display text-lg font-semibold">{{ tier.name }}</h3>
<p class="mt-4 flex items-baseline gap-1">
<span class="y2k-display text-4xl font-bold tracking-tight" style="font-family:var(--font-heading)">{% if currency %}{{ currency }}{% else %}${% endif %}{{ tier.price }}</span>
{% if tier.period %}<span class="y2k-mono text-sm text-muted-foreground">{{ tier.period }}</span>{% endif %}
</p>
{% if tier.description %}<p class="mt-3 text-sm text-muted-foreground">{{ tier.description }}</p>{% endif %}
{% if tier.features %}<ul class="mt-6 flex-1 space-y-3 text-sm">
{% for feature in tier.features %}<li class="flex items-start gap-2"><svg class="mt-0.5 h-4 w-4 shrink-0 text-primary" aria-hidden="true"><use href="/icons/lucide.svg#check"></use></svg><span>{{ feature }}</span></li>{% endfor %}
</ul>{% endif %}
{% if tier.ctaUrl and tier.ctaLabel %}<a href="{{ tier.ctaUrl }}" class="y2k-mono uppercase tracking-wider clip-cut mt-8 inline-flex w-full items-center justify-center rounded-none px-5 py-3 font-semibold transition-opacity hover:opacity-90 {% if tier.highlighted %}rgb-split bg-primary text-primary-foreground{% else %}border border-primary/40 bg-background text-foreground hover:bg-muted{% endif %}">{{ tier.ctaLabel }}</a>{% endif %}
</div>
{% endfor %}
</div>
{% if billingNote %}<p class="y2k-mono mt-8 text-center text-sm text-muted-foreground">{{ billingNote }}</p>{% endif %}
</div>
</section>

View File

@ -1,2 +0,0 @@
{# y2k quote — dashed neon left border; y2k-display for pull; mono figcaption #}
{% with style=style|default:"block" align=align|default:"left" %}<figure class="bn-quote my-6 {% if style == "inline-card" %}rounded-md border border-dashed border-primary/40 bg-card p-6 md:p-8 neon-edge-magenta{% elif style == "pull" %}border-l-4 border-dashed border-primary pl-6 md:pl-8 py-2{% else %}border-l-2 border-dashed border-primary/50 pl-5 py-1{% endif %}{% if align == "center" %} text-center{% endif %}{% if class %} {{ class }}{% endif %}"><blockquote{% if cite %} cite="{{ cite }}"{% endif %} class="{% if style == "pull" %}text-2xl md:text-3xl font-semibold leading-snug tracking-tight y2k-display{% else %}text-lg md:text-xl leading-relaxed{% endif %} text-foreground">{{ quote|safe }}</blockquote>{% if attribution or role or cite %}<figcaption class="mt-4 text-sm text-muted-foreground y2k-mono{% if align == "center" %} flex flex-col items-center{% endif %}">{% if attribution %}<span class="font-medium text-foreground">{{ attribution }}</span>{% endif %}{% if role %}<span class="{% if attribution %}block {% endif %}text-muted-foreground">{{ role }}</span>{% endif %}{% if cite %}<a href="{{ cite }}" class="{% if attribution or role %}mt-1 {% endif %}inline-block underline underline-offset-2 hover:text-foreground">Source</a>{% endif %}</figcaption>{% endif %}</figure>{% endwith %}

View File

@ -1,17 +0,0 @@
{# related-posts (y2k override) — grid from the `posts` provider. #}
{% if posts %}
<section class="border-t border-primary/30 py-12 md:py-16 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-6xl px-4">
<h2 class="y2k-display glitch mb-8 text-2xl font-semibold tracking-tight" data-text="{{ heading|default:'Related posts' }}">{{ heading|default:"Related posts" }}</h2>
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 {% if columns == 2 %}lg:grid-cols-2{% else %}lg:grid-cols-3{% endif %}">
{% for post in posts %}
<article class="hud-frame group flex flex-col rounded-md border border-dashed border-primary/40 bg-card p-4 text-card-foreground neon-edge-cyan transition-transform hover:-translate-y-0.5">
{% if post.featured_image_url %}<a href="{{ post.url }}" class="mb-3 block aspect-video overflow-hidden rounded-md bg-muted">{% img post.featured_image_url alt=post.title class="h-full w-full object-cover transition-transform duration-300 group-hover:scale-105" %}</a>{% endif %}
<h3 class="y2k-display text-base font-semibold leading-snug"><a href="{{ post.url }}" class="transition-colors hover:text-primary">{{ post.title }}</a></h3>
{% if post.published_at %}<p class="y2k-mono mt-1 text-xs uppercase tracking-wide text-accent">{{ post.published_at|date:"M j, Y" }}</p>{% endif %}
</article>
{% endfor %}
</div>
</div>
</section>
{% endif %}

View File

@ -1,14 +0,0 @@
{# rich_section y2k override: mono eyebrow, glitch heading, HUD-framed media, clip-cut CTA. Field reads + {% img %} preserved. #}
<section class="py-16 md:py-24 {% if background == "muted" %}bg-muted text-foreground{% elif background == "card" %}bg-card text-card-foreground{% else %}bg-background text-foreground{% endif %}{% if class %} {{ class }}{% endif %}">
<div class="mx-auto grid max-w-6xl items-center gap-10 px-4 md:grid-cols-2 md:gap-16">
<div class="{% if mediaPosition == "left" %}md:order-2{% else %}md:order-1{% endif %}">
{% if eyebrow %}<p class="y2k-mono text-xs font-semibold uppercase tracking-widest text-accent">{{ eyebrow }}</p>{% endif %}
{% if heading %}<h2 class="y2k-display glitch mt-2 text-3xl font-semibold tracking-tight md:text-4xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
{% if body %}<div class="mt-4 space-y-4 text-lg text-muted-foreground [&_a]:text-primary [&_a]:underline [&_strong]:text-foreground">{{ body|safe }}</div>{% endif %}
{% if ctaUrl and ctaLabel %}<a href="{{ ctaUrl }}" class="y2k-mono uppercase tracking-wider clip-cut rgb-split mt-6 inline-flex items-center gap-2 rounded-none bg-primary px-5 py-3 font-semibold text-primary-foreground transition-opacity hover:opacity-90">{{ ctaLabel }}<svg class="h-4 w-4" aria-hidden="true"><use href="/icons/lucide.svg#arrow-right"></use></svg></a>{% endif %}
</div>
<div class="{% if mediaPosition == "left" %}md:order-1{% else %}md:order-2{% endif %}">
{% if media %}<div class="hud-frame overflow-hidden rounded-md border border-dashed border-primary/50 neon-edge-cyan">{% img media alt=mediaAlt class="w-full h-auto object-cover" %}</div>{% endif %}
</div>
</div>
</section>

View File

@ -1,2 +0,0 @@
{# y2k rich-text — body font via y2k-prose #}
{% if html %}<article class="rich-text prose prose-lg max-w-none y2k-prose y2k-body">{{ html|safe }}</article>{% endif %}

View File

@ -1,15 +0,0 @@
{# 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" %}
<nav class="flex-1 {% if compact %}w-56 py-4 pr-3{% else %}w-64 py-6 pr-4{% endif %}{% if showBorder %} border-r border-primary/30{% endif %}{% if class %} {{ class }}{% endif %}">
{% if title %}<h3 class="y2k-eyebrow y2k-mono {% if compact %}px-2 mb-3 text-xs uppercase tracking-widest text-accent{% else %}px-3 mb-4 text-sm uppercase tracking-widest text-accent{% endif %} font-semibold">{{ title }}</h3>{% endif %}
<ul class="{% if compact %}space-y-0.5{% else %}space-y-1{% endif %}">
{% for item in menu.items %}
<li><a href="{{ item.url|default:'#' }}"{% if item.open_in_new_tab %} target="_blank" rel="noopener"{% endif %}{% if item.title %} title="{{ item.title }}"{% endif %} data-bn-sidebar-link class="flex items-center rounded-md y2k-mono text-sm uppercase tracking-wide text-muted-foreground transition-colors hover:text-primary hover:bg-accent {% if compact %}px-2 py-1.5{% else %}px-3 py-2{% endif %} {{ item.css_class }}">{% if item.icon %}<svg class="mr-2 h-4 w-4" aria-hidden="true"><use href="/icons/{{ item.icon|split:":"|first }}.svg#{{ item.icon|split:":"|last }}"></use></svg>{% endif %}{{ item.label }}</a></li>
{% endfor %}
</ul>
</nav>
{% if highlightCurrent %}<script>
(function(){var p=window.location.pathname;document.querySelectorAll('[data-bn-sidebar-link]').forEach(function(a){if(a.getAttribute('href')===p){a.classList.remove('text-muted-foreground');a.classList.add('text-foreground','bg-accent','font-medium');}});})();
</script>{% endif %}

View File

@ -1,14 +0,0 @@
{# y2k social-links — mono label, sharp neon-bordered chips; icon sprites preserved #}
<div class="social-links{% if class %} {{ class }}{% endif %}">
{% if heading %}<p class="mb-3 text-sm font-medium text-muted-foreground y2k-mono uppercase tracking-widest">{{ heading }}</p>{% endif %}
<ul class="flex {% if style == "stack" %}flex-col gap-2{% else %}flex-wrap items-center gap-3{% endif %}">
{% for link in links %}{% if link.url %}
<li>
<a href="{{ link.url }}" target="_blank" rel="noopener noreferrer" aria-label="{{ link.label|default:link.network }}" class="inline-flex items-center gap-2 rounded-none border border-primary/40 text-foreground transition-colors hover:bg-accent hover:text-accent-foreground {% if style == "stack" %}w-full px-4 py-2{% else %}p-2.5{% endif %}">
{% if link.network %}<svg class="h-5 w-5" aria-hidden="true"><use href="/icons/{{ link.network|split:":"|first }}.svg#{{ link.network|split:":"|last }}"></use></svg>{% endif %}
{% if style == "stack" and link.label %}<span class="text-sm font-medium y2k-mono">{{ link.label }}</span>{% endif %}
</a>
</li>
{% endif %}{% endfor %}
</ul>
</div>

View File

@ -1,50 +0,0 @@
{# stats y2k override: scanline overlay, glitch heading, HUD stat tiles, mono labels. data-stats + countUp script/attrs preserved byte-for-byte. #}
<section class="relative overflow-hidden py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}"{% if countUp %} data-stats{% endif %}>
<div aria-hidden="true" class="scanlines pointer-events-none absolute inset-0"></div>
<div class="relative z-10 mx-auto max-w-6xl px-4">
{% if heading %}<h2 class="y2k-display glitch text-center text-3xl font-semibold tracking-tight md:text-4xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground">{{ intro }}</p>{% endif %}
<dl class="mt-12 grid grid-cols-2 gap-8 {% if columns == 2 %}md:grid-cols-2{% elif columns == 3 %}md:grid-cols-3{% else %}md:grid-cols-4{% endif %}">
{% for item in items %}
<div class="hud-frame text-center">
<dd class="y2k-display text-4xl font-bold tracking-tight md:text-5xl" style="font-family:var(--font-heading)">{% if item.prefix %}{{ item.prefix }}{% endif %}<span{% if countUp %} data-countup-value="{{ item.value }}"{% endif %}>{{ item.value }}</span>{% if item.suffix %}{{ item.suffix }}{% endif %}</dd>
{% if item.label %}<dt class="y2k-mono mt-2 text-sm font-medium uppercase tracking-widest text-muted-foreground">{{ item.label }}</dt>{% endif %}
</div>
{% endfor %}
</dl>
</div>
</section>
{% if countUp %}<script>
(function(){
var sections = document.querySelectorAll('[data-stats]');
var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
function animate(section){
var nodes = section.querySelectorAll('[data-countup-value]');
nodes.forEach(function(node){
var raw = node.getAttribute('data-countup-value') || '';
var target = parseFloat(raw.replace(/[^0-9.\-]/g, ''));
if (isNaN(target)) return;
var decimals = (raw.split('.')[1] || '').length;
var start = null, dur = 1200;
function step(ts){
if (start === null) start = ts;
var p = Math.min((ts - start) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
node.textContent = (target * eased).toFixed(decimals);
if (p < 1) requestAnimationFrame(step);
else node.textContent = target.toFixed(decimals);
}
requestAnimationFrame(step);
});
}
sections.forEach(function(section){
if (reduce || !('IntersectionObserver' in window)) return;
var io = new IntersectionObserver(function(entries){
entries.forEach(function(e){
if (e.isIntersecting){ animate(section); io.disconnect(); }
});
}, { threshold: 0.3 });
io.observe(section);
});
})();
</script>{% endif %}

View File

@ -1,22 +0,0 @@
{# team y2k override: glitch heading, dashed-neon HUD member panels (lime), mono roles. {% img %} + social icon svgs + control flow preserved. #}
<section class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-6xl px-4">
{% if heading %}<h2 class="y2k-display glitch text-center text-3xl font-semibold tracking-tight md:text-4xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground">{{ intro }}</p>{% endif %}
<div class="mt-12 {% if layout == "list" %}mx-auto max-w-3xl space-y-6{% elif layout == "single" %}mx-auto max-w-xl{% else %}grid grid-cols-1 gap-8 sm:grid-cols-2 {% if columns == 2 %}lg:grid-cols-2{% elif columns == 4 %}lg:grid-cols-4{% else %}lg:grid-cols-3{% endif %}{% endif %}">
{% for member in members %}
<div class="{% if layout == "list" %}hud-frame flex items-start gap-5 rounded-md border border-dashed border-primary/50 bg-card p-5 text-card-foreground neon-edge-lime{% else %}text-center{% endif %}">
{% if member.photo %}<span class="{% if layout == "list" %}h-20 w-20{% else %}mx-auto h-28 w-28{% endif %} block shrink-0 overflow-hidden rounded-full bg-muted">{% img member.photo alt=member.name class="h-full w-full object-cover" %}</span>{% endif %}
<div class="{% if layout != "list" %}mt-4{% endif %}">
{% if member.name %}<h3 class="y2k-display text-lg font-semibold">{{ member.name }}</h3>{% endif %}
{% if member.role %}<p class="y2k-mono uppercase tracking-wide text-sm font-medium text-primary">{{ member.role }}</p>{% endif %}
{% if member.bio %}<p class="mt-2 text-sm text-muted-foreground">{{ member.bio }}</p>{% endif %}
{% if member.socials %}<div class="mt-3 flex gap-3 {% if layout != "list" %}justify-center{% endif %}">
{% for social in member.socials %}{% if social.url %}<a href="{{ social.url }}" class="text-muted-foreground transition-colors hover:text-primary" aria-label="Social link"><svg class="h-5 w-5" aria-hidden="true"><use href="/icons/{{ social.icon|split:":"|first }}.svg#{{ social.icon|split:":"|last }}"></use></svg></a>{% endif %}{% endfor %}
</div>{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
</section>

View File

@ -1,27 +0,0 @@
{# testimonial y2k override: glitch heading, dashed-neon HUD quote cards. Rating svgs, {% img %}, and control flow preserved. #}
<section class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-6xl px-4">
{% if heading %}<h2 class="y2k-display glitch text-center text-3xl font-semibold tracking-tight md:text-4xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
<div class="mt-12 {% if layout == "single" %}mx-auto max-w-3xl{% elif layout == "carousel" %}flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4{% else %}grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3{% endif %}">
{% for q in quotes %}
<figure class="hud-frame flex flex-col rounded-md border border-dashed border-primary/50 bg-card p-6 text-card-foreground neon-edge-cyan{% if layout == "carousel" %} min-w-[18rem] max-w-sm shrink-0 snap-start{% endif %}">
{% if q.rating %}<div class="mb-3 flex gap-0.5" aria-label="Rated {{ q.rating }} out of 5">
<svg class="h-4 w-4 {% if q.rating >= 1 %}text-primary{% else %}text-muted-foreground/30{% endif %}" aria-hidden="true"><use href="/icons/lucide.svg#star"></use></svg>
<svg class="h-4 w-4 {% if q.rating >= 2 %}text-primary{% else %}text-muted-foreground/30{% endif %}" aria-hidden="true"><use href="/icons/lucide.svg#star"></use></svg>
<svg class="h-4 w-4 {% if q.rating >= 3 %}text-primary{% else %}text-muted-foreground/30{% endif %}" aria-hidden="true"><use href="/icons/lucide.svg#star"></use></svg>
<svg class="h-4 w-4 {% if q.rating >= 4 %}text-primary{% else %}text-muted-foreground/30{% endif %}" aria-hidden="true"><use href="/icons/lucide.svg#star"></use></svg>
<svg class="h-4 w-4 {% if q.rating >= 5 %}text-primary{% else %}text-muted-foreground/30{% endif %}" aria-hidden="true"><use href="/icons/lucide.svg#star"></use></svg>
</div>{% endif %}
<blockquote class="flex-1 text-lg leading-relaxed">{% if q.quote %}&ldquo;{{ q.quote }}&rdquo;{% endif %}</blockquote>
<figcaption class="mt-5 flex items-center gap-3">
{% if q.avatar %}<span class="h-10 w-10 shrink-0 overflow-hidden rounded-full bg-muted">{% img q.avatar alt=q.name class="h-full w-full object-cover" %}</span>{% endif %}
<span>
{% if q.name %}<span class="y2k-display block font-semibold">{{ q.name }}</span>{% endif %}
{% if q.role %}<span class="y2k-mono uppercase tracking-wide block text-sm text-muted-foreground">{{ q.role }}</span>{% endif %}
</span>
</figcaption>
</figure>
{% endfor %}
</div>
</div>
</section>

View File

@ -1,2 +1 @@
{# y2k text — body font via y2k-prose #}
<div class="prose prose-gray max-w-none flex-1 y2k-prose y2k-body {{ proseClass }}">{{ text|safe }}</div>
<div class="y2k-text prose max-w-none {{ class }}">{{ text|safe }}</div>

View File

@ -1,22 +0,0 @@
{# timeline y2k override: glitch heading, mono step labels, neon markers. All field reads + control flow preserved. #}
<section class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-4xl px-4">
{% if heading %}<h2 class="y2k-display glitch text-center text-3xl font-semibold tracking-tight md:text-4xl" data-text="{{ heading }}">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground">{{ intro }}</p>{% endif %}
<ol class="mt-12 {% if orientation == "horizontal" %}grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4{% else %}relative space-y-8 before:absolute before:left-4 before:top-2 before:h-[calc(100%-1rem)] before:w-px before:bg-primary/50{% endif %}">
{% for step in steps %}
<li class="{% if orientation == "horizontal" %}text-center{% else %}relative pl-14{% endif %}">
<span class="clip-cut flex h-8 w-8 items-center justify-center rounded-md bg-primary text-sm font-semibold text-primary-foreground y2k-mono {% if orientation == "horizontal" %}mx-auto{% else %}absolute left-0 top-0 ring-4 ring-background{% endif %}">
{% if marker == "numbered" %}{{ forloop.counter }}{% elif marker == "dot" %}<span class="h-2 w-2 rounded-full bg-primary-foreground"></span>{% else %}<svg class="h-4 w-4" aria-hidden="true"><use href="/icons/lucide.svg#flag"></use></svg>{% endif %}
</span>
<div class="{% if orientation == "horizontal" %}mt-4{% else %}pt-1{% endif %}">
{% if step.label %}<p class="y2k-mono text-xs font-semibold uppercase tracking-widest text-accent">{{ step.label }}</p>{% endif %}
{% if step.title %}<h3 class="y2k-display mt-1 text-lg font-semibold">{{ step.title }}</h3>{% endif %}
{% if step.text %}<p class="mt-1 text-muted-foreground">{{ step.text }}</p>{% endif %}
{% if step.media %}<div class="mt-3 overflow-hidden rounded-md border border-primary/40">{% img step.media alt=step.title class="w-full h-auto object-cover" %}</div>{% endif %}
</div>
</li>
{% endfor %}
</ol>
</div>
</section>

View File

@ -1,10 +0,0 @@
{# 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 %}<a href="{{ it.link }}" class="inline-flex items-center gap-1.5 hover:opacity-80 transition-opacity">{% if it.icon %}::{{ it.icon }}::{% endif %}{% if it.label %}<span>{{ it.label }}</span>{% endif %}</a>{% else %}<span class="inline-flex items-center gap-1.5">{% if it.icon %}::{{ it.icon }}::{% endif %}{% if it.label %}<span>{{ it.label }}</span>{% endif %}</span>{% endif %}{% endmacro %}
<div class="y2k-mono uppercase tracking-wide w-full text-xs {% if not showOnMobile %}hidden sm:block{% endif %} {{ ubcls }} {{ class }}">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-9 flex items-center justify-between gap-4">
<div class="flex items-center gap-4 flex-wrap">{% for it in leftItems %}{{ ubitem(it) }}{% endfor %}</div>
<div class="flex items-center gap-4 flex-wrap justify-end">{% for it in rightItems %}{{ ubitem(it) }}{% endfor %}</div>
</div>
</div>

View File

@ -1,23 +0,0 @@
{# 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" %}<figure class="bn-video my-6{% if class %} {{ class }}{% endif %}"><div class="bn-video-frame hud-frame relative w-full overflow-hidden rounded-md border border-primary/40 bg-muted neon-edge-magenta {% if aspect == "4:3" %}aspect-[4/3]{% elif aspect == "1:1" %}aspect-square{% elif aspect == "21:9" %}aspect-[21/9]{% else %}aspect-video{% endif %}" data-bn-video data-video-url="{{ url|default:"" }}" data-video-media="{% if media %}{{ media|mediaURL }}{% endif %}"><button type="button" data-bn-video-play class="group absolute inset-0 flex h-full w-full items-center justify-center focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring" aria-label="Play video{% if title %}: {{ title }}{% endif %}">{% if poster %}{% img poster alt=title|default:"" class="absolute inset-0 h-full w-full object-cover" %}{% endif %}<span class="absolute inset-0 bg-primary/10 transition-colors group-hover:bg-primary/20" aria-hidden="true"></span><span class="clip-cut relative inline-flex h-16 w-16 items-center justify-center rounded-md border border-primary/60 bg-background/90 text-primary shadow-lg transition-transform group-hover:scale-105 motion-reduce:transform-none">::lucide:play:xl::</span></button></div>{% if caption %}<figcaption class="y2k-mono mt-2 text-center text-sm text-muted-foreground">{{ caption }}</figcaption>{% endif %}</figure><script>
(function(){
if(window.__bnVideoFacade)return;window.__bnVideoFacade=true;
function yt(u){var m=u.match(/(?:youtube\.com\/(?:watch\?v=|embed\/|shorts\/)|youtu\.be\/)([\w-]{6,})/);return m?m[1]:null;}
function vim(u){var m=u.match(/vimeo\.com\/(?:video\/)?(\d+)/);return m?m[1]:null;}
document.addEventListener('click',function(e){
var btn=e.target.closest('[data-bn-video-play]');if(!btn)return;
var frame=btn.closest('[data-bn-video]');if(!frame)return;
e.preventDefault();
var url=frame.getAttribute('data-video-url')||'',media=frame.getAttribute('data-video-media')||'',node;
var vstyle='position:absolute;inset:0;width:100%;height:100%;object-fit:contain;background:rgba(0,0,0,1);';
if(media){node=document.createElement('video');node.src=media;node.controls=true;node.autoplay=true;node.setAttribute('playsinline','');node.style.cssText=vstyle;}
else{var y=yt(url),v=vim(url),embed;
if(y)embed='https://www.youtube-nocookie.com/embed/'+y+'?autoplay=1&rel=0';
else if(v)embed='https://player.vimeo.com/video/'+v+'?autoplay=1';
if(embed){node=document.createElement('iframe');node.src=embed;node.allow='autoplay; fullscreen; picture-in-picture';node.setAttribute('allowfullscreen','');node.setAttribute('title',btn.getAttribute('aria-label')||'Video');node.style.cssText='position:absolute;inset:0;width:100%;height:100%;border:0;';}
else if(url){node=document.createElement('video');node.src=url;node.controls=true;node.autoplay=true;node.setAttribute('playsinline','');node.style.cssText=vstyle;}
}
if(node){btn.remove();frame.appendChild(node);}
});
})();
</script>{% endwith %}

View File

@ -1,15 +1,22 @@
<!doctype html>
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
<html lang="en">
{{ head_html|safe }}
<body class="y2k-body min-h-screen flex flex-col antialiased" style="background-color: hsl(var(--background)); color: hsl(var(--foreground)); font-family: var(--font-body, &quot;Inter&quot;, system-ui, sans-serif);">
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col">
{{ admin_banner_html|safe }}
<div aria-hidden="true" class="scanlines pointer-events-none fixed inset-0 z-50" data-y2k-scanlines></div>
<header class="y2k-chrome w-full sticky top-0 z-40" style="background-color: hsl(var(--background) / 0.85); border-bottom: 1px solid hsl(var(--border));">{{ slots.header|safe }}</header>
<main class="flex-grow w-full max-w-5xl mx-auto px-4 py-12 grid gap-10 lg:grid-cols-[1fr_260px]">
<article class="y2k-prose prose prose-invert max-w-none min-w-0">{{ slots.main|safe }}</article>
{% if slots.aside %}<aside class="y2k-aside y2k-mono hud-frame rounded-md border border-primary/30 p-5 text-sm h-max" style="color: hsl(var(--muted-foreground)); background-color: hsl(var(--card));">{{ slots.aside|safe }}</aside>{% endif %}
</main>
<footer class="w-full mt-auto" style="border-top: 1px solid hsl(var(--border));">{{ slots.footer|safe }}</footer>
<header class="w-full">
{{ slots.header|safe }}
</header>
<div class="max-w-6xl mx-auto w-full px-4 py-10 flex-grow grid gap-8 md:grid-cols-[1fr_280px]">
<main class="min-w-0">
{% if slots.main %}<article class="y2k-text prose max-w-none">{{ slots.main|safe }}</article>{% else %}<div class="py-20 text-center"><p class="text-muted-foreground">No content blocks assigned to this page.</p></div>{% endif %}
</main>
<aside class="min-w-0">
{{ slots.aside|safe }}
</aside>
</div>
<footer class="w-full mt-auto">
{{ slots.footer|safe }}
</footer>
{{ body_end_html|safe }}
</body>
</html>

View File

@ -1,14 +0,0 @@
<!doctype html>
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
{{ head_html|safe }}
<body class="y2k-body min-h-screen flex flex-col antialiased" style="background-color: hsl(var(--background)); color: hsl(var(--foreground)); font-family: var(--font-body, &quot;Inter&quot;, system-ui, sans-serif);">
{{ admin_banner_html|safe }}
<div aria-hidden="true" class="scanlines pointer-events-none fixed inset-0 z-50" data-y2k-scanlines></div>
<header class="y2k-chrome w-full" style="border-bottom: 1px solid hsl(var(--border));">{{ slots.header|safe }}</header>
<main class="flex-grow w-full flex items-center justify-center px-4 py-16">
<div class="w-full max-w-md">{% if slots.main %}{{ slots.main|safe }}{% else %}<p class="y2k-mono text-center uppercase tracking-widest" style="color: hsl(var(--muted-foreground));">Sign in</p>{% endif %}</div>
</main>
<footer class="w-full mt-auto" style="border-top: 1px solid hsl(var(--border));">{{ slots.footer|safe }}</footer>
{{ body_end_html|safe }}
</body>
</html>

View File

@ -1,12 +0,0 @@
<!doctype html>
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
{{ head_html|safe }}
<body class="y2k-body min-h-screen flex flex-col antialiased" style="background-color: hsl(var(--background)); color: hsl(var(--foreground)); font-family: var(--font-body, &quot;Inter&quot;, system-ui, sans-serif);">
{{ admin_banner_html|safe }}
<div aria-hidden="true" class="scanlines pointer-events-none fixed inset-0 z-50" data-y2k-scanlines></div>
<header class="y2k-chrome w-full sticky top-0 z-40" style="background-color: hsl(var(--background) / 0.85); border-bottom: 1px solid hsl(var(--border));">{{ slots.header|safe }}</header>
<main class="flex-grow w-full max-w-6xl mx-auto px-4 py-12">{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="py-20 text-center y2k-mono uppercase tracking-widest" style="color: hsl(var(--muted-foreground));"><p>No posts yet</p></div>{% endif %}</main>
<footer class="w-full mt-auto" style="border-top: 1px solid hsl(var(--border));">{{ slots.footer|safe }}</footer>
{{ body_end_html|safe }}
</body>
</html>

View File

@ -1,12 +0,0 @@
<!doctype html>
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
{{ head_html|safe }}
<body class="y2k-body min-h-screen flex flex-col antialiased" style="background-color: hsl(var(--background)); color: hsl(var(--foreground)); font-family: var(--font-body, &quot;Inter&quot;, system-ui, sans-serif);">
{{ admin_banner_html|safe }}
<div aria-hidden="true" class="scanlines pointer-events-none fixed inset-0 z-50" data-y2k-scanlines></div>
<header class="y2k-chrome w-full sticky top-0 z-40" style="background-color: hsl(var(--background) / 0.85); border-bottom: 1px solid hsl(var(--border));">{{ slots.header|safe }}</header>
<main class="flex-grow w-full max-w-5xl mx-auto px-4 py-12">{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="py-20 text-center y2k-mono uppercase tracking-widest" style="color: hsl(var(--muted-foreground));"><p>Add a contact form here</p></div>{% endif %}</main>
<footer class="w-full mt-auto" style="border-top: 1px solid hsl(var(--border));">{{ slots.footer|safe }}</footer>
{{ body_end_html|safe }}
</body>
</html>

View File

@ -1,12 +1,17 @@
<!doctype html>
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
<html lang="en">
{{ head_html|safe }}
<body class="y2k-body min-h-screen flex flex-col antialiased" style="background-color: hsl(var(--background)); color: hsl(var(--foreground)); font-family: var(--font-body, &quot;Inter&quot;, system-ui, sans-serif);">
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col">
{{ admin_banner_html|safe }}
<div aria-hidden="true" class="scanlines pointer-events-none fixed inset-0 z-50" data-y2k-scanlines></div>
<header class="y2k-chrome w-full sticky top-0 z-40" style="background-color: hsl(var(--background) / 0.85); border-bottom: 1px solid hsl(var(--border));">{{ slots.header|safe }}</header>
<main class="flex-grow w-full max-w-6xl mx-auto px-4 py-12">{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="py-20 text-center y2k-empty" style="color: hsl(var(--muted-foreground));"><p class="y2k-mono uppercase tracking-widest">Nothing on this page yet</p></div>{% endif %}</main>
<footer class="w-full mt-auto" style="border-top: 1px solid hsl(var(--border));">{{ slots.footer|safe }}</footer>
<header class="w-full">
{{ slots.header|safe }}
</header>
<main class="flex-grow max-w-4xl mx-auto w-full px-4 py-8">
{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="py-20 text-center"><p class="text-muted-foreground">No content blocks assigned to this page.</p></div>{% endif %}
</main>
<footer class="w-full mt-auto">
{{ slots.footer|safe }}
</footer>
{{ body_end_html|safe }}
</body>
</html>

View File

@ -1,12 +1,17 @@
<!doctype html>
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
<html lang="en">
{{ head_html|safe }}
<body class="y2k-body min-h-screen flex flex-col antialiased" style="background-color: hsl(var(--background)); color: hsl(var(--foreground)); font-family: var(--font-body, &quot;Inter&quot;, system-ui, sans-serif);">
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col y2k-mesh-bg">
{{ admin_banner_html|safe }}
<div aria-hidden="true" class="scanlines pointer-events-none fixed inset-0 z-50" data-y2k-scanlines></div>
<header class="y2k-chrome w-full sticky top-0 z-40" style="background-color: hsl(var(--background) / 0.85); border-bottom: 1px solid hsl(var(--border));">{{ slots.header|safe }}</header>
<main class="flex-grow w-full">{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="py-20 text-center y2k-mono uppercase tracking-widest" style="color: hsl(var(--muted-foreground));"><p>Nothing on this page yet</p></div>{% endif %}</main>
<footer class="w-full mt-auto" style="border-top: 1px solid hsl(var(--border));">{{ slots.footer|safe }}</footer>
<header class="w-full">
{{ slots.header|safe }}
</header>
<main class="flex-grow w-full">
{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="max-w-4xl mx-auto py-20 px-4 text-center"><p class="text-muted-foreground">No content blocks assigned to this page.</p></div>{% endif %}
</main>
<footer class="w-full mt-auto">
{{ slots.footer|safe }}
</footer>
{{ body_end_html|safe }}
</body>
</html>

View File

@ -1,12 +1,23 @@
<!doctype html>
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
<html lang="en">
{{ head_html|safe }}
<body class="y2k-body min-h-screen flex flex-col antialiased" style="background-color: hsl(var(--background)); color: hsl(var(--foreground)); font-family: var(--font-body, &quot;Inter&quot;, system-ui, sans-serif);">
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col">
{{ admin_banner_html|safe }}
<div aria-hidden="true" class="scanlines pointer-events-none fixed inset-0 z-50" data-y2k-scanlines></div>
<header class="y2k-chrome w-full sticky top-0 z-40">{{ slots.header|safe }}</header>
<main class="flex-grow w-full">{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="py-20 text-center y2k-mono uppercase tracking-widest" style="color: hsl(var(--muted-foreground));"><p>Nothing on this page yet</p></div>{% endif %}</main>
<footer class="w-full mt-auto" style="border-top: 1px solid hsl(var(--border));">{{ slots.footer|safe }}</footer>
<section class="w-full">
{{ slots.hero|safe }}
</section>
<section class="w-full">
{{ slots.marquee|safe }}
</section>
<main class="flex-grow">
{% if slots.main %}<div class="max-w-6xl mx-auto px-4 py-16">{{ slots.main|safe }}</div>{% endif %}
</main>
<section class="w-full">
{{ slots.cta|safe }}
</section>
<footer class="w-full mt-auto">
{{ slots.footer|safe }}
</footer>
{{ body_end_html|safe }}
</body>
</html>