Compare commits

..

No commits in common. "486d3a283e6ff1cd750e40ea5caac1e9075bb610" and "9955f5f0020ede29006274cf3fdc3ac97600128c" have entirely different histories.

101 changed files with 1043 additions and 2436 deletions

74
PORT-NOTES.md Normal file
View File

@ -0,0 +1,74 @@
# Gotham — templ → codeless `.bnp` port notes
This theme was ported from the bundled Go/templ `gotham` plugin (source kept in
`port-source/` for reference) to a **codeless** `.bnp` (no Go, no `plugin.wasm`),
matching the `art-deco` reference theme's structure. Every block, page template,
override and the email wrapper is now a host-rendered `.ninjatpl`.
## What maps 1:1 (faithful, no behavior change)
| Source (templ) | Codeless equivalent | Notes |
|---|---|---|
| `RenderGotham` (default) | `templates/gotham/default.ninjatpl` | `bn.Head`/`bn.BodyEnd`/`bn.AdminBypassBanner` replaced by host-injected `{{ head_html }}` / `{{ body_end_html }}` / `{{ admin_banner_html }}`. `<html class="dark">` hardcoded exactly as source (Gotham is always dark). |
| `RenderGothamLanding` | `templates/gotham/landing.ninjatpl` | hero/main/cta/footer slots preserved. |
| `RenderGothamFullWidth` | `templates/gotham/full-width.ninjatpl` | |
| `RenderGothamCentered` | `templates/gotham/centered.ninjatpl` | |
| `FeaturesBlock` + `featuresComponent` | `blocks/features.ninjatpl` | Inline icon SVG switch (chart/users/clock/star/default) ported verbatim as `{% if %}` chains. `columns` grid logic preserved. |
| `FooterBlock` + `footerComponent` | `blocks/footer.ninjatpl` | Column grid + link-URL logic preserved (see PageID note below). |
| `StatsBlock` + `statsComponent` / `statsEmptyComponent` | `blocks/stats.ninjatpl` | Uses the `items` content array (the only path `StatsBlock` ever took). Empty-state placeholder preserved. |
| `GothamHeadingBlock` override | `templates/overrides/gotham/heading.ninjatpl` | Per-level base classes + `text-accent` preserved. |
| `GothamTextBlock` override | `templates/overrides/gotham/text.ninjatpl` | `prose prose-invert prose-amber` preserved. |
| `GothamEmailWrapper` | `templates/email/gotham.ninjatpl` | Var names (`site_name`, `site_url`, `colors.background/card/foreground/muted/mutedForeground/border/primary`, `preview_text`, `logo_url`, `unsubscribe_url`) match the host email-context contract — identical to the `art-deco` reference wrapper. |
| `assets/style.css` | `assets/style.css` | Byte-identical (`gotham-accent`, `gotham-accent-bg`, `gotham-card`, `gotham-glow`, …). Host serves it as a static and links it via `head_html`. |
| `DefaultMasterPages()` | `master_pages.json` | `gotham:default-master` with navbar / slot / `gotham:footer`. |
| `presets.json`, `fonts.json` | unchanged | Carried over as manifest JSON refs. |
## Behavior gaps / decisions the parent should review
1. **`hero` block was NOT registered in the source.** `port-source/hero.go` /
`hero.templ` / `schemas/hero.schema.json` exist, but `register.go` never calls
`br.Register(HeroBlockMeta, ...)`, and `HeroBlock` has the CMS-internal
builtin signature `(ctx, content, _ []string)` — it was **dead code**. Because
the task explicitly asked for a hero port and the `landing` template exposes a
`hero` slot, I ported it faithfully to `blocks/hero.ninjatpl` +
`hero.schema.json` and registered it as `gotham:hero`. **If the parent wants to
match the original registered surface exactly, delete those two files and the
`gotham:hero` entry in `blocks/blocks.yaml`** — nothing else references it.
2. **`stat_item` block dropped (intentionally).** `port-source/stat_item.go`
(`StatItemBlockMeta`, `Hidden: true`) and `statItemComponent` were also never
registered in `register.go`, and `StatsBlock` renders its items inline (never
via a `stat_item` child block). The child-block container path
(`statsContainerComponent` / `gridCols(childCount)`) was likewise unused. No
codeless equivalent was created; the inline stats rendering is fully preserved.
3. **Footer internal-page links are a placeholder, same as source.** The templ
`getLinkURL` emitted `/pages/<page_id>` for a link with `page_id` set, with a
source comment "In production, this would be resolved to the actual page
slug." The codeless template reproduces `/pages/{{ link.page_id }}` verbatim —
it does **not** resolve `page_id` → real slug (the codeless block declares no
data providers, and the source didn't resolve it either). Behavior is
identical to the original; flagging only because it was already a known
limitation.
4. **No computing logic required code.** All templ helper functions
(`gridCols`, `featureGridCols`, `footerGridCols`, `gothamHeadingBaseClass`,
`parseHeadingLevel`, the email `gotham*Color` fallbacks) are pure
presentational branching and were fully expressed with `{% if %}` /
`|default:` in ninjatpl. Nothing in the source needed a wasm guest, so the
artifact is genuinely codeless.
## Not ported (obsolete)
`port-source/` also contains `embed.go`, `registration.go`, `register.go`,
`text_override.go`, `heading_override.go` — Go glue for the bundled/`.so` era.
None have a codeless equivalent (their concerns are now declarations in
`manifest.yaml` / `blocks.yaml` / `master_pages.json`). Delete `port-source/`
once the port is verified.
## Scope / version
- `version` starts at **1.0.0** (graduation from bundled).
- `scope` set to **@ninja** per the port task. NOTE: the sibling reference themes
(`art-deco`, `brutalist`) use `@themes`, and the dev registry has both scopes.
Change `plugin.mod` if `@themes` is preferred.

View File

@ -1,24 +1,36 @@
# Gotham
An after-dark theme in near-black with brass hairline accents. Tall condensed headlines sit over letterboxed hero bars, so pages carry the mood of a private members' club after midnight. It reads exclusive and filmic. The look is cinematic luxe, the kind of page that feels like an invitation.
Dark, modern BlockNinja theme plugin. Bold typography, high-contrast accents,
and a small set of theme-specific blocks (stats, features, footer) plus
overrides for the built-in `heading` and `text` blocks when the Gotham
template is active.
## Good for
## Page templates
We built Gotham for nightlife and hospitality. Treat these as starting points, not limits. If the mood fits your brand, use it:
- `default` — header / main / footer
- `landing` — hero / main / cta / footer
- `full-width` — edge-to-edge header / main / footer
- `centered` — narrow centered content for articles and docs
- Nightclubs and late-night lounges
- Private members' clubs
- Cocktail bars and speakeasies
- Event spaces and ticketed shows
## Blocks
## What you get
- `gotham:stats` — stat row with configurable items
- `gotham:features` — feature grid
- `gotham:footer` — branded footer with optional signup form
- Five color presets, each with a tuned light mode and a tuned dark mode: Velvet Black, Midnight Brass, Noir Rose, Smoke & Silver, and Bourbon.
- Four bundled fonts, ready to assign: Antonio for condensed display headings, Cormorant Garamond for elegant serif text, Inter for interface copy, and IBM Plex Mono. Fonts stay admin-controlled, so you can swap them from the typography settings.
- Four persona blocks: a door strip for guest-list and entry details, an events marquee, a membership-tiers section, and a bottle-service menu.
- A demo site that installs when you activate the theme: a home page, an about page, a short blog with three posts, 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.
## Build
## Install
```
make # builds gotham.so locally
make rebuild # rebuilds plugin inside an instance container
```
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.
## Versioning
```
make bump-patch # 0.1.0 -> 0.1.1
make bump-minor # 0.1.0 -> 0.2.0
make bump-major # 0.1.0 -> 1.0.0
```
Then `git push --tags` and `ninja plugin publish` to release.

View File

@ -1,29 +0,0 @@
# Recommended fonts — Gotham
Gotham is admin-font-controlled. Every family is consumed through
`var(--font-heading|body|mono, <fallback>)`, so nothing breaks if you assign a
different family. These are the intended stacks.
Four OFL families ship bundled in `assets/fonts/` (see `LICENSES.md`) and appear
in the admin font picker as `source=template` rows. Assign them in
Admin -> Appearance -> Typography.
| Role | Variable | Assign | Bundled | Fallback |
|------|----------|--------|---------|----------|
| Headings | `--font-heading` | **Antonio** (or Oswald / Bebas Neue) | yes | `'Antonio', 'Oswald', 'Bebas Neue', sans-serif` |
| Body | `--font-body` | **Cormorant Garamond** | yes | `'Cormorant Garamond', 'Cormorant', Georgia, serif` |
| Meta / labels | `--font-body` accents | **Inter** | yes | `Inter, system-ui, sans-serif` |
| Mono | `--font-mono` | **IBM Plex Mono** | yes | `'IBM Plex Mono', ui-monospace, monospace` |
## Why these
- **Antonio** is a tall condensed sans. It carries the letterboxed marquee
headlines. Oswald and Bebas Neue are the intended substitutes if you prefer to
assign from the Google Fonts tab.
- **Cormorant Garamond** is the elegant serif body voice — the invitation card,
not the neon sign.
- **Inter** handles small meta: labels, eyebrows, form fields.
- **IBM Plex Mono** covers any monospace surface (times, codes, run sheets).
Keep the condensed display and the serif body paired. That contrast is the
theme's typographic signature.

View File

@ -1,21 +0,0 @@
# Recommended icons — Gotham
Gotham renders icons from the sprite system only, as
`<svg><use href="/icons/<pack>.svg#<name>"/></svg>` (or the `::pack:name:size::`
template shorthand). It never inlines SVG paths.
## Required pack
- **Lucide** (`lucide`) — the theme's default pack. Declared in
`plugin.mod` `required_icon_packs = ["lucide"]`.
Lucide ships bundled with the CMS, so no manual install is normally needed. If a
theme icon renders empty, open `/admin/icons`, confirm **Lucide** is installed
and enabled, then reactivate the theme.
## Icons the theme references
`lucide:diamond`, `lucide:arrow-right`, `lucide:check`, `lucide:chevron-right`
appear in seed content and block defaults. All resolve from the bundled Lucide
pack. Any icon-picker field defaults to a Lucide glyph so the theme renders
without further setup.

0
assets/fonts/.gitkeep Normal file
View File

View File

@ -1,16 +0,0 @@
# Bundled font licenses
All bundled woff2 files in this directory are latin-subset builds redistributed
under the SIL Open Font License 1.1 (OFL). No modifications were made beyond
subsetting to the latin range.
| Family | Files | License | Source |
|--------|-------|---------|--------|
| Antonio | antonio-latin.woff2, antonio-latin-ext.woff2 | SIL OFL 1.1 | Vernon Adams / Google Fonts |
| Cormorant Garamond | cormorant-garamond-400.woff2, cormorant-garamond-400-italic.woff2, cormorant-garamond-600.woff2, cormorant-garamond-700.woff2 | SIL OFL 1.1 | Christian Thalmann / Google Fonts |
| Inter | inter-400.woff2, inter-500.woff2, inter-700.woff2 | SIL OFL 1.1 | Rasmus Andersson / Google Fonts |
| IBM Plex Mono | ibm-plex-mono-400.woff2, ibm-plex-mono-500.woff2 | SIL OFL 1.1 | IBM / Google Fonts |
Full OFL text: https://openfontlicense.org/. Font-family is still admin-controlled;
these bundled families appear as `source=template` rows in the fonts picker and
are consumed only via `var(--font-heading|body|mono, <fallback>)`.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
/* placeholder for fonts */

33
assets/style.css Normal file
View File

@ -0,0 +1,33 @@
/* Gotham template styles - uses theme CSS variables */
/* Accent color classes - mapped to theme primary */
.gotham-accent {
color: hsl(var(--primary));
}
.gotham-accent-bg {
background-color: hsl(var(--primary));
}
.gotham-accent-bg:hover {
background-color: hsl(var(--primary) / 0.9);
}
/* Border accent */
.gotham-border-accent {
border-color: hsl(var(--primary));
}
/* Additional Gotham-specific utilities */
.gotham-gradient {
background: linear-gradient(180deg, hsl(var(--background)) 0%, hsl(var(--card)) 100%);
}
.gotham-card {
background-color: hsl(var(--card));
border: 1px solid hsl(var(--border));
}
.gotham-glow {
box-shadow: 0 0 20px hsl(var(--primary) / 0.3);
}

View File

@ -1,32 +1,26 @@
# Gotham theme block definitions (codeless .bnp, WO-TF-018).
# Only TRUE persona furniture remains as theme blocks. Everything that
# duplicated a builtin (hero, stats, feature grid, footer, gallery, divider,
# quote, testimonial...) is now a template OVERRIDE of the builtin key under
# templates/overrides/gotham/. Keys are fully qualified (gotham:<key>) — the
# codeless loader registers definitions verbatim; the qualified keys match the
# data-block attributes, master_pages.json and seed references.
# Gotham theme block definitions (codeless .bnp, WO-WZ-021).
# Keys are fully qualified (gotham:<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
# master_pages.json references.
blocks:
- key: gotham:door_strip
title: Door Strip
description: Sticky near-black door strip with brass rules — reservations line, guest-list CTA and tonight's hours.
category: navigation
schema: door_strip.schema.json
template: door_strip.ninjatpl
- key: gotham:events_marquee
title: Events Marquee
description: Letterboxed line-up of nights and residencies, each with date, room and a booking link.
category: content
schema: events_marquee.schema.json
template: events_marquee.ninjatpl
- key: gotham:membership_tiers
title: Membership Tiers
description: Side-by-side membership cards with a brass-framed feature list and an apply link.
category: content
schema: membership_tiers.schema.json
template: membership_tiers.ninjatpl
- key: gotham:bottle_menu
title: Bottle Menu
description: Cellar and bottle-service list grouped into sections, each pour with a note and price.
category: content
schema: bottle_menu.schema.json
template: bottle_menu.ninjatpl
- key: gotham:hero
title: Hero Section
description: Large hero section with headline and optional CTA
schema: hero.schema.json
template: hero.ninjatpl
- key: gotham:stats
title: Stats Counter
description: Display statistics with labels and values
schema: stats.schema.json
template: stats.ninjatpl
- key: gotham:features
title: Feature Cards
description: Grid of feature cards with icons and descriptions
schema: features.schema.json
template: features.ninjatpl
- key: gotham:footer
title: Footer
description: Multi-column footer with links and copyright
schema: footer.schema.json
template: footer.ninjatpl

View File

@ -1,29 +0,0 @@
{# bottle_menu — cellar list. sections[] and section.items[] are arrays of maps. #}
<section data-block="gotham:bottle_menu" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-4xl px-4">
<div class="text-center reveal">
{% if eyebrow %}<div class="mb-4 flex items-center justify-center gap-4"><span class="reel-rule w-10"></span><span class="reel-caps text-xs text-primary">{{ eyebrow }}</span><span class="reel-rule w-10"></span></div>{% endif %}
{% if title %}<h2 class="reel-caps text-3xl md:text-4xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ title }}</h2>{% endif %}
</div>
{% if sections %}
<div class="mt-12 space-y-12">
{% for section in sections %}
<div class="reveal">
{% if section.title %}<h3 class="reel-caps reel-underline w-fit text-lg text-primary" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);">{{ section.title }}</h3>{% endif %}
<ul class="mt-5 space-y-4">
{% for pour in section.items %}
<li class="flex items-baseline gap-4">
<span class="shrink-0" style="font-family:var(--font-body, 'Cormorant Garamond', Georgia, serif);color:hsl(var(--foreground));font-size:1.05rem;">{{ pour.name }}</span>
<span class="reel-rule flex-1 translate-y-[-2px]" style="opacity:0.4;"></span>
{% if pour.price %}<span class="shrink-0 reel-caps text-sm text-primary">{{ pour.price }}</span>{% endif %}
{% if pour.note %}<span class="hidden shrink-0 basis-full text-sm italic sm:block sm:basis-auto" style="color:hsl(var(--muted-foreground));">{{ pour.note }}</span>{% endif %}
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
</div>
{% endif %}
{% if footnote %}<p class="mt-10 text-center text-sm italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Cormorant Garamond', Georgia, serif);">{{ footnote }}</p>{% endif %}
</div>
</section>

View File

@ -1,36 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Bottle Menu",
"description": "Cellar and bottle-service list grouped into sections.",
"type": "object",
"properties": {
"eyebrow": { "type": "string", "title": "Eyebrow", "x-editor": "text", "default": "The cellar" },
"title": { "type": "string", "title": "Section title", "x-editor": "text", "default": "Bottle service" },
"footnote": { "type": "string", "title": "Footnote", "x-editor": "text", "default": "" },
"sections": {
"type": "array",
"title": "Sections",
"x-editor": "array",
"default": [],
"items": {
"type": "object",
"properties": {
"title": { "type": "string", "title": "Section title", "x-editor": "text" },
"items": {
"type": "array",
"title": "Pours",
"x-editor": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string", "title": "Name", "x-editor": "text" },
"note": { "type": "string", "title": "Note", "x-editor": "text" },
"price": { "type": "string", "title": "Price", "x-editor": "text" }
}
}
}
}
}
}
}
}

View File

@ -1,11 +0,0 @@
{# door_strip — sticky brass-on-black door strip. Iterates hours[] (array of strings). #}
<div data-block="gotham:door_strip" class="sticky top-0 z-30 w-full film-grain reel-strip" style="background-color: hsl(var(--foreground)); color: hsl(var(--background));">
<div class="max-w-6xl mx-auto px-4 py-2 flex flex-wrap items-center justify-between gap-3 text-sm">
<div class="flex items-center gap-3 reel-caps text-xs" style="font-family: var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif); color: hsl(var(--primary));">
<span>{{ label|default:"The Door" }}</span>
{% if phone %}<span aria-hidden="true" style="color: hsl(var(--primary) / 0.5);">/</span><a href="tel:{{ phone }}" class="reel-link" style="border-bottom-color: hsl(var(--primary));">{{ phone }}</a>{% endif %}
</div>
{% if hours %}<div class="hidden sm:flex flex-wrap items-center gap-3" style="color: hsl(var(--background));">{% for h in hours %}{% if not forloop.First %}<span aria-hidden="true" style="color: hsl(var(--primary) / 0.5);">/</span>{% endif %}<span>{{ h.line }}</span>{% endfor %}</div>{% endif %}
{% if reserveUrl %}<a href="{{ reserveUrl }}" class="reel-btn" style="background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground)); border-color: hsl(var(--primary));">{{ reserveLabel|default:"Guest list" }}</a>{% endif %}
</div>
</div>

View File

@ -1,48 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Door Strip",
"description": "Sticky near-black door strip with brass rules, reservations line, guest-list CTA and tonight's hours.",
"type": "object",
"properties": {
"label": {
"type": "string",
"title": "Strip label",
"description": "Short label at the head of the strip, e.g. 'The Door'.",
"x-editor": "text",
"default": "The Door"
},
"phone": {
"type": "string",
"title": "Reservations phone",
"description": "International-format number for the reservations desk.",
"x-editor": "text",
"default": ""
},
"reserveLabel": {
"type": "string",
"title": "Reserve button label",
"x-editor": "text",
"default": "Guest list"
},
"reserveUrl": {
"type": "string",
"title": "Reserve / guest-list URL",
"description": "Link to the booking or guest-list page.",
"x-editor": "link",
"default": ""
},
"hours": {
"type": "array",
"title": "Door hours",
"description": "Free-form lines such as 'Thu-Sat - 9pm till late'.",
"x-editor": "array",
"default": [],
"items": {
"type": "object",
"properties": {
"line": { "type": "string", "title": "Line", "x-editor": "text" }
}
}
}
}
}

View File

@ -1,24 +0,0 @@
{# events_marquee — letterboxed line-up. Iterates events[] (array of maps). #}
<section data-block="gotham:events_marquee" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-5xl px-4">
<div class="text-center reveal">
{% if eyebrow %}<div class="mb-4 flex items-center justify-center gap-4"><span class="reel-rule w-10"></span><span class="reel-caps text-xs text-primary">{{ eyebrow }}</span><span class="reel-rule w-10"></span></div>{% endif %}
{% if title %}<h2 class="reel-caps text-3xl md:text-4xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ title }}</h2>{% endif %}
</div>
{% if events %}
<ul class="mt-10 divide-y divide-[hsl(var(--border))] border-y border-[hsl(var(--border))]">
{% for event in events %}
<li class="reveal grid grid-cols-1 gap-1 py-5 sm:grid-cols-[8rem_1fr_auto] sm:items-baseline sm:gap-6">
<span class="reel-caps text-sm text-primary" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);">{{ event.date }}</span>
<div>
<span class="reel-caps text-lg" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ event.name }}</span>
{% if event.room %}<span class="ml-3 text-sm" style="color:hsl(var(--muted-foreground));">{{ event.room }}</span>{% endif %}
{% if event.note %}<p class="mt-1 text-sm italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Cormorant Garamond', Georgia, serif);">{{ event.note }}</p>{% endif %}
</div>
{% if event.url %}<a href="{{ event.url }}" class="reel-link inline-flex items-center gap-1 justify-self-start text-sm sm:justify-self-end">Book::lucide:arrow-right:sm::</a>{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
</div>
</section>

View File

@ -1,36 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Events Marquee",
"description": "Letterboxed line-up of nights and residencies.",
"type": "object",
"properties": {
"eyebrow": {
"type": "string",
"title": "Eyebrow",
"x-editor": "text",
"default": "The programme"
},
"title": {
"type": "string",
"title": "Section title",
"x-editor": "text",
"default": "Tonight and after"
},
"events": {
"type": "array",
"title": "Events",
"x-editor": "array",
"default": [],
"items": {
"type": "object",
"properties": {
"date": { "type": "string", "title": "Date", "x-editor": "text" },
"name": { "type": "string", "title": "Name", "x-editor": "text" },
"room": { "type": "string", "title": "Room", "x-editor": "text" },
"note": { "type": "string", "title": "Note", "x-editor": "text" },
"url": { "type": "string", "title": "Booking URL", "x-editor": "link" }
}
}
}
}
}

1
blocks/features.ninjatpl Normal file
View File

@ -0,0 +1 @@
{% if features %}<section class="py-20 flex-1"><div class="max-w-6xl mx-auto px-4">{% if section_title %}<h2 class="text-3xl font-bold text-center mb-12 gotham-accent">{{ section_title }}</h2>{% endif %}<div class="grid gap-8 {% if columns|integer == 1 %}grid-cols-1{% elif columns|integer == 2 %}grid-cols-1 md:grid-cols-2{% elif columns|integer == 4 %}grid-cols-1 md:grid-cols-2 lg:grid-cols-4{% else %}grid-cols-1 md:grid-cols-2 lg:grid-cols-3{% endif %}">{% for feature in features %}<div class="p-6 gotham-surface rounded-lg gotham-border border hover:border-primary/30 transition-colors">{% if feature.icon %}<div class="mb-4 gotham-accent">{% if feature.icon == "chart" %}<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10 mx-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg>{% elif feature.icon == "users" %}<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10 mx-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"></path></svg>{% elif feature.icon == "clock" %}<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10 mx-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>{% elif feature.icon == "star" %}<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10 mx-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z"></path></svg>{% else %}<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10 mx-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path></svg>{% endif %}</div>{% endif %}<h3 class="text-xl font-semibold text-foreground mb-3">{{ feature.title }}</h3>{% if feature.description %}<p class="text-muted-foreground leading-relaxed">{{ feature.description }}</p>{% endif %}</div>{% endfor %}</div></div></section>{% endif %}

View File

@ -0,0 +1,55 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Features Block",
"description": "Grid of feature cards with icons and descriptions",
"type": "object",
"properties": {
"section_title": {
"type": "string",
"title": "Section Title",
"description": "Optional title above the feature cards",
"x-editor": "text"
},
"columns": {
"type": "integer",
"title": "Columns",
"description": "Number of columns (1-4)",
"x-editor": "number",
"minimum": 1,
"maximum": 4,
"default": 3
},
"features": {
"type": "array",
"title": "Features",
"description": "List of feature cards",
"default": [],
"x-editor": "array",
"items": {
"type": "object",
"properties": {
"icon": {
"type": "string",
"title": "Icon",
"description": "Icon name (chart, users, clock, star)",
"x-editor": "select",
"enum": ["", "chart", "users", "clock", "star"]
},
"title": {
"type": "string",
"title": "Title",
"description": "Feature title",
"x-editor": "text"
},
"description": {
"type": "string",
"title": "Description",
"description": "Feature description",
"x-editor": "textarea"
}
},
"required": ["title"]
}
}
}
}

1
blocks/footer.ninjatpl Normal file
View File

@ -0,0 +1 @@
<div class="max-w-6xl mx-auto px-4">{% if columns %}<div class="grid gap-8 mb-12 {% if columns|length == 1 %}grid-cols-1{% elif columns|length == 2 %}grid-cols-1 md:grid-cols-2{% elif columns|length == 3 %}grid-cols-1 md:grid-cols-3{% else %}grid-cols-1 md:grid-cols-2 lg:grid-cols-4{% endif %}">{% for col in columns %}<div>{% if col.heading %}<h4 class="text-lg font-semibold gotham-accent mb-4">{{ col.heading }}</h4>{% endif %}{% if col.links %}<ul class="space-y-2">{% for link in col.links %}<li><a href="{% if link.page_id %}/pages/{{ link.page_id }}{% elif link.url %}{{ link.url }}{% else %}#{% endif %}" class="text-muted-foreground hover:text-foreground transition-colors">{{ link.text }}</a></li>{% endfor %}</ul>{% endif %}</div>{% endfor %}</div>{% endif %}{% if copyright %}<div class="pt-8 gotham-border border-t text-center"><p class="text-muted-foreground text-sm">{{ copyright }}</p></div>{% endif %}</div>

63
blocks/footer.schema.json Normal file
View File

@ -0,0 +1,63 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Footer Block",
"description": "Multi-column footer with links and copyright",
"type": "object",
"properties": {
"copyright": {
"type": "string",
"title": "Copyright Text",
"description": "Copyright notice displayed at the bottom",
"default": "© 2025 Your Company. All rights reserved.",
"x-editor": "text"
},
"columns": {
"type": "array",
"title": "Footer Columns",
"description": "Columns with headings and links",
"x-editor": "array",
"items": {
"type": "object",
"properties": {
"heading": {
"type": "string",
"title": "Column Heading",
"description": "Title for this column",
"x-editor": "text"
},
"links": {
"type": "array",
"title": "Links",
"description": "Links in this column",
"x-editor": "array",
"items": {
"type": "object",
"properties": {
"text": {
"type": "string",
"title": "Link Text",
"description": "Display text for the link",
"x-editor": "text"
},
"page_id": {
"type": "string",
"title": "Page",
"description": "Select an internal page (overrides URL)",
"x-editor": "page-select"
},
"url": {
"type": "string",
"title": "External URL",
"description": "External URL (used if no page selected)",
"x-editor": "url"
}
},
"required": ["text"]
}
}
},
"required": ["heading"]
}
}
}
}

1
blocks/hero.ninjatpl Normal file
View File

@ -0,0 +1 @@
<section class="relative py-32 overflow-hidden">{% if background_url %}<div class="absolute inset-0 z-0"><img src="{{ background_url }}" alt="" class="w-full h-full object-cover opacity-20"><div class="absolute inset-0 bg-gradient-to-b from-background/80 via-background/60 to-background"></div></div>{% endif %}<div class="relative z-10 max-w-4xl mx-auto px-4 text-center"><h1 class="text-5xl md:text-6xl font-bold mb-6"><span class="gotham-accent">{{ headline }}</span></h1>{% if subheadline %}<p class="text-xl md:text-2xl text-muted-foreground mb-10 max-w-2xl mx-auto">{{ subheadline }}</p>{% endif %}{% if cta_text and cta_url %}<a href="{{ cta_url }}" class="inline-block px-8 py-4 gotham-accent-bg text-primary-foreground font-semibold rounded-lg transition-colors text-lg">{{ cta_text }}</a>{% endif %}</div></section>

39
blocks/hero.schema.json Normal file
View File

@ -0,0 +1,39 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Hero Block",
"description": "Large hero section with headline and optional CTA",
"type": "object",
"properties": {
"headline": {
"type": "string",
"title": "Headline",
"description": "Main headline text",
"x-editor": "text"
},
"subheadline": {
"type": "string",
"title": "Subheadline",
"description": "Supporting text below the headline",
"x-editor": "textarea"
},
"background_url": {
"type": "string",
"title": "Background Image URL",
"description": "URL for the background image",
"x-editor": "url"
},
"cta_text": {
"type": "string",
"title": "CTA Button Text",
"description": "Text for the call-to-action button",
"x-editor": "text"
},
"cta_url": {
"type": "string",
"title": "CTA Button URL",
"description": "Link for the call-to-action button",
"x-editor": "url"
}
},
"required": ["headline"]
}

View File

@ -1,35 +0,0 @@
{# membership_tiers — brass-framed membership cards. tiers[] and tier.features[] are arrays of maps. #}
<section data-block="gotham:membership_tiers" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-6xl px-4">
<div class="text-center reveal">
{% if eyebrow %}<div class="mb-4 flex items-center justify-center gap-4"><span class="reel-rule w-10"></span><span class="reel-caps text-xs text-primary">{{ eyebrow }}</span><span class="reel-rule w-10"></span></div>{% endif %}
{% if title %}<h2 class="reel-caps text-3xl md:text-4xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ title }}</h2>{% endif %}
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-lg italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Cormorant Garamond', Georgia, serif);">{{ intro }}</p>{% endif %}
</div>
{% if tiers %}
<div class="mt-12 grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{% for tier in tiers %}
<article class="reveal reel-frame flex flex-col bg-card text-card-foreground {% if tier.featured %}reel-foil{% endif %}">
<div class="flex items-baseline justify-between gap-2">
<h3 class="reel-caps text-xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ tier.name }}</h3>
{% if tier.featured %}<span class="reel-caps text-[0.6rem] text-primary">House pick</span>{% endif %}
</div>
<div class="mt-3 flex items-baseline gap-1">
<span class="text-3xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--primary));">{{ tier.price }}</span>
{% if tier.cadence %}<span class="text-sm" style="color:hsl(var(--muted-foreground));">{{ tier.cadence }}</span>{% endif %}
</div>
{% if tier.description %}<p class="mt-3 text-sm leading-relaxed" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Cormorant Garamond', Georgia, serif);">{{ tier.description }}</p>{% endif %}
{% if tier.features %}
<ul class="mt-5 space-y-2 border-t border-[hsl(var(--border))] pt-5 text-sm">
{% for feature in tier.features %}
<li class="flex items-start gap-2"><span class="mt-0.5 shrink-0 text-primary">::lucide:check:sm::</span><span style="color:hsl(var(--foreground));">{{ feature.label }}</span></li>
{% endfor %}
</ul>
{% endif %}
{% if tier.applyUrl %}<a href="{{ tier.applyUrl }}" class="reel-btn mt-6 justify-center">{{ tier.applyLabel|default:"Apply" }}</a>{% endif %}
</article>
{% endfor %}
</div>
{% endif %}
</div>
</section>

View File

@ -1,40 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Membership Tiers",
"description": "Side-by-side membership cards with a brass-framed feature list.",
"type": "object",
"properties": {
"eyebrow": { "type": "string", "title": "Eyebrow", "x-editor": "text", "default": "Membership" },
"title": { "type": "string", "title": "Section title", "x-editor": "text", "default": "Join the house" },
"intro": { "type": "string", "title": "Intro", "x-editor": "textarea", "default": "" },
"tiers": {
"type": "array",
"title": "Tiers",
"x-editor": "array",
"default": [],
"items": {
"type": "object",
"properties": {
"name": { "type": "string", "title": "Name", "x-editor": "text" },
"price": { "type": "string", "title": "Price", "x-editor": "text" },
"cadence": { "type": "string", "title": "Cadence", "x-editor": "text" },
"description": { "type": "string", "title": "Description", "x-editor": "textarea" },
"featured": { "type": "boolean", "title": "Featured", "x-editor": "boolean", "default": false },
"applyLabel": { "type": "string", "title": "Apply label", "x-editor": "text" },
"applyUrl": { "type": "string", "title": "Apply URL", "x-editor": "link" },
"features": {
"type": "array",
"title": "Features",
"x-editor": "array",
"items": {
"type": "object",
"properties": {
"label": { "type": "string", "title": "Label", "x-editor": "text" }
}
}
}
}
}
}
}
}

1
blocks/stats.ninjatpl Normal file
View File

@ -0,0 +1 @@
{% if items %}<section class="py-16 gotham-surface flex-1"><div class="max-w-6xl mx-auto px-4"><div class="grid gap-8 {% if items|length == 1 %}grid-cols-1{% elif items|length == 2 %}grid-cols-1 md:grid-cols-2{% elif items|length == 3 %}grid-cols-1 md:grid-cols-3{% else %}grid-cols-1 md:grid-cols-2 lg:grid-cols-4{% endif %}">{% for stat in items %}<div class="text-center p-6 rounded-lg gotham-border border">{% if stat.icon %}<div class="mb-3 gotham-accent">{% if stat.icon == "chart" %}<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10 mx-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg>{% elif stat.icon == "users" %}<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10 mx-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"></path></svg>{% elif stat.icon == "clock" %}<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10 mx-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>{% elif stat.icon == "star" %}<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10 mx-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z"></path></svg>{% else %}<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10 mx-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path></svg>{% endif %}</div>{% endif %}<div class="text-4xl font-bold gotham-accent mb-2">{{ stat.value }}</div><div class="text-muted-foreground uppercase text-sm tracking-wider">{{ stat.label }}</div></div>{% endfor %}</div></div></section>{% else %}<section class="py-16 gotham-surface flex-1"><div class="max-w-6xl mx-auto px-4"><div class="text-center p-8 border border-dashed border-border rounded-lg"><p class="text-muted-foreground">Click + to add stat items</p></div></div></section>{% endif %}

40
blocks/stats.schema.json Normal file
View File

@ -0,0 +1,40 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Stats Block",
"description": "Display statistics with labels and values",
"type": "object",
"properties": {
"items": {
"type": "array",
"title": "Statistics",
"description": "List of statistic items to display",
"x-editor": "array",
"items": {
"type": "object",
"properties": {
"value": {
"type": "string",
"title": "Value",
"description": "The statistic value (e.g., '100+', '99%')",
"x-editor": "text"
},
"label": {
"type": "string",
"title": "Label",
"description": "Description of the statistic",
"x-editor": "text"
},
"icon": {
"type": "string",
"title": "Icon",
"description": "Icon name (chart, users, clock, star)",
"x-editor": "select",
"enum": ["", "chart", "users", "clock", "star"]
}
},
"required": ["value", "label"]
}
}
},
"required": []
}

View File

@ -1,38 +1,88 @@
[
{
"name": "Antonio",
"family": "Antonio",
"name": "Gotham",
"family": "Gotham",
"variants": [
{ "weight": "400", "style": "normal", "file": "fonts/antonio-latin.woff2" },
{ "weight": "600", "style": "normal", "file": "fonts/antonio-latin.woff2" },
{ "weight": "700", "style": "normal", "file": "fonts/antonio-latin.woff2" }
]
{
"weight": "100",
"style": "normal",
"file": "fonts/web/gothamthin-webfont.woff2"
},
{
"name": "Cormorant Garamond",
"family": "Cormorant Garamond",
"variants": [
{ "weight": "400", "style": "normal", "file": "fonts/cormorant-garamond-400.woff2" },
{ "weight": "400", "style": "italic", "file": "fonts/cormorant-garamond-400-italic.woff2" },
{ "weight": "600", "style": "normal", "file": "fonts/cormorant-garamond-600.woff2" },
{ "weight": "700", "style": "normal", "file": "fonts/cormorant-garamond-700.woff2" }
]
"weight": "100",
"style": "italic",
"file": "fonts/web/gothamthinitalic-webfont.woff2"
},
{
"name": "Inter",
"family": "Inter",
"variants": [
{ "weight": "400", "style": "normal", "file": "fonts/inter-400.woff2" },
{ "weight": "500", "style": "normal", "file": "fonts/inter-500.woff2" },
{ "weight": "700", "style": "normal", "file": "fonts/inter-700.woff2" }
]
"weight": "200",
"style": "normal",
"file": "fonts/web/gothamxlight-webfont.woff2"
},
{
"name": "IBM Plex Mono",
"family": "IBM Plex Mono",
"variants": [
{ "weight": "400", "style": "normal", "file": "fonts/ibm-plex-mono-400.woff2" },
{ "weight": "500", "style": "normal", "file": "fonts/ibm-plex-mono-500.woff2" }
"weight": "200",
"style": "italic",
"file": "fonts/web/gothamxlightitalic-webfont.woff2"
},
{
"weight": "300",
"style": "normal",
"file": "fonts/web/gothamlight-webfont.woff2"
},
{
"weight": "300",
"style": "italic",
"file": "fonts/web/gothamlightitalic-webfont.woff2"
},
{
"weight": "400",
"style": "normal",
"file": "fonts/web/gothambook-webfont.woff2"
},
{
"weight": "400",
"style": "italic",
"file": "fonts/web/gothambookitalic-webfont.woff2"
},
{
"weight": "500",
"style": "normal",
"file": "fonts/web/gothammedium-webfont.woff2"
},
{
"weight": "500",
"style": "italic",
"file": "fonts/web/gothammediumitalic-webfont.woff2"
},
{
"weight": "700",
"style": "normal",
"file": "fonts/web/gothambold-webfont.woff2"
},
{
"weight": "700",
"style": "italic",
"file": "fonts/web/gothambolditalic-webfont.woff2"
},
{
"weight": "800",
"style": "normal",
"file": "fonts/web/gothamblack-webfont.woff2"
},
{
"weight": "800",
"style": "italic",
"file": "fonts/web/gothamblackitalic-webfont.woff2"
},
{
"weight": "900",
"style": "normal",
"file": "fonts/web/gothamultra-webfont.woff2"
},
{
"weight": "900",
"style": "italic",
"file": "fonts/web/gothamultraitalic-webfont.woff2"
}
]
}
]

View File

@ -1,10 +1,9 @@
# Gotham theme — codeless .bnp manifest (WO-TF-018, Wave A).
# Gotham theme — codeless .bnp manifest (WO-WZ-021 Phase B).
# Synthesized into manifest.pb by `ninja plugin build` (no plugin.wasm).
#
# Nightlife / private-club identity: cinematic near-black luxe, brass hairline
# accents, letterboxed heroes, tall condensed display type. Utility CSS lives in
# `css.input_css_append` below (host Tailwind pipeline) — colour is always
# hsl(var(--token)) and font-family always var(--font-heading|body|mono, ...).
# No `css:` section: the wasm/templ Gotham shipped no CSSManifest — its utility
# CSS lives in assets/style.css, which the host serves as a static asset and
# links into <head> automatically (head_html) when assets/style.css exists.
theme_presets: presets.json
bundled_fonts: fonts.json
master_pages: master_pages.json
@ -12,231 +11,33 @@ master_pages: master_pages.json
system_templates:
- key: gotham
title: Gotham
description: "After-dark identity for a nightlife venue or private members' club — cinematic near-black luxe, brass accents and condensed display type."
description: "Dark, modern theme with bold typography"
page_templates:
- system: gotham
key: default
title: Default
description: "Centred near-black page with brass rails, header, main and footer."
description: "Standard dark page layout with header, main content, and footer"
slots: [header, main, footer]
- system: gotham
key: landing
title: Marquee Landing
description: "Letterboxed hero, door strip and a closing CTA for the main room."
title: Landing Page
description: "Full-width hero sections, ideal for marketing pages"
slots: [hero, main, cta, footer]
- system: gotham
key: article
title: Feature
description: "Narrow editorial column for press, listings and the house journal."
slots: [header, main, aside, footer]
- system: gotham
key: full-width
title: Grand Room
description: "Edge-to-edge gallery layout for the room, lookbooks and galleries."
title: Full Width
description: "Edge-to-edge content without max-width constraints"
slots: [header, main, footer]
- system: gotham
key: blog-index
title: The Journal
description: "Blog index with a brass masthead, featured and post-grid slots."
slots: [header, featured, main, footer]
- system: gotham
key: contact
title: Reservations
description: "Contact layout with an optional aside for hours and the door."
slots: [header, main, aside, footer]
- system: gotham
key: auth
title: Members
description: "Centred members' shell with wordmark and a spotlight crest."
slots: [main, footer]
- system: gotham
key: centered
title: Centered
description: "Narrow centred column for documents and single statements."
title: Centered Content
description: "Narrow, centered content for articles and documentation"
slots: [header, main, footer]
template_overrides:
# Chrome (4)
- { template: gotham, block: navbar }
- { template: gotham, block: footer }
- { template: gotham, block: utility-bar }
- { template: gotham, block: announcement-bar }
# Marketing (12)
- { template: gotham, block: hero }
- { template: gotham, block: feature-grid }
- { template: gotham, block: rich-section }
- { template: gotham, block: stats }
- { template: gotham, block: testimonial }
- { template: gotham, block: logo-strip }
- { template: gotham, block: pricing }
- { template: gotham, block: team }
- { template: gotham, block: faq }
- { template: gotham, block: timeline }
- { template: gotham, block: cta }
- { template: gotham, block: countdown }
# Primitives (11)
- { template: gotham, block: heading }
- { template: gotham, block: text }
- { template: gotham, block: rich-text }
- { template: gotham, block: image }
- { template: gotham, block: quote }
- { template: gotham, block: divider }
- { template: gotham, block: button }
- { template: gotham, block: card }
- { template: gotham, block: gallery }
- { template: gotham, block: video-embed }
- { template: gotham, block: audio-embed }
# Commerce / local (6)
- { template: gotham, block: menu-list }
- { template: gotham, block: event-list }
- { template: gotham, block: contact-form }
- { template: gotham, block: contact-card }
- { template: gotham, block: hours-location }
- { template: gotham, block: social-links }
# Blog family (12)
- { template: gotham, block: blog-hero }
- { template: gotham, block: featured-posts }
- { template: gotham, block: related-posts }
- { template: gotham, block: popular-posts }
- { template: gotham, block: category-list }
- { template: gotham, block: post-hero }
- { template: gotham, block: post-metadata }
- { template: gotham, block: author-bio }
- { template: gotham, block: author-bio-hero }
- { template: gotham, block: newsletter-signup }
- { template: gotham, block: breadcrumbs }
- { template: gotham, block: sidebar-nav }
# Auth surface (3)
- { template: gotham, block: auth-form }
- { template: gotham, block: password-reset-form }
- { template: gotham, block: auth-status }
# System (1)
- { template: gotham, block: page-suggestions }
email_wrappers:
- gotham
css:
input_css_append: |
/* Gotham theme — cinematic near-black utility CSS.
*
* Colour is always consumed via hsl(var(--token)); font families always
* come from var(--font-heading|body|mono, <fallback>). No literal colours
* except black scrims layered over IMAGES (letterbox effect).
*/
@layer utilities {
/* reel-rule — brass hairline, 1px ascending to 2px. */
.reel-rule { border: none; border-top: 1px solid hsl(var(--primary)); margin: 0; }
.reel-rule-thick { border: none; border-top: 2px solid hsl(var(--primary)); margin: 0; }
/* reel-frame — thin film frame with brass corner ticks. */
.reel-frame { position: relative; border: 1px solid hsl(var(--border)); padding: 1.25rem 1.5rem; }
.reel-frame::before, .reel-frame::after {
content: ""; position: absolute; width: 14px; height: 14px;
border-color: hsl(var(--primary)); border-style: solid; pointer-events: none;
}
.reel-frame::before { top: -1px; left: -1px; border-width: 1px 0 0 1px; }
.reel-frame::after { bottom: -1px; right: -1px; border-width: 0 1px 1px 0; }
/* reel-foil — inset brass glow used on hover / featured states. */
.reel-foil {
box-shadow:
inset 0 0 0 1px hsl(var(--primary)),
inset 0 1px 0 hsl(var(--primary) / 0.5),
inset 0 -1px 0 hsl(var(--primary) / 0.3);
}
/* reel-caps — condensed caps tracking for display headings. */
.reel-caps { text-transform: uppercase; letter-spacing: 0.12em; }
/* reel-underline — brass hairline underline on headings. */
.reel-underline { position: relative; padding-bottom: 0.35em; }
.reel-underline::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: hsl(var(--primary)); }
/* reel-btn — the invitation. Brass on near-black, inverts on hover. */
.reel-btn {
display: inline-flex; align-items: center; gap: 0.5rem;
border: 1px solid hsl(var(--primary));
background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground));
padding: 0.7rem 1.7rem; border-radius: 2px;
text-transform: uppercase; letter-spacing: 0.14em; font-size: 0.8rem; line-height: 1;
transition: background-color 200ms ease, color 200ms ease, box-shadow 200ms ease;
}
.reel-btn:hover, .reel-btn:focus-visible {
background-color: transparent; color: hsl(var(--primary));
box-shadow: inset 0 0 0 1px hsl(var(--primary));
}
.reel-btn:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 3px; }
/* reel-link — brass underline link. */
.reel-link { color: hsl(var(--primary)); text-decoration: none; border-bottom: 1px solid hsl(var(--primary) / 0.5); transition: color 200ms ease, border-color 200ms ease; }
.reel-link:hover, .reel-link:focus-visible { color: hsl(var(--accent)); border-bottom-color: hsl(var(--accent)); }
/* film-grain — 4% film-grain noise, dark surfaces. */
.film-grain { position: relative; isolation: isolate; }
.film-grain::after {
content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none; opacity: 0.04;
background-image:
repeating-radial-gradient(circle at 17% 23%, hsl(var(--foreground)) 0 1px, transparent 1px 3px),
repeating-radial-gradient(circle at 73% 61%, hsl(var(--foreground)) 0 1px, transparent 1px 3px);
mix-blend-mode: overlay;
}
/* spotlight — a radial pool of brass light, hero/crest overlay. */
.spotlight { background-image: radial-gradient(ellipse 60% 55% at 50% 40%, hsl(var(--primary) / 0.16), transparent 70%); }
/* reel-strip — brass double-rule band (crest / marquee accent). */
.reel-strip {
background-image:
linear-gradient(hsl(var(--primary)), hsl(var(--primary))),
linear-gradient(hsl(var(--primary)), hsl(var(--primary)));
background-size: 100% 1px, 100% 1px;
background-position: top, bottom;
background-repeat: no-repeat;
}
/* reel-mat — brass inner-mat around framed media. */
.reel-mat { position: relative; padding: 4px; box-shadow: inset 0 0 0 1px hsl(var(--border)); }
.reel-mat > * { display: block; width: 100%; height: 100%; }
/* reel-dropcap — condensed brass dropcap on first paragraph. */
.reel-dropcap[data-deco-dropcap]::first-letter,
.reel-dropcap[data-reel-dropcap]::first-letter {
font-family: var(--font-heading, "Antonio", "Oswald", "Bebas Neue", sans-serif);
font-size: 3.2em; line-height: 0.9; float: left; padding: 0.02em 0.14em 0 0; color: hsl(var(--primary));
}
/* letterbox-scrim — black cinematic bars over hero imagery (image overlay). */
.letterbox-scrim {
background-image: linear-gradient(to bottom,
hsl(0 0% 0% / 0.78) 0%, hsl(0 0% 0% / 0.15) 16%,
transparent 40%, transparent 60%,
hsl(0 0% 0% / 0.15) 84%, hsl(0 0% 0% / 0.78) 100%);
}
/* reveal — MID-tier scroll reveal. No JS = visible; reduced-motion honoured. */
.reveal { opacity: 1; }
.gotham-js .reveal { opacity: 0; transform: translateY(16px); transition: opacity .5s ease, transform .5s ease; }
.gotham-js .reveal.is-visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
.gotham-js .reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
}
}
/* Raw CSS (outside @layer, JIT-scan-independent) — layout guarantees the
* host Tailwind scan may not reliably emit from theme .ninjatpl files.
* Imageless hero: a compact letterboxed text plate, never a 75vh void. */
.gotham-hero-plate { min-height: 520px; }
@media (max-width: 640px) { .gotham-hero-plate { min-height: 380px; } }
/* Mobile collapse safety net — guarantee persona/override grids fall to a
* single column at ~<=640px even if the responsive prefix classes are not
* compiled from the theme templates. Base is already 1-col in the markup;
* this hard-forces it so a dropped sm:/md:/lg: prefix can never strand a
* wide multi-column grid on a phone (no horizontal overflow). */
@media (max-width: 640px) {
[data-block="gotham:events_marquee"] ul > li { grid-template-columns: 1fr !important; }
[data-block="gotham:membership_tiers"] .grid,
[data-block-override="gotham:feature-grid"] .grid { grid-template-columns: 1fr !important; }
}

View File

@ -2,22 +2,11 @@
{
"key": "gotham:default-master",
"title": "Gotham Default Master",
"page_templates": ["default", "article", "blog-index", "contact", "centered", "full-width"],
"page_templates": ["default", "centered"],
"blocks": [
{ "block_key": "navbar", "title": "House Navigation", "content": { "menuName": "main", "logoType": "text", "logoText": { "text": "Gotham House" } }, "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": "Site Footer", "content": { "layout": "centered", "companyName": "Gotham House", "tagline": "A members' club after dark." }, "slot": "footer", "sort_order": 0 }
]
},
{
"key": "gotham:marquee-master",
"title": "Gotham Marquee Master",
"page_templates": ["landing"],
"blocks": [
{ "block_key": "navbar", "title": "House Navigation", "content": { "menuName": "main", "logoType": "text", "logoText": { "text": "Gotham House" } }, "slot": "hero", "sort_order": 0 },
{ "block_key": "gotham:door_strip", "title": "Door Strip", "content": { "label": "The Door", "phone": "", "reserveLabel": "Guest list", "reserveUrl": "/contact", "hours": [] }, "slot": "hero", "sort_order": 1 },
{ "block_key": "slot", "title": "Main Content", "content": { "slotName": "main" }, "slot": "main", "sort_order": 0 },
{ "block_key": "footer", "title": "Site Footer", "content": { "layout": "centered", "companyName": "Gotham House", "tagline": "A members' club after dark." }, "slot": "footer", "sort_order": 0 }
{ "block_key": "navbar", "title": "Main Navigation", "content": { "menuName": "main" }, "slot": "header", "sort_order": 0 },
{ "block_key": "slot", "title": "Main Content Slot", "content": { "slotName": "main", "placeholder": "Page content will appear here" }, "slot": "main", "sort_order": 0 },
{ "block_key": "gotham:footer", "title": "Site Footer", "content": { "showSignup": true }, "slot": "footer", "sort_order": 0 }
]
}
]

View File

@ -2,8 +2,8 @@
name = "gotham"
display_name = "Gotham"
scope = "@themes"
version = "1.0.1"
description = "After-dark identity for a nightlife venue or private members' club. Cinematic near-black luxe, brass hairline accents, letterboxed heroes and tall condensed display type."
version = "1.0.0"
description = "Dark, high-contrast BlockNinja theme with bold typography and modern accent panels."
kind = "theme"
categories = ["templates"]
tags = ["dark", "nightlife", "luxury", "club", "cinematic", "brass", "condensed", "hospitality", "events"]
tags = ["dark", "high-contrast", "agency", "blog", "professional", "modern", "serif"]

View File

@ -1,252 +1,502 @@
[
{
"id": "velvet-black",
"name": "Velvet Black",
"description": "Deep matte black lifted by brass gilt. The house palette. Silver-screen platinum by day, velvet black after dark.",
"id": "dark-knight",
"name": "The Dark Knight",
"description": "Classic Gotham - dark with amber accents",
"theme": {
"mode": "both",
"lightColors": {
"background": "40 22% 95%",
"foreground": "30 10% 12%",
"card": "40 24% 98%",
"cardForeground": "30 10% 12%",
"popover": "40 24% 98%",
"popoverForeground": "30 10% 12%",
"primary": "38 58% 42%",
"primaryForeground": "40 30% 96%",
"secondary": "40 12% 88%",
"secondaryForeground": "30 12% 16%",
"muted": "40 12% 90%",
"mutedForeground": "30 8% 40%",
"accent": "36 62% 48%",
"accentForeground": "40 30% 96%",
"destructive": "0 68% 44%",
"destructiveForeground": "40 26% 96%",
"border": "40 18% 78%",
"input": "40 16% 82%",
"ring": "38 58% 44%"
"background": "0 0% 100%",
"foreground": "0 0% 3.9%",
"card": "0 0% 100%",
"cardForeground": "0 0% 3.9%",
"popover": "0 0% 100%",
"popoverForeground": "0 0% 3.9%",
"primary": "45 93% 47%",
"primaryForeground": "0 0% 0%",
"secondary": "0 0% 96%",
"secondaryForeground": "0 0% 9%",
"muted": "0 0% 96%",
"mutedForeground": "0 0% 45%",
"accent": "45 93% 47%",
"accentForeground": "0 0% 0%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "0 0% 90%",
"input": "0 0% 90%",
"ring": "45 93% 47%"
},
"darkColors": {
"background": "30 9% 5%",
"foreground": "40 28% 90%",
"card": "30 9% 8%",
"cardForeground": "40 28% 90%",
"popover": "30 9% 8%",
"popoverForeground": "40 28% 90%",
"primary": "40 66% 54%",
"primaryForeground": "30 22% 6%",
"secondary": "30 8% 14%",
"secondaryForeground": "40 24% 86%",
"muted": "30 7% 12%",
"mutedForeground": "40 12% 62%",
"accent": "38 74% 60%",
"accentForeground": "30 22% 6%",
"destructive": "0 70% 50%",
"destructiveForeground": "40 26% 95%",
"border": "40 24% 20%",
"input": "40 18% 16%",
"ring": "40 66% 54%"
}
"background": "0 0% 3.9%",
"foreground": "0 0% 98%",
"card": "0 0% 6%",
"cardForeground": "0 0% 98%",
"popover": "0 0% 6%",
"popoverForeground": "0 0% 98%",
"primary": "45 93% 47%",
"primaryForeground": "0 0% 0%",
"secondary": "0 0% 10%",
"secondaryForeground": "0 0% 98%",
"muted": "0 0% 10%",
"mutedForeground": "0 0% 60%",
"accent": "45 93% 47%",
"accentForeground": "0 0% 0%",
"destructive": "0 62% 30%",
"destructiveForeground": "0 0% 98%",
"border": "0 0% 14%",
"input": "0 0% 14%",
"ring": "45 93% 47%"
},
"mode": "dark"
}
},
{
"id": "midnight-brass",
"name": "Midnight Brass",
"description": "Cool blue-black steel with bright brass. A colder, later room.",
"id": "joker",
"name": "The Joker",
"description": "Chaotic purple and toxic green",
"theme": {
"mode": "both",
"lightColors": {
"background": "220 16% 95%",
"foreground": "222 18% 14%",
"card": "220 20% 98%",
"cardForeground": "222 18% 14%",
"popover": "220 20% 98%",
"popoverForeground": "222 18% 14%",
"primary": "40 60% 44%",
"primaryForeground": "220 20% 96%",
"secondary": "220 12% 88%",
"secondaryForeground": "222 16% 18%",
"muted": "220 12% 90%",
"mutedForeground": "220 8% 40%",
"accent": "40 66% 50%",
"accentForeground": "222 20% 8%",
"destructive": "0 68% 46%",
"destructiveForeground": "220 20% 96%",
"border": "220 14% 78%",
"input": "220 12% 82%",
"ring": "40 60% 46%"
"background": "0 0% 100%",
"foreground": "270 50% 15%",
"card": "0 0% 98%",
"cardForeground": "270 50% 15%",
"popover": "0 0% 98%",
"popoverForeground": "270 50% 15%",
"primary": "120 80% 35%",
"primaryForeground": "0 0% 100%",
"secondary": "270 40% 90%",
"secondaryForeground": "270 50% 15%",
"muted": "270 20% 95%",
"mutedForeground": "270 30% 40%",
"accent": "300 100% 70%",
"accentForeground": "0 0% 0%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "270 20% 85%",
"input": "270 20% 85%",
"ring": "120 80% 35%"
},
"darkColors": {
"background": "222 20% 6%",
"foreground": "215 22% 90%",
"card": "222 20% 9%",
"cardForeground": "215 22% 90%",
"popover": "222 20% 9%",
"popoverForeground": "215 22% 90%",
"primary": "42 72% 58%",
"primaryForeground": "222 24% 8%",
"secondary": "222 16% 15%",
"secondaryForeground": "215 20% 86%",
"muted": "222 14% 13%",
"mutedForeground": "215 12% 62%",
"accent": "40 78% 62%",
"accentForeground": "222 24% 8%",
"destructive": "0 72% 52%",
"destructiveForeground": "215 22% 95%",
"border": "220 20% 22%",
"input": "220 16% 18%",
"ring": "42 72% 58%"
}
"background": "270 30% 8%",
"foreground": "120 100% 80%",
"card": "270 30% 12%",
"cardForeground": "120 100% 80%",
"popover": "270 30% 12%",
"popoverForeground": "120 100% 80%",
"primary": "120 100% 40%",
"primaryForeground": "270 30% 8%",
"secondary": "270 50% 20%",
"secondaryForeground": "120 100% 80%",
"muted": "270 30% 15%",
"mutedForeground": "270 20% 60%",
"accent": "300 100% 50%",
"accentForeground": "0 0% 0%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "270 30% 20%",
"input": "270 30% 20%",
"ring": "120 100% 40%"
},
"mode": "dark"
}
},
{
"id": "noir-rose",
"name": "Noir Rose",
"description": "Near-black warmed with rose-gold. The velvet-rope room.",
"id": "riddler",
"name": "The Riddler",
"description": "Bright neon green with purple mystery",
"theme": {
"mode": "both",
"lightColors": {
"background": "20 20% 95%",
"foreground": "18 14% 14%",
"card": "20 24% 98%",
"cardForeground": "18 14% 14%",
"popover": "20 24% 98%",
"popoverForeground": "18 14% 14%",
"primary": "16 52% 50%",
"primaryForeground": "20 30% 97%",
"secondary": "20 14% 88%",
"secondaryForeground": "18 14% 18%",
"muted": "20 12% 90%",
"mutedForeground": "18 10% 40%",
"accent": "12 60% 56%",
"accentForeground": "20 30% 97%",
"destructive": "0 68% 46%",
"destructiveForeground": "20 26% 96%",
"border": "18 18% 78%",
"input": "18 16% 82%",
"ring": "16 52% 50%"
"background": "0 0% 100%",
"foreground": "0 0% 5%",
"card": "0 0% 98%",
"cardForeground": "0 0% 5%",
"popover": "0 0% 98%",
"popoverForeground": "0 0% 5%",
"primary": "120 70% 35%",
"primaryForeground": "0 0% 100%",
"secondary": "280 50% 85%",
"secondaryForeground": "280 80% 20%",
"muted": "0 0% 96%",
"mutedForeground": "120 30% 35%",
"accent": "280 80% 55%",
"accentForeground": "0 0% 100%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "120 30% 85%",
"input": "0 0% 90%",
"ring": "120 70% 35%"
},
"darkColors": {
"background": "16 12% 6%",
"foreground": "22 24% 90%",
"card": "16 12% 9%",
"cardForeground": "22 24% 90%",
"popover": "16 12% 9%",
"popoverForeground": "22 24% 90%",
"primary": "18 64% 60%",
"primaryForeground": "16 24% 8%",
"secondary": "16 12% 15%",
"secondaryForeground": "22 22% 86%",
"muted": "16 10% 13%",
"mutedForeground": "22 14% 62%",
"accent": "10 70% 64%",
"accentForeground": "16 24% 8%",
"destructive": "0 72% 54%",
"destructiveForeground": "22 24% 95%",
"border": "18 20% 22%",
"input": "18 16% 18%",
"ring": "18 64% 60%"
}
"background": "0 0% 4%",
"foreground": "120 100% 50%",
"card": "0 0% 8%",
"cardForeground": "120 100% 50%",
"popover": "0 0% 8%",
"popoverForeground": "120 100% 50%",
"primary": "120 100% 45%",
"primaryForeground": "0 0% 0%",
"secondary": "280 80% 30%",
"secondaryForeground": "120 100% 50%",
"muted": "0 0% 12%",
"mutedForeground": "120 50% 40%",
"accent": "280 100% 60%",
"accentForeground": "0 0% 100%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "120 100% 20%",
"input": "0 0% 15%",
"ring": "120 100% 45%"
},
"mode": "dark"
}
},
{
"id": "smoke-silver",
"name": "Smoke & Silver",
"description": "Graphite and cool silver. Restrained, low on gold. The quiet booth.",
"id": "harley-quinn",
"name": "Harley Quinn",
"description": "Bold red, black, and playful pink",
"theme": {
"mode": "both",
"lightColors": {
"background": "210 8% 95%",
"foreground": "210 10% 14%",
"card": "210 10% 98%",
"cardForeground": "210 10% 14%",
"popover": "210 10% 98%",
"popoverForeground": "210 10% 14%",
"primary": "210 8% 38%",
"primaryForeground": "210 12% 97%",
"secondary": "210 8% 88%",
"secondaryForeground": "210 10% 18%",
"muted": "210 8% 90%",
"mutedForeground": "210 6% 42%",
"accent": "42 40% 52%",
"accentForeground": "210 12% 10%",
"destructive": "0 66% 46%",
"destructiveForeground": "210 12% 96%",
"border": "210 8% 78%",
"input": "210 8% 82%",
"ring": "210 8% 42%"
"background": "0 0% 100%",
"foreground": "0 0% 5%",
"card": "350 50% 98%",
"cardForeground": "0 0% 5%",
"popover": "350 50% 98%",
"popoverForeground": "0 0% 5%",
"primary": "350 85% 50%",
"primaryForeground": "0 0% 100%",
"secondary": "210 80% 90%",
"secondaryForeground": "210 100% 20%",
"muted": "0 0% 96%",
"mutedForeground": "350 20% 40%",
"accent": "330 100% 75%",
"accentForeground": "0 0% 0%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "350 30% 88%",
"input": "0 0% 90%",
"ring": "350 85% 50%"
},
"darkColors": {
"background": "210 10% 7%",
"foreground": "210 12% 90%",
"card": "210 10% 10%",
"cardForeground": "210 12% 90%",
"popover": "210 10% 10%",
"popoverForeground": "210 12% 90%",
"primary": "210 14% 74%",
"primaryForeground": "210 12% 8%",
"secondary": "210 10% 15%",
"secondaryForeground": "210 12% 86%",
"muted": "210 8% 13%",
"mutedForeground": "210 8% 60%",
"accent": "42 56% 60%",
"accentForeground": "210 12% 8%",
"destructive": "0 70% 52%",
"destructiveForeground": "210 12% 95%",
"border": "210 12% 22%",
"input": "210 10% 18%",
"ring": "210 14% 74%"
}
"background": "0 0% 5%",
"foreground": "0 0% 95%",
"card": "350 80% 10%",
"cardForeground": "0 0% 95%",
"popover": "350 80% 10%",
"popoverForeground": "0 0% 95%",
"primary": "350 100% 55%",
"primaryForeground": "0 0% 100%",
"secondary": "210 100% 50%",
"secondaryForeground": "0 0% 100%",
"muted": "0 0% 12%",
"mutedForeground": "350 30% 60%",
"accent": "330 100% 65%",
"accentForeground": "0 0% 0%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "350 50% 20%",
"input": "0 0% 15%",
"ring": "350 100% 55%"
},
"mode": "dark"
}
},
{
"id": "bourbon",
"name": "Bourbon",
"description": "Espresso black and whisky amber. The last-pour palette.",
"id": "mr-freeze",
"name": "Mr. Freeze",
"description": "Cold ice blue and frozen white",
"theme": {
"mode": "both",
"lightColors": {
"background": "36 24% 94%",
"foreground": "28 18% 13%",
"card": "36 26% 98%",
"cardForeground": "28 18% 13%",
"popover": "36 26% 98%",
"popoverForeground": "28 18% 13%",
"primary": "30 62% 42%",
"primaryForeground": "36 30% 96%",
"secondary": "36 16% 87%",
"secondaryForeground": "28 16% 17%",
"muted": "36 14% 89%",
"mutedForeground": "28 12% 40%",
"accent": "26 66% 48%",
"accentForeground": "36 30% 96%",
"destructive": "0 68% 44%",
"destructiveForeground": "36 26% 96%",
"border": "34 20% 76%",
"input": "34 18% 80%",
"ring": "30 62% 44%"
"background": "0 0% 100%",
"foreground": "200 60% 10%",
"card": "200 40% 98%",
"cardForeground": "200 60% 10%",
"popover": "200 40% 98%",
"popoverForeground": "200 60% 10%",
"primary": "195 90% 45%",
"primaryForeground": "0 0% 100%",
"secondary": "200 40% 92%",
"secondaryForeground": "200 60% 10%",
"muted": "200 30% 95%",
"mutedForeground": "200 30% 40%",
"accent": "180 80% 60%",
"accentForeground": "200 60% 10%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "200 30% 88%",
"input": "200 30% 85%",
"ring": "195 90% 45%"
},
"darkColors": {
"background": "28 16% 6%",
"foreground": "36 28% 89%",
"card": "28 16% 9%",
"cardForeground": "36 28% 89%",
"popover": "28 16% 9%",
"popoverForeground": "36 28% 89%",
"primary": "32 72% 54%",
"primaryForeground": "28 24% 7%",
"secondary": "28 14% 15%",
"secondaryForeground": "36 24% 85%",
"muted": "28 12% 13%",
"mutedForeground": "36 14% 60%",
"accent": "24 80% 58%",
"accentForeground": "28 24% 7%",
"destructive": "0 72% 52%",
"destructiveForeground": "36 26% 95%",
"border": "30 22% 22%",
"input": "30 18% 18%",
"ring": "32 72% 54%"
"background": "200 50% 8%",
"foreground": "200 100% 95%",
"card": "200 50% 12%",
"cardForeground": "200 100% 95%",
"popover": "200 50% 12%",
"popoverForeground": "200 100% 95%",
"primary": "195 100% 50%",
"primaryForeground": "200 50% 8%",
"secondary": "200 60% 20%",
"secondaryForeground": "200 100% 95%",
"muted": "200 40% 15%",
"mutedForeground": "200 40% 60%",
"accent": "180 100% 70%",
"accentForeground": "200 50% 8%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "200 40% 25%",
"input": "200 40% 20%",
"ring": "195 100% 50%"
},
"mode": "dark"
}
},
{
"id": "poison-ivy",
"name": "Poison Ivy",
"description": "Deep forest green with seductive red",
"theme": {
"lightColors": {
"background": "0 0% 100%",
"foreground": "140 50% 12%",
"card": "140 30% 98%",
"cardForeground": "140 50% 12%",
"popover": "140 30% 98%",
"popoverForeground": "140 50% 12%",
"primary": "140 60% 35%",
"primaryForeground": "0 0% 100%",
"secondary": "0 50% 92%",
"secondaryForeground": "0 70% 25%",
"muted": "140 20% 95%",
"mutedForeground": "140 25% 40%",
"accent": "0 70% 55%",
"accentForeground": "0 0% 100%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "140 25% 88%",
"input": "140 20% 85%",
"ring": "140 60% 35%"
},
"darkColors": {
"background": "140 40% 6%",
"foreground": "140 60% 90%",
"card": "140 40% 10%",
"cardForeground": "140 60% 90%",
"popover": "140 40% 10%",
"popoverForeground": "140 60% 90%",
"primary": "140 70% 40%",
"primaryForeground": "0 0% 100%",
"secondary": "0 70% 40%",
"secondaryForeground": "0 0% 100%",
"muted": "140 30% 15%",
"mutedForeground": "140 30% 55%",
"accent": "0 80% 50%",
"accentForeground": "0 0% 100%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "140 30% 20%",
"input": "140 30% 15%",
"ring": "140 70% 40%"
},
"mode": "dark"
}
},
{
"id": "two-face",
"name": "Two-Face",
"description": "Split personality - half dark, half light",
"theme": {
"lightColors": {
"background": "0 0% 100%",
"foreground": "0 0% 5%",
"card": "0 0% 98%",
"cardForeground": "0 0% 5%",
"popover": "0 0% 98%",
"popoverForeground": "0 0% 5%",
"primary": "0 0% 10%",
"primaryForeground": "0 0% 100%",
"secondary": "0 0% 92%",
"secondaryForeground": "0 0% 10%",
"muted": "0 0% 96%",
"mutedForeground": "0 0% 45%",
"accent": "45 100% 50%",
"accentForeground": "0 0% 0%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "0 0% 88%",
"input": "0 0% 85%",
"ring": "0 0% 10%"
},
"darkColors": {
"background": "0 0% 5%",
"foreground": "0 0% 95%",
"card": "0 0% 10%",
"cardForeground": "0 0% 95%",
"popover": "0 0% 10%",
"popoverForeground": "0 0% 95%",
"primary": "0 0% 95%",
"primaryForeground": "0 0% 5%",
"secondary": "0 0% 20%",
"secondaryForeground": "0 0% 95%",
"muted": "0 0% 15%",
"mutedForeground": "0 0% 55%",
"accent": "45 100% 50%",
"accentForeground": "0 0% 0%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "0 0% 25%",
"input": "0 0% 20%",
"ring": "0 0% 95%"
},
"mode": "dark"
}
},
{
"id": "catwoman",
"name": "Catwoman",
"description": "Sleek purple and black elegance",
"theme": {
"lightColors": {
"background": "0 0% 100%",
"foreground": "280 30% 15%",
"card": "280 20% 98%",
"cardForeground": "280 30% 15%",
"popover": "280 20% 98%",
"popoverForeground": "280 30% 15%",
"primary": "280 50% 45%",
"primaryForeground": "0 0% 100%",
"secondary": "280 25% 92%",
"secondaryForeground": "280 30% 15%",
"muted": "280 15% 96%",
"mutedForeground": "280 15% 40%",
"accent": "320 70% 60%",
"accentForeground": "0 0% 100%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "280 20% 88%",
"input": "280 15% 85%",
"ring": "280 50% 45%"
},
"darkColors": {
"background": "280 20% 6%",
"foreground": "280 10% 90%",
"card": "280 20% 10%",
"cardForeground": "280 10% 90%",
"popover": "280 20% 10%",
"popoverForeground": "280 10% 90%",
"primary": "280 60% 55%",
"primaryForeground": "0 0% 100%",
"secondary": "280 30% 20%",
"secondaryForeground": "280 10% 90%",
"muted": "280 15% 15%",
"mutedForeground": "280 10% 55%",
"accent": "320 80% 60%",
"accentForeground": "0 0% 100%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "280 20% 22%",
"input": "280 20% 18%",
"ring": "280 60% 55%"
},
"mode": "dark"
}
},
{
"id": "scarecrow",
"name": "Scarecrow",
"description": "Fear-inducing orange and decayed brown",
"theme": {
"lightColors": {
"background": "0 0% 100%",
"foreground": "30 40% 15%",
"card": "35 30% 98%",
"cardForeground": "30 40% 15%",
"popover": "35 30% 98%",
"popoverForeground": "30 40% 15%",
"primary": "25 85% 45%",
"primaryForeground": "0 0% 100%",
"secondary": "30 30% 92%",
"secondaryForeground": "30 40% 15%",
"muted": "30 20% 95%",
"mutedForeground": "30 25% 40%",
"accent": "15 90% 50%",
"accentForeground": "0 0% 100%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "30 25% 88%",
"input": "30 20% 85%",
"ring": "25 85% 45%"
},
"darkColors": {
"background": "30 30% 6%",
"foreground": "35 60% 85%",
"card": "30 30% 10%",
"cardForeground": "35 60% 85%",
"popover": "30 30% 10%",
"popoverForeground": "35 60% 85%",
"primary": "25 90% 50%",
"primaryForeground": "0 0% 0%",
"secondary": "30 40% 20%",
"secondaryForeground": "35 60% 85%",
"muted": "30 25% 15%",
"mutedForeground": "30 20% 50%",
"accent": "15 100% 45%",
"accentForeground": "0 0% 100%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "30 30% 20%",
"input": "30 30% 15%",
"ring": "25 90% 50%"
},
"mode": "dark"
}
},
{
"id": "bane",
"name": "Bane",
"description": "Military tactical green and black",
"theme": {
"lightColors": {
"background": "0 0% 100%",
"foreground": "100 30% 15%",
"card": "100 20% 98%",
"cardForeground": "100 30% 15%",
"popover": "100 20% 98%",
"popoverForeground": "100 30% 15%",
"primary": "100 45% 30%",
"primaryForeground": "0 0% 100%",
"secondary": "100 20% 92%",
"secondaryForeground": "100 30% 15%",
"muted": "100 15% 95%",
"mutedForeground": "100 20% 40%",
"accent": "45 75% 50%",
"accentForeground": "0 0% 0%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "100 20% 88%",
"input": "100 15% 85%",
"ring": "100 45% 30%"
},
"darkColors": {
"background": "100 20% 5%",
"foreground": "100 10% 85%",
"card": "100 20% 9%",
"cardForeground": "100 10% 85%",
"popover": "100 20% 9%",
"popoverForeground": "100 10% 85%",
"primary": "100 50% 35%",
"primaryForeground": "0 0% 100%",
"secondary": "100 25% 18%",
"secondaryForeground": "100 10% 85%",
"muted": "100 15% 14%",
"mutedForeground": "100 10% 50%",
"accent": "45 80% 45%",
"accentForeground": "0 0% 0%",
"destructive": "0 84% 60%",
"destructiveForeground": "0 0% 98%",
"border": "100 20% 18%",
"input": "100 20% 14%",
"ring": "100 50% 35%"
},
"mode": "dark"
}
}
]

View File

@ -1,255 +0,0 @@
{
"demo": true,
"settings": {
"merge": {
"companyName": "Gotham House",
"tagline": "A members' club after dark."
}
},
"data_tables": [
{
"key": "contact_submissions",
"name": "Door Enquiries",
"description": "Seeded contact-form submissions for the Gotham House 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": "party", "label": "Party size", "type": "text", "required": false },
{ "key": "message", "label": "Message", "type": "text", "required": true }
]
}
],
"pages": [
{
"slug": "/",
"title": "Gotham House",
"template_key": "landing",
"blocks": [
{
"block_key": "hero",
"title": "Hero",
"slot": "main",
"sort_order": 1,
"content": {
"template": "centered",
"minHeight": "75vh",
"eyebrow": "Members only",
"headline": "Gotham House",
"subheadline": "A members' club after dark.",
"description": "Late rooms, a long bar and a short guest list. Doors from nine, Thursday to Sunday. Bring your card.",
"ctaPrimary": { "text": "Request the guest list", "url": "/contact" },
"ctaSecondary": { "text": "See the programme", "url": "#programme" }
}
},
{
"block_key": "gotham:events_marquee",
"title": "Programme",
"slot": "main",
"sort_order": 2,
"content": {
"eyebrow": "The programme",
"title": "Tonight and after",
"events": [
{ "date": "Thu", "name": "Late Set", "room": "Main Room", "note": "Resident selectors. Slow start, long finish.", "url": "/contact" },
{ "date": "Fri", "name": "The Gilt Sessions", "room": "The Brass Room", "note": "House and disco on the good system.", "url": "/contact" },
{ "date": "Sat", "name": "Members' Night", "room": "Whole House", "note": "Card on the door. No list, no entry.", "url": "/contact" },
{ "date": "Sun", "name": "Last Pour", "room": "The Cellar Bar", "note": "Quiet room. Records and a nightcap.", "url": "/contact" }
]
}
},
{
"block_key": "gotham:membership_tiers",
"title": "Membership",
"slot": "main",
"sort_order": 3,
"content": {
"eyebrow": "Membership",
"title": "Join the house",
"intro": "Two ways in. Both start at the door.",
"tiers": [
{
"name": "House",
"price": "$40",
"cadence": "/ month",
"description": "The standard card. Enough for most nights.",
"featured": false,
"applyLabel": "Apply",
"applyUrl": "/contact",
"features": [
{ "label": "Priority on the guest list" },
{ "label": "The main room and the cellar bar" },
{ "label": "Two guests a night" }
]
},
{
"name": "Brass",
"price": "$120",
"cadence": "/ month",
"description": "The full card. Every room, every night.",
"featured": true,
"applyLabel": "Apply",
"applyUrl": "/contact",
"features": [
{ "label": "Everything in House" },
{ "label": "The Brass Room and private booths" },
{ "label": "First call on bottle service" },
{ "label": "Four guests a night" }
]
}
]
}
},
{
"block_key": "gotham:bottle_menu",
"title": "Bottle service",
"slot": "main",
"sort_order": 4,
"content": {
"eyebrow": "The cellar",
"title": "Bottle service",
"footnote": "Booths hold six. Ask the host for the full list.",
"sections": [
{
"title": "Champagne",
"items": [
{ "name": "House brut", "note": "By the glass or the bottle.", "price": "$95" },
{ "name": "Vintage rose", "note": "A short shelf. Ask first.", "price": "$180" }
]
},
{
"title": "Spirits",
"items": [
{ "name": "Aged rye", "note": "Neat, one large cube.", "price": "$140" },
{ "name": "Single malt", "note": "Islay. Bring water.", "price": "$160" }
]
}
]
}
},
{
"block_key": "cta",
"title": "Closing CTA",
"slot": "cta",
"sort_order": 1,
"content": {
"background": "band",
"heading": "The list closes at nine",
"text": "Send your name and party. We reply the same day.",
"primaryLabel": "Request the guest list",
"primaryUrl": "/contact"
}
}
]
},
{
"slug": "/about",
"title": "The House",
"template_key": "default",
"blocks": [
{ "block_key": "heading", "title": "Heading", "slot": "main", "sort_order": 1, "content": { "text": "The House", "level": 1 } },
{ "block_key": "rich-text", "title": "Story", "slot": "main", "sort_order": 2, "content": { "html": "<p>Gotham House keeps late hours behind an unmarked door. We run four nights a week and hold the rest of the week back.</p><p>The rooms are dark by design. The bar is long. The list is short. Members come first, and their guests come with them.</p>" } },
{ "block_key": "divider", "title": "Divider", "slot": "main", "sort_order": 3, "content": { "variant": "ornament", "icon": "lucide:diamond" } },
{ "block_key": "quote", "title": "Quote", "slot": "main", "sort_order": 4, "content": { "style": "block", "align": "center", "quote": "Come late. Stay later.", "attribution": "The House" } }
]
},
{
"slug": "/blog",
"title": "The Journal",
"template_key": "blog-index",
"blocks": [
{ "block_key": "card", "title": "Post: The Door", "slot": "main", "sort_order": 1, "content": { "layout": "vertical", "title": "How the door works", "text": "<p>The list, the card and the guests. What to send and when.</p>", "link": "/blog/how-the-door-works", "linkLabel": "Read on" } },
{ "block_key": "card", "title": "Post: A Night", "slot": "main", "sort_order": 2, "content": { "layout": "vertical", "title": "A night at the house", "text": "<p>From the first pour to the last record. How the evening runs.</p>", "link": "/blog/a-night-at-the-house", "linkLabel": "Read on" } },
{ "block_key": "card", "title": "Post: The Cellar", "slot": "main", "sort_order": 3, "content": { "layout": "vertical", "title": "Notes from the cellar", "text": "<p>What we pour after midnight and why the list stays short.</p>", "link": "/blog/notes-from-the-cellar", "linkLabel": "Read on" } }
]
},
{
"slug": "/blog/how-the-door-works",
"title": "How the door works",
"template_key": "article",
"parent_slug": "/blog",
"blocks": [
{ "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "How the door works", "level": 1 } },
{ "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "<p>Send your name and party by nine. We hold the list until then and reply the same day.</p><p>Members lead. Guests come with them, and there is a cap. Bring your card. The door checks it.</p>" } }
]
},
{
"slug": "/blog/a-night-at-the-house",
"title": "A night at the house",
"template_key": "article",
"parent_slug": "/blog",
"blocks": [
{ "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "A night at the house", "level": 1 } },
{ "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "<p>Doors open at nine. The first set starts slow. The main room fills after eleven and holds until the lights come up.</p><p>The cellar bar stays quiet. That is the point. Records, a nightcap and the last pour.</p>" } }
]
},
{
"slug": "/blog/notes-from-the-cellar",
"title": "Notes from the cellar",
"template_key": "article",
"parent_slug": "/blog",
"blocks": [
{ "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "Notes from the cellar", "level": 1 } },
{ "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "<p>The list is short by choice. We pour champagne by the glass, keep a bench of rye and hold a small shelf for members who ask.</p><p>Tell the host what you are drinking. The rest follows.</p>" } }
]
},
{
"slug": "/contact",
"title": "The Door",
"template_key": "contact",
"blocks": [
{
"block_key": "contact-form",
"title": "Guest List Enquiry",
"slot": "main",
"sort_order": 1,
"content": {
"heading": "Request the guest list",
"description": "Send your name, the night and the party. We reply the same day.",
"submitLabel": "Send to the door",
"successMessage": "Thank you. Your name is with the door. We will reply by email.",
"fields": [
{ "name": "name", "label": "Your name", "type": "text", "required": true, "width": "half" },
{ "name": "email", "label": "Email", "type": "email", "required": true, "width": "half" },
{ "name": "party", "label": "Party size", "type": "text", "required": false, "width": "half" },
{ "name": "message", "label": "Which night, and any notes", "type": "textarea", "required": true, "width": "full" }
],
"formConfig": { "enabled": true, "captchaEnabled": false, "targetTable": "contact_submissions" }
}
},
{
"block_key": "hours-location",
"title": "Door & Hours",
"slot": "aside",
"sort_order": 1,
"content": {
"heading": "The Door",
"hours": [
{ "day": "Thursday", "hours": "9pm to late" },
{ "day": "Friday to Saturday", "hours": "9pm to 4am" },
{ "day": "Sunday", "hours": "9pm to 2am" },
{ "day": "Monday to Wednesday", "closed": true }
],
"specialHoursNote": "Members' Night runs Saturday. Card on the door.",
"address": "1 Vane Court\nThe Old Quarter",
"phone": "+1 555 0139"
}
}
]
},
{
"slug": "/login",
"title": "Members",
"template_key": "auth",
"blocks": [
{ "block_key": "auth-form", "title": "Sign in", "slot": "main", "sort_order": 1, "content": { "default_tab": "login", "show_social": false } },
{ "block_key": "text", "title": "Foot", "slot": "footer", "sort_order": 1, "content": { "text": "<p>Members book priority nights and private booths.</p>" } }
]
}
],
"menu_items": [
{ "menu": "main", "label": "Home", "page_slug": "/", "sort_order": 1 },
{ "menu": "main", "label": "The House", "page_slug": "/about", "sort_order": 2 },
{ "menu": "main", "label": "The Journal", "page_slug": "/blog", "sort_order": 3 },
{ "menu": "main", "label": "The Door", "page_slug": "/contact", "sort_order": 4 }
]
}

View File

@ -1,19 +0,0 @@
{
"workflows": [
{
"key": "contact_notify_admins",
"name": "Door enquiry to admins",
"description": "On a guest-list 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

@ -1,6 +1,6 @@
<!doctype html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="x-apple-disable-message-reformatting">
@ -16,49 +16,84 @@
<![endif]-->
<title>{{ site_name }}</title>
<style type="text/css">
/* CSS Reset for Email */
body, table, td, p, a, li, blockquote {
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
img { -ms-interpolation-mode: bicubic; border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; }
body { margin: 0 !important; padding: 0 !important; width: 100% !important; }
a[x-apple-data-detectors] { color: inherit !important; text-decoration: none !important; }
table, td {
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
}
img {
-ms-interpolation-mode: bicubic;
border: 0;
height: auto;
line-height: 100%;
outline: none;
text-decoration: none;
}
body {
margin: 0 !important;
padding: 0 !important;
width: 100% !important;
}
a[x-apple-data-detectors] {
color: inherit !important;
text-decoration: none !important;
}
/* Gotham-specific styles */
h1, h2, h3, h4, h5, h6 {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
font-weight: 700;
}
/* Responsive styles */
@media only screen and (max-width: 620px) {
.email-container { width: 100% !important; max-width: 100% !important; }
.content-padding { padding-left: 24px !important; padding-right: 24px !important; }
.email-container {
width: 100% !important;
max-width: 100% !important;
}
.content-padding {
padding-left: 24px !important;
padding-right: 24px !important;
}
}
</style>
</head>
<body style="background-color: {{ colors.background }}; margin: 0; padding: 0; font-family: &amp;#39;Cormorant Garamond&amp;#39;, &amp;#39;Cormorant&amp;#39;, Georgia, serif;">
{% if preview_text %}<div style="display: none; max-height: 0; overflow: hidden; mso-hide: all;">{{ preview_text }}</div>{% endif %}
</head>
<body style="background-color: {{ colors.background|default:"#0a0a0a" }}; margin: 0; padding: 0; font-family: &amp;#39;Inter&amp;#39;, -apple-system, BlinkMacSystemFont, &amp;#39;Segoe UI&amp;#39;, Roboto, sans-serif;">
<!-- Preview text (hidden) -->
{% if preview_text %}<div style="display: none; max-height: 0; overflow: hidden; mso-hide: all;">{{ preview_text }}</div><div style="display: none; max-height: 0; overflow: hidden; mso-hide: all;">&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;</div>{% endif %}
<!-- Main Container -->
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
<tr>
<td align="center" style="padding: 32px 10px; background-color: {{ colors.background }};">
<table role="presentation" class="email-container" width="600" cellspacing="0" cellpadding="0" border="0" style="max-width: 600px; background-color: {{ colors.background }}; border: 1px solid {{ colors.border }};">
<td align="center" style="padding: 48px 10px; background-color: {{ colors.background|default:"#0a0a0a" }};">
<!-- Email Container -->
<table role="presentation" class="email-container" width="600" cellspacing="0" cellpadding="0" border="0" style="max-width: 600px; background-color: {{ colors.card|default:"#141414" }}; border: 1px solid {{ colors.border|default:"#262626" }};">
<!-- Header -->
<tr>
<td align="center" style="padding: 0; background-color: {{ colors.foreground }};">
<img src="data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20600%2080%22%3E%3Crect%20width%3D%22600%22%20height%3D%2280%22%20fill%3D%22%23191510%22%2F%3E%3Cg%20stroke%3D%22%23{{ colors.primary|default:"#c9a14a"|cut:"#" }}%22%20stroke-width%3D%221%22%20fill%3D%22none%22%3E%3Cpath%20d%3D%22M300%2080%20L%20100%2010%22%2F%3E%3Cpath%20d%3D%22M300%2080%20L%20180%2010%22%2F%3E%3Cpath%20d%3D%22M300%2080%20L%20260%2010%22%2F%3E%3Cpath%20d%3D%22M300%2080%20L%20340%2010%22%2F%3E%3Cpath%20d%3D%22M300%2080%20L%20420%2010%22%2F%3E%3Cpath%20d%3D%22M300%2080%20L%20500%2010%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E" alt="" width="600" height="80" style="display: block; width: 100%; max-width: 600px; height: 80px; background-color: {{ colors.foreground }};">
{% if site_name %}<h1 style="margin: 0; padding: 24px 16px; font-family: &amp;#39;Oswald&amp;#39;, &amp;#39;Antonio&amp;#39;, Georgia, serif; font-size: 28px; text-transform: uppercase; letter-spacing: 0.18em; color: {{ colors.primary }};">{{ site_name }}</h1>{% endif %}
</td>
<td align="center" style="padding: 32px 40px; background-color: {{ colors.card|default:"#141414" }}; border-bottom: 1px solid {{ colors.border|default:"#262626" }};">{% if logo_url %}<img src="{{ logo_url }}" alt="{{ site_name }}" style="max-height: 48px; width: auto; display: block;">{% elif site_name %}<h1 style="margin: 0; font-size: 24px; font-weight: 800; color: {{ colors.foreground|default:"#fafafa" }}; letter-spacing: -0.5px; text-transform: uppercase;">{{ site_name }}</h1>{% endif %}</td>
</tr>
<!-- Body Content -->
<tr>
<td class="content-padding" style="padding: 40px 48px; color: {{ colors.foreground }}; font-size: 16px; line-height: 1.7;">{{ body|safe }}</td>
<td class="content-padding" style="padding: 40px 48px; color: {{ colors.foreground|default:"#fafafa" }}; font-size: 16px; line-height: 1.75; letter-spacing: 0.01em;">{{ body|safe }}</td>
</tr>
<!-- Footer -->
<tr>
<td style="padding: 0 48px;"><hr style="border: none; border-top: 1px solid {{ colors.primary }}; margin: 0;"></td>
</tr>
<td style="padding: 32px 48px; background-color: {{ colors.muted|default:"#1a1a1a" }}; border-top: 1px solid {{ colors.border|default:"#262626" }};">
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
<tr>
<td align="center" style="padding: 24px 48px; color: {{ colors.mutedForeground }}; font-size: 13px;">
<p style="margin: 0 0 8px; font-family: &amp;#39;Oswald&amp;#39;, &amp;#39;Antonio&amp;#39;, Georgia, serif; text-transform: uppercase; letter-spacing: 0.18em; color: {{ colors.foreground }};">{{ site_name }}</p>
{% if site_url %}<p style="margin: 0 0 8px;"><a href="{{ site_url }}" style="color: {{ colors.primary }}; text-decoration: none; border-bottom: 1px solid {{ colors.primary }};">{{ site_url }}</a></p>{% endif %}
{% if support_email %}<p style="margin: 12px 0 0; font-style: italic; color: {{ colors.mutedForeground }};">P.S. Reservations &amp; enquiries: {{ support_email }}</p>{% endif %}
{% if unsubscribe_url %}<p style="margin: 16px 0 0; font-size: 11px;"><a href="{{ unsubscribe_url }}" style="color: {{ colors.mutedForeground }}; text-decoration: none;">Unsubscribe</a></p>{% endif %}
<td align="center">
<p style="margin: 0 0 8px; font-size: 13px; font-weight: 700; color: {{ colors.foreground|default:"#fafafa" }}; text-transform: uppercase; letter-spacing: 0.1em;">{{ site_name }}</p>
{% if site_url %}<p style="margin: 0 0 16px; font-size: 13px; color: {{ colors.mutedForeground|default:"#737373" }};"><a href="{{ site_url }}" style="color: {{ colors.primary|default:"#fafafa" }}; text-decoration: none; border-bottom: 1px solid {{ colors.primary|default:"#fafafa" }};">{{ site_url }}</a></p>{% endif %}
{% if unsubscribe_url %}<p style="margin: 0; font-size: 11px; color: {{ colors.mutedForeground|default:"#737373" }};"><a href="{{ unsubscribe_url }}" style="color: {{ colors.mutedForeground|default:"#737373" }}; text-decoration: none;">Unsubscribe</a></p>{% endif %}
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</td>
</tr>
</table>
</body>
</html>

View File

@ -1,23 +0,0 @@
<!doctype html>
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
{{ head_html|safe }}
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col film-grain" style="font-family: var(--font-body, &quot;Cormorant Garamond&quot;, &quot;Cormorant&quot;, Georgia, serif);">
{{ admin_banner_html|safe }}
<header class="w-full">
<div class="max-w-3xl mx-auto px-4">{{ slots.header|safe }}</div>
</header>
<div class="flex-grow grid gap-12 max-w-5xl mx-auto w-full px-4 py-12 md:py-16 md:grid-cols-[2fr_1fr]">
<main class="prose max-w-none">
{% if slots.main %}<article class="reel-dropcap" data-reel-dropcap="true">{{ slots.main|safe }}</article>{% else %}<div class="py-20 text-center"><p class="italic" style="color:hsl(var(--muted-foreground));">No content blocks assigned to this page.</p></div>{% endif %}
</main>
<aside class="w-full md:border-l md:border-[hsl(var(--primary)/0.3)] md:pl-10">{{ slots.aside|safe }}</aside>
</div>
<footer class="w-full mt-auto border-t border-[hsl(var(--primary)/0.35)]">
<div class="max-w-5xl mx-auto px-4">{{ slots.footer|safe }}</div>
</footer>
{{ body_end_html|safe }}
<script>
(function(){if(window.__gothamMotion)return;window.__gothamMotion=1;var d=document,root=d.documentElement;root.classList.add('gotham-js');var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;function pending(){return d.querySelectorAll('.reveal:not(.is-visible)');}function reveal(el){el.classList.add('is-visible');}function inView(){var h=window.innerHeight||root.clientHeight;pending().forEach(function(el){var r=el.getBoundingClientRect();if(r.top<h*1.05&&r.bottom>-h*0.25)reveal(el);});}function all(){pending().forEach(reveal);}function run(){if(reduce){all();return;}inView();window.addEventListener('scroll',inView,{passive:true});window.addEventListener('resize',inView,{passive:true});window.addEventListener('load',function(){inView();setTimeout(inView,120);},{once:true});setTimeout(inView,400);setTimeout(all,2600);d.querySelectorAll('[data-spotlight]').forEach(function(s){s.addEventListener('pointermove',function(ev){var r=s.getBoundingClientRect();s.style.setProperty('--sx',((ev.clientX-r.left)/r.width*100)+'%');s.style.setProperty('--sy',((ev.clientY-r.top)/r.height*100)+'%');});});}if(d.readyState!='loading')run();else d.addEventListener('DOMContentLoaded',run);})();
</script>
</body>
</html>

View File

@ -1,21 +0,0 @@
<!doctype html>
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
{{ head_html|safe }}
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col film-grain" style="font-family: var(--font-body, &quot;Cormorant Garamond&quot;, &quot;Cormorant&quot;, Georgia, serif);">
{{ admin_banner_html|safe }}
<main class="flex-grow w-full flex flex-col items-center justify-center px-4 py-12" data-spotlight style="background-image:radial-gradient(circle at var(--sx,50%) var(--sy,42%), hsl(var(--primary)/0.10), transparent 60%);">
<div class="w-full max-w-md">
<a href="/" class="block text-center" aria-label="{{ site_settings.Title }} home">
{% if site_settings.Logo %}<img src="{{ site_settings.Logo }}" alt="{{ site_settings.LogoAlt }}" class="mx-auto h-12 w-auto">{% else %}<span class="reel-caps text-2xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ site_settings.Title|default:"BlockNinja" }}</span>{% endif %}
</a>
<div class="mx-auto mt-4 mb-6 h-4 w-32 reel-strip" aria-hidden="true"></div>
<div>{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="py-20 text-center"><p class="italic" style="color:hsl(var(--muted-foreground));">No form assigned to this page.</p></div>{% endif %}</div>
{% if slots.footer %}<div class="mt-6 text-center text-sm" style="color:hsl(var(--muted-foreground));">{{ slots.footer|safe }}</div>{% endif %}
</div>
</main>
{{ body_end_html|safe }}
<script>
(function(){if(window.__gothamMotion)return;window.__gothamMotion=1;var d=document,root=d.documentElement;root.classList.add('gotham-js');var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;function pending(){return d.querySelectorAll('.reveal:not(.is-visible)');}function reveal(el){el.classList.add('is-visible');}function inView(){var h=window.innerHeight||root.clientHeight;pending().forEach(function(el){var r=el.getBoundingClientRect();if(r.top<h*1.05&&r.bottom>-h*0.25)reveal(el);});}function all(){pending().forEach(reveal);}function run(){if(reduce){all();return;}inView();window.addEventListener('scroll',inView,{passive:true});window.addEventListener('resize',inView,{passive:true});window.addEventListener('load',function(){inView();setTimeout(inView,120);},{once:true});setTimeout(inView,400);setTimeout(all,2600);d.querySelectorAll('[data-spotlight]').forEach(function(s){s.addEventListener('pointermove',function(ev){var r=s.getBoundingClientRect();s.style.setProperty('--sx',((ev.clientX-r.left)/r.width*100)+'%');s.style.setProperty('--sy',((ev.clientY-r.top)/r.height*100)+'%');});});}if(d.readyState!='loading')run();else d.addEventListener('DOMContentLoaded',run);})();
</script>
</body>
</html>

View File

@ -1,26 +0,0 @@
<!doctype html>
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
{{ head_html|safe }}
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col film-grain" style="font-family: var(--font-body, &quot;Cormorant Garamond&quot;, &quot;Cormorant&quot;, Georgia, serif);">
{{ admin_banner_html|safe }}
{% if slots.header %}<header class="w-full">{{ slots.header|safe }}</header>{% endif %}
<main class="flex-grow w-full">
<div class="max-w-6xl mx-auto px-4 py-12 md:py-16">
{% if title %}
<div class="text-center reveal">
<h1 class="reel-caps reel-underline mx-auto w-fit text-4xl md:text-5xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);">{{ title }}</h1>
{% if page_meta.MetaDescription %}<p class="mt-4 text-lg italic" style="color:hsl(var(--muted-foreground));">{{ page_meta.MetaDescription }}</p>{% endif %}
<div class="mx-auto mt-6 h-4 w-40 reel-strip" aria-hidden="true"></div>
</div>
{% endif %}
{% if slots.featured %}<section class="mt-12">{{ slots.featured|safe }}</section>{% endif %}
{% if slots.main %}<section class="mt-12">{{ slots.main|safe }}</section>{% else %}<div class="py-20 text-center"><p class="italic" style="color:hsl(var(--muted-foreground));">No posts published yet.</p></div>{% endif %}
</div>
</main>
{% if slots.footer %}<footer class="w-full mt-auto border-t border-[hsl(var(--primary)/0.35)]">{{ slots.footer|safe }}</footer>{% endif %}
{{ body_end_html|safe }}
<script>
(function(){if(window.__gothamMotion)return;window.__gothamMotion=1;var d=document,root=d.documentElement;root.classList.add('gotham-js');var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;function pending(){return d.querySelectorAll('.reveal:not(.is-visible)');}function reveal(el){el.classList.add('is-visible');}function inView(){var h=window.innerHeight||root.clientHeight;pending().forEach(function(el){var r=el.getBoundingClientRect();if(r.top<h*1.05&&r.bottom>-h*0.25)reveal(el);});}function all(){pending().forEach(reveal);}function run(){if(reduce){all();return;}inView();window.addEventListener('scroll',inView,{passive:true});window.addEventListener('resize',inView,{passive:true});window.addEventListener('load',function(){inView();setTimeout(inView,120);},{once:true});setTimeout(inView,400);setTimeout(all,2600);d.querySelectorAll('[data-spotlight]').forEach(function(s){s.addEventListener('pointermove',function(ev){var r=s.getBoundingClientRect();s.style.setProperty('--sx',((ev.clientX-r.left)/r.width*100)+'%');s.style.setProperty('--sy',((ev.clientY-r.top)/r.height*100)+'%');});});}if(d.readyState!='loading')run();else d.addEventListener('DOMContentLoaded',run);})();
</script>
</body>
</html>

View File

@ -1,16 +1,11 @@
<!doctype html>
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
<html lang="en" class="dark">
{{ head_html|safe }}
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col film-grain" style="font-family: var(--font-body, &quot;Cormorant Garamond&quot;, &quot;Cormorant&quot;, Georgia, serif);">
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col">
{{ admin_banner_html|safe }}
<header class="w-full border-b border-[hsl(var(--primary)/0.35)]"><div class="max-w-2xl mx-auto px-4">{{ slots.header|safe }}</div></header>
<main class="flex-grow max-w-2xl mx-auto w-full px-4 py-12 md:py-16">
{% if slots.main %}<article class="prose prose-lg max-w-none">{{ slots.main|safe }}</article>{% else %}<div class="py-20 text-center"><p class="italic" style="color:hsl(var(--muted-foreground));">No content blocks assigned to this page.</p></div>{% endif %}
</main>
<footer class="w-full mt-auto border-t border-[hsl(var(--primary)/0.35)]"><div class="max-w-2xl mx-auto px-4">{{ slots.footer|safe }}</div></footer>
<header class="w-full border-b border-border"><div class="max-w-2xl mx-auto px-4">{{ slots.header|safe }}</div></header>
<main class="flex-grow max-w-2xl mx-auto w-full px-4 py-12">{% if slots.main %}<article class="prose prose-invert prose-lg 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>
<footer class="w-full mt-auto">{{ slots.footer|safe }}</footer>
{{ body_end_html|safe }}
<script>
(function(){if(window.__gothamMotion)return;window.__gothamMotion=1;var d=document,root=d.documentElement;root.classList.add('gotham-js');var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;function pending(){return d.querySelectorAll('.reveal:not(.is-visible)');}function reveal(el){el.classList.add('is-visible');}function inView(){var h=window.innerHeight||root.clientHeight;pending().forEach(function(el){var r=el.getBoundingClientRect();if(r.top<h*1.05&&r.bottom>-h*0.25)reveal(el);});}function all(){pending().forEach(reveal);}function run(){if(reduce){all();return;}inView();window.addEventListener('scroll',inView,{passive:true});window.addEventListener('resize',inView,{passive:true});window.addEventListener('load',function(){inView();setTimeout(inView,120);},{once:true});setTimeout(inView,400);setTimeout(all,2600);d.querySelectorAll('[data-spotlight]').forEach(function(s){s.addEventListener('pointermove',function(ev){var r=s.getBoundingClientRect();s.style.setProperty('--sx',((ev.clientX-r.left)/r.width*100)+'%');s.style.setProperty('--sy',((ev.clientY-r.top)/r.height*100)+'%');});});}if(d.readyState!='loading')run();else d.addEventListener('DOMContentLoaded',run);})();
</script>
</body>
</html>

View File

@ -1,25 +0,0 @@
<!doctype html>
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
{{ head_html|safe }}
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col film-grain" style="font-family: var(--font-body, &quot;Cormorant Garamond&quot;, &quot;Cormorant&quot;, Georgia, serif);">
{{ admin_banner_html|safe }}
{% if slots.header %}<header class="w-full">{{ slots.header|safe }}</header>{% endif %}
<main class="flex-grow w-full">
<div class="max-w-6xl mx-auto px-4 py-12 md:py-16">
{% if slots.aside %}
<div class="grid gap-12 md:grid-cols-[2fr_1fr]">
<div>{% if slots.main %}{{ slots.main|safe }}{% endif %}</div>
<aside class="w-full md:border-l md:border-[hsl(var(--primary)/0.3)] md:pl-10">{{ slots.aside|safe }}</aside>
</div>
{% else %}
<div class="max-w-3xl mx-auto">{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="py-20 text-center"><p class="italic" style="color:hsl(var(--muted-foreground));">No content blocks assigned to this page.</p></div>{% endif %}</div>
{% endif %}
</div>
</main>
{% if slots.footer %}<footer class="w-full mt-auto border-t border-[hsl(var(--primary)/0.35)]">{{ slots.footer|safe }}</footer>{% endif %}
{{ body_end_html|safe }}
<script>
(function(){if(window.__gothamMotion)return;window.__gothamMotion=1;var d=document,root=d.documentElement;root.classList.add('gotham-js');var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;function pending(){return d.querySelectorAll('.reveal:not(.is-visible)');}function reveal(el){el.classList.add('is-visible');}function inView(){var h=window.innerHeight||root.clientHeight;pending().forEach(function(el){var r=el.getBoundingClientRect();if(r.top<h*1.05&&r.bottom>-h*0.25)reveal(el);});}function all(){pending().forEach(reveal);}function run(){if(reduce){all();return;}inView();window.addEventListener('scroll',inView,{passive:true});window.addEventListener('resize',inView,{passive:true});window.addEventListener('load',function(){inView();setTimeout(inView,120);},{once:true});setTimeout(inView,400);setTimeout(all,2600);d.querySelectorAll('[data-spotlight]').forEach(function(s){s.addEventListener('pointermove',function(ev){var r=s.getBoundingClientRect();s.style.setProperty('--sx',((ev.clientX-r.left)/r.width*100)+'%');s.style.setProperty('--sy',((ev.clientY-r.top)/r.height*100)+'%');});});}if(d.readyState!='loading')run();else d.addEventListener('DOMContentLoaded',run);})();
</script>
</body>
</html>

View File

@ -1,20 +1,11 @@
<!doctype html>
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
<html lang="en" class="dark">
{{ head_html|safe }}
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col film-grain" style="font-family: var(--font-body, &quot;Cormorant Garamond&quot;, &quot;Cormorant&quot;, Georgia, serif);">
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col">
{{ admin_banner_html|safe }}
<header class="w-full">
<div class="max-w-5xl mx-auto px-4">{{ slots.header|safe }}</div>
</header>
<main class="flex-grow max-w-3xl mx-auto w-full px-4 py-12 md:py-16">
{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="py-20 text-center"><p class="italic" style="color:hsl(var(--muted-foreground));">No content blocks assigned to this page.</p></div>{% endif %}
</main>
<footer class="w-full mt-auto border-t border-[hsl(var(--primary)/0.35)]">
<div class="max-w-5xl mx-auto px-4">{{ slots.footer|safe }}</div>
</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 }}
<script>
(function(){if(window.__gothamMotion)return;window.__gothamMotion=1;var d=document,root=d.documentElement;root.classList.add('gotham-js');var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;function pending(){return d.querySelectorAll('.reveal:not(.is-visible)');}function reveal(el){el.classList.add('is-visible');}function inView(){var h=window.innerHeight||root.clientHeight;pending().forEach(function(el){var r=el.getBoundingClientRect();if(r.top<h*1.05&&r.bottom>-h*0.25)reveal(el);});}function all(){pending().forEach(reveal);}function run(){if(reduce){all();return;}inView();window.addEventListener('scroll',inView,{passive:true});window.addEventListener('resize',inView,{passive:true});window.addEventListener('load',function(){inView();setTimeout(inView,120);},{once:true});setTimeout(inView,400);setTimeout(all,2600);d.querySelectorAll('[data-spotlight]').forEach(function(s){s.addEventListener('pointermove',function(ev){var r=s.getBoundingClientRect();s.style.setProperty('--sx',((ev.clientX-r.left)/r.width*100)+'%');s.style.setProperty('--sy',((ev.clientY-r.top)/r.height*100)+'%');});});}if(d.readyState!='loading')run();else d.addEventListener('DOMContentLoaded',run);})();
</script>
</body>
</html>

View File

@ -1,18 +1,11 @@
<!doctype html>
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
<html lang="en" class="dark">
{{ head_html|safe }}
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col film-grain" style="font-family: var(--font-body, &quot;Cormorant Garamond&quot;, &quot;Cormorant&quot;, Georgia, serif);">
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col">
{{ admin_banner_html|safe }}
<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="italic" style="color:hsl(var(--muted-foreground));">No content blocks assigned to this page.</p></div>{% endif %}
</main>
<footer class="w-full mt-auto border-t border-[hsl(var(--primary)/0.35)]">
<div class="max-w-5xl mx-auto px-4">{{ slots.footer|safe }}</div>
</footer>
<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 }}
<script>
(function(){if(window.__gothamMotion)return;window.__gothamMotion=1;var d=document,root=d.documentElement;root.classList.add('gotham-js');var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;function pending(){return d.querySelectorAll('.reveal:not(.is-visible)');}function reveal(el){el.classList.add('is-visible');}function inView(){var h=window.innerHeight||root.clientHeight;pending().forEach(function(el){var r=el.getBoundingClientRect();if(r.top<h*1.05&&r.bottom>-h*0.25)reveal(el);});}function all(){pending().forEach(reveal);}function run(){if(reduce){all();return;}inView();window.addEventListener('scroll',inView,{passive:true});window.addEventListener('resize',inView,{passive:true});window.addEventListener('load',function(){inView();setTimeout(inView,120);},{once:true});setTimeout(inView,400);setTimeout(all,2600);d.querySelectorAll('[data-spotlight]').forEach(function(s){s.addEventListener('pointermove',function(ev){var r=s.getBoundingClientRect();s.style.setProperty('--sx',((ev.clientX-r.left)/r.width*100)+'%');s.style.setProperty('--sy',((ev.clientY-r.top)/r.height*100)+'%');});});}if(d.readyState!='loading')run();else d.addEventListener('DOMContentLoaded',run);})();
</script>
</body>
</html>

View File

@ -1,19 +1,12 @@
<!doctype html>
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
<html lang="en" class="dark">
{{ head_html|safe }}
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col film-grain" style="font-family: var(--font-body, &quot;Cormorant Garamond&quot;, &quot;Cormorant&quot;, Georgia, serif);">
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col">
{{ admin_banner_html|safe }}
<section class="w-full">{{ slots.hero|safe }}</section>
<main class="flex-grow">
{% if slots.main %}{{ slots.main|safe }}{% endif %}
</main>
<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 border-t border-[hsl(var(--primary)/0.35)]">
<div class="max-w-5xl mx-auto px-4">{{ slots.footer|safe }}</div>
</footer>
<footer class="w-full mt-auto">{{ slots.footer|safe }}</footer>
{{ body_end_html|safe }}
<script>
(function(){if(window.__gothamMotion)return;window.__gothamMotion=1;var d=document,root=d.documentElement;root.classList.add('gotham-js');var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;function pending(){return d.querySelectorAll('.reveal:not(.is-visible)');}function reveal(el){el.classList.add('is-visible');}function inView(){var h=window.innerHeight||root.clientHeight;pending().forEach(function(el){var r=el.getBoundingClientRect();if(r.top<h*1.05&&r.bottom>-h*0.25)reveal(el);});}function all(){pending().forEach(reveal);}function run(){if(reduce){all();return;}inView();window.addEventListener('scroll',inView,{passive:true});window.addEventListener('resize',inView,{passive:true});window.addEventListener('load',function(){inView();setTimeout(inView,120);},{once:true});setTimeout(inView,400);setTimeout(all,2600);d.querySelectorAll('[data-spotlight]').forEach(function(s){s.addEventListener('pointermove',function(ev){var r=s.getBoundingClientRect();s.style.setProperty('--sx',((ev.clientX-r.left)/r.width*100)+'%');s.style.setProperty('--sy',((ev.clientY-r.top)/r.height*100)+'%');});});}if(d.readyState!='loading')run();else d.addEventListener('DOMContentLoaded',run);})();
</script>
</body>
</html>

View File

@ -1,21 +0,0 @@
{# announcement-bar override — gilded banner. Dismiss <script>, data-bn-annkey and localStorage keying preserved verbatim. Fields mirror builtin: variant, message, linkText, linkUrl, dismissable. #}
{% set anncls = "bg-primary/15 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-block-override="gotham:announcement-bar" data-bn-announcement data-bn-annkey="{{ variant|default:'info' }}:{{ message|slugify }}" class="bn-announcement w-full border-b border-[hsl(var(--primary)/0.4)] {{ 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="reel-caps text-xs">{{ 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 override — gold-framed player card. Fields + facade JS mirror builtin verbatim: url, media, artwork, title, artist, download. #}
<figure data-block-override="gotham:audio-embed" class="bn-audio reel-frame my-8 overflow-hidden bg-card text-card-foreground{% 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="reel-mat h-16 w-16 shrink-0 overflow-hidden 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="reel-caps truncate text-sm" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ title }}</p>{% endif %}{% if artist %}<p class="truncate text-sm italic" style="color:hsl(var(--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="reel-btn inline-flex items-center gap-2 text-sm" 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="reel-link inline-flex items-center gap-1 text-sm">::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,33 +0,0 @@
{# auth-form override — gilded login/register. Wiring is UNTOUCHABLE and reproduced exactly from builtin auth_blocks.go: single shared #auth-message, /api/auth/login (email,password) and /api/auth/register (username,email,password), hx-target #auth-message. Captcha widget injected from _captcha_widget when captchaEnabled (server enforces). Fields: default_tab, show_social (social omitted — needs Go/site config; graceful). #}
{% if context.isPublicLoggedIn %}
<div data-block-override="gotham:auth-form" class="auth-form-logged-in mx-auto max-w-md text-center py-8">
<p class="italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Cormorant Garamond', Georgia, serif);">You are signed in as <strong style="color:hsl(var(--foreground));">{{ context.publicUsername }}</strong></p>
<a href="/account" class="reel-link mt-3 inline-block">My account</a>
</div>
{% else %}
<div data-block-override="gotham:auth-form" class="auth-form reel-frame mx-auto max-w-md bg-card p-8">
<div id="auth-message"></div>
<div class="mb-6 flex border-b border-[hsl(var(--primary)/0.3)]">
<button type="button" onclick="document.getElementById('login-panel').classList.remove('hidden');document.getElementById('register-panel').classList.add('hidden');this.setAttribute('data-active','1');this.nextElementSibling.removeAttribute('data-active');" data-auth-tab {% if default_tab != "register" %}data-active="1"{% endif %} class="reel-caps flex-1 py-2 text-center text-xs border-b-2 border-transparent data-[active]:border-[hsl(var(--primary))] data-[active]:text-primary">Sign in</button>
<button type="button" onclick="document.getElementById('register-panel').classList.remove('hidden');document.getElementById('login-panel').classList.add('hidden');this.setAttribute('data-active','1');this.previousElementSibling.removeAttribute('data-active');" data-auth-tab {% if default_tab == "register" %}data-active="1"{% endif %} class="reel-caps flex-1 py-2 text-center text-xs border-b-2 border-transparent data-[active]:border-[hsl(var(--primary))] data-[active]:text-primary">Register</button>
</div>
<div id="login-panel" class="{% if default_tab == "register" %}hidden{% endif %}">
<form hx-post="/api/auth/login" hx-target="#auth-message" hx-swap="innerHTML">
<div class="mb-3"><label class="reel-caps block text-xs mb-1" style="color:hsl(var(--primary));">Email</label><input type="email" name="email" required class="w-full rounded-none border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:border-[hsl(var(--primary))] focus:ring-1 focus:ring-ring" /></div>
<div class="mb-4"><label class="reel-caps block text-xs mb-1" style="color:hsl(var(--primary));">Password</label><input type="password" name="password" required class="w-full rounded-none border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:border-[hsl(var(--primary))] focus:ring-1 focus:ring-ring" /></div>
{% if _captcha_widget %}{{ _captcha_widget|safe }}{% endif %}
<button type="submit" class="reel-btn w-full" hx-disabled-elt="this">Sign in</button>
<p class="text-center mt-3 text-sm"><a href="/forgot-password" class="reel-link">Forgot password?</a></p>
</form>
</div>
<div id="register-panel" class="{% if default_tab != "register" %}hidden{% endif %}">
<form hx-post="/api/auth/register" hx-target="#auth-message" hx-swap="innerHTML">
<div class="mb-3"><label class="reel-caps block text-xs mb-1" style="color:hsl(var(--primary));">Username</label><input type="text" name="username" required pattern="[a-zA-Z0-9_-]{3,30}" class="w-full rounded-none border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:border-[hsl(var(--primary))] focus:ring-1 focus:ring-ring" /></div>
<div class="mb-3"><label class="reel-caps block text-xs mb-1" style="color:hsl(var(--primary));">Email</label><input type="email" name="email" required class="w-full rounded-none border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:border-[hsl(var(--primary))] focus:ring-1 focus:ring-ring" /></div>
<div class="mb-4"><label class="reel-caps block text-xs mb-1" style="color:hsl(var(--primary));">Password</label><input type="password" name="password" required minlength="8" class="w-full rounded-none border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:border-[hsl(var(--primary))] focus:ring-1 focus:ring-ring" /></div>
{% if _captcha_widget %}{{ _captcha_widget|safe }}{% endif %}
<button type="submit" class="reel-btn w-full" hx-disabled-elt="this">Create account</button>
</form>
</div>
</div>
{% endif %}

View File

@ -1,31 +0,0 @@
{# auth-status override — header login state. Logged-out: Sign in / Reserve links. Logged-in: gold monogram + dropdown (logout posts /api/auth/logout). Driven by context.isPublicLoggedIn / context.publicDisplayName / context.publicUsername. #}
{% if context.isPublicLoggedIn %}
<div data-block-override="gotham:auth-status" class="auth-status relative">
<button type="button" onclick="this.nextElementSibling.classList.toggle('hidden')" class="reel-caps flex items-center gap-2 text-xs opacity-90 hover:opacity-100 px-3 py-1.5 transition-colors">
<span class="inline-flex h-7 w-7 items-center justify-center rounded-full text-[0.7rem] font-bold" style="background-color:hsl(var(--primary));color:hsl(var(--primary-foreground));">{{ context.publicDisplayName|default:context.publicUsername|first|upper }}</span>
<span>{{ context.publicDisplayName|default:context.publicUsername }}</span>
<svg class="w-4 h-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="hidden absolute right-0 top-full mt-1 w-48 reel-frame bg-card py-1 z-50">
<a href="/account" class="block px-4 py-2 text-sm text-foreground hover:bg-accent">My profile</a>
<a href="/account/reviews" class="block px-4 py-2 text-sm text-foreground hover:bg-accent">My reviews</a>
<hr class="my-1 reel-rule"/>
<button hx-post="/api/auth/logout" hx-swap="none" class="block w-full text-left px-4 py-2 text-sm text-destructive hover:bg-accent">Sign out</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>
{% else %}
<div data-block-override="gotham:auth-status" class="auth-status flex items-center gap-2">
<a href="/login" class="reel-caps text-xs px-3 py-1.5 opacity-80 hover:opacity-100 hover:text-primary transition-colors">Sign in</a>
<a href="/login?tab=register" class="reel-btn text-xs">Reserve</a>
</div>
{% endif %}

View File

@ -1,22 +0,0 @@
{# author-bio-hero (gotham) — author-page header from the `authors` provider. #}
{% if author %}
<section data-block-override="gotham:author-bio-hero" class="reel-rule-thick bg-background py-12 text-foreground md:py-16{% if class %} {{ class }}{% endif %}">
<div class="mx-auto flex max-w-3xl flex-col items-center px-4 text-center">
{% if showAvatar and author.avatar_url %}<div class="mb-6 h-24 w-24 overflow-hidden rounded-full bg-muted ring-2 ring-primary">{% img author.avatar_url alt=author.display_name class="h-full w-full object-cover" %}</div>{% endif %}
<div class="mb-4 flex items-center justify-center gap-4"><span class="reel-rule w-12"></span><span class="reel-rule w-12"></span></div>
<h1 class="reel-caps text-3xl md:text-4xl text-foreground" style="font-family:var(--font-heading, &quot;Antonio&quot;, &quot;Oswald&quot;, Georgia, serif)">{{ author.display_name }}</h1>
{% if author.job_title or author.company %}<p class="reel-caps mt-2 text-sm text-primary">{{ author.job_title }}{% if author.job_title and author.company %} · {% endif %}{{ author.company }}</p>{% endif %}
{% if author.bio %}<p class="mt-5 max-w-2xl leading-relaxed text-muted-foreground" style="font-family:var(--font-body, &quot;Cormorant Garamond&quot;, &quot;Cormorant&quot;, Georgia, serif)">{{ author.bio }}</p>{% endif %}
<div class="mt-6 flex flex-wrap items-center justify-center gap-4 text-sm text-muted-foreground">
{% 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-6 flex items-center justify-center gap-4">
{% 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,14 +0,0 @@
{# author-bio (gotham) — byline card sourced from the `post` provider's author_* fields. #}
{% if post.author_name %}
<aside data-block-override="gotham:author-bio" class="reel-frame bg-card p-6 text-card-foreground{% if class %} {{ class }}{% endif %}">
{% if heading %}<p class="reel-caps mb-4 text-xs text-primary">{{ 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 bg-muted ring-1 ring-[hsl(var(--primary)/0.5)]">{% img post.author_avatar alt=post.author_name class="h-full w-full object-cover" %}</div>{% endif %}
<div class="min-w-0">
<h3 class="reel-caps text-base text-card-foreground" style="font-family:var(--font-heading, &quot;Antonio&quot;, &quot;Oswald&quot;, Georgia, serif)">{% 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>
<span class="reel-rule mt-2 block w-10"></span>
{% if post.author_bio %}<p class="mt-2 text-sm text-muted-foreground" style="font-family:var(--font-body, &quot;Cormorant Garamond&quot;, &quot;Cormorant&quot;, Georgia, serif)">{{ post.author_bio }}</p>{% endif %}
</div>
</div>
</aside>
{% endif %}

View File

@ -1,24 +0,0 @@
{# blog-hero (gotham) — blog-index header + featured posts from the `posts` provider. #}
<section data-block-override="gotham:blog-hero" class="bg-background py-12 text-foreground md:py-16{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-6xl px-4">
<div class="mx-auto max-w-2xl text-center">
{% if title %}<h1 class="reel-caps reel-underline text-4xl md:text-5xl text-foreground" style="font-family:var(--font-heading, &quot;Antonio&quot;, &quot;Oswald&quot;, Georgia, serif)">{{ title }}</h1>{% endif %}
{% if subtitle %}<p class="mt-4 text-lg text-muted-foreground" style="font-family:var(--font-body, &quot;Cormorant Garamond&quot;, &quot;Cormorant&quot;, Georgia, serif)">{{ subtitle }}</p>{% endif %}
</div>
{% if posts %}
<div class="mt-12 grid grid-cols-1 gap-8 md:grid-cols-3">
{% for post in posts %}
<article class="group flex flex-col{% if forloop.First %} md:col-span-3 md:flex-row md:items-stretch md:gap-8{% endif %}">
{% if post.featured_image_url %}<a href="{{ post.url }}" class="reel-mat mb-4 block overflow-hidden 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-500 group-hover:scale-105" %}</a>{% endif %}
<div class="flex flex-col justify-center{% if forloop.First %} md:w-1/2{% endif %}">
<span class="reel-rule mb-4 block w-12"></span>
<h2 class="reel-caps {% if forloop.First %}text-2xl{% else %}text-lg{% endif %} leading-snug text-foreground" style="font-family:var(--font-heading, &quot;Antonio&quot;, &quot;Oswald&quot;, Georgia, serif)"><a href="{{ post.url }}" class="transition-colors hover:text-primary">{{ post.title }}</a></h2>
{% if showExcerpt and post.excerpt %}<p class="mt-3 text-sm text-muted-foreground" style="font-family:var(--font-body, &quot;Cormorant Garamond&quot;, &quot;Cormorant&quot;, Georgia, serif)">{{ post.excerpt }}</p>{% endif %}
<div class="mt-4 flex flex-wrap items-center gap-x-3 text-xs text-muted-foreground">{% if post.author_name %}<span class="reel-caps text-primary">{{ 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 (gotham) — 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" data-block-override="gotham:breadcrumbs" class="py-3{% if class %} {{ class }}{% endif %}">
<ol class="flex flex-wrap items-center gap-1 text-xs">
{% if showHome %}<li class="flex items-center"><a href="/" class="reel-caps text-muted-foreground 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-3.5 w-3.5 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="reel-caps text-foreground" aria-current="page">{{ item.label }}</span>{% endif %}{% else %}<a href="{{ item.url }}" class="reel-caps text-muted-foreground transition-colors hover:text-primary">{{ item.label }}</a>{% endif %}
</li>
{% endfor %}
</ol>
</nav>

View File

@ -1,10 +0,0 @@
{# button override — gold-ringed lacquer pill. Fields (new builtin contract, per OVERRIDE-CONTRACT §4): link, style, label, size, icon, iconPosition, newTab, align. #}
{% with align=align|default:"left" %}
<div data-block-override="gotham:button" class="bn-button flex {% if align == "center" %}justify-center{% elif align == "right" %}justify-end{% else %}justify-start{% endif %}{% if class %} {{ class }}{% endif %}">
{% if style == "ghost" or style == "link" or style == "secondary" %}
<a href="{{ link|default:'#' }}"{% if newTab %} target="_blank" rel="noopener"{% endif %} class="reel-link inline-flex items-center gap-2 text-sm">{% if icon and iconPosition != "right" %}::{{ icon }}:sm::{% endif %}{{ label|default:"Learn more" }}{% if icon and iconPosition == "right" %}::{{ icon }}:sm::{% endif %}</a>
{% else %}
<a href="{{ link|default:'#' }}"{% if newTab %} target="_blank" rel="noopener"{% endif %} class="reel-btn inline-flex items-center gap-2">{% if icon and iconPosition != "right" %}::{{ icon }}:sm::{% endif %}{{ label|default:"Learn more" }}{% if icon and iconPosition == "right" %}::{{ icon }}:sm::{% endif %}</a>
{% endif %}
</div>
{% endwith %}

View File

@ -1,4 +0,0 @@
{# card override — angular gold Deco panel. Fields mirror builtin: layout, link, media, title, text, linkLabel. Media via {% img %}. #}
{% with layout=layout|default:"vertical" %}
<article data-block-override="gotham:card" class="bn-card reel-frame overflow-hidden bg-card text-card-foreground transition-shadow hover:reel-foil {% 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="reel-caps text-lg" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ title }}</h3>{% endif %}{% if text %}<div class="text-sm leading-relaxed" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Cormorant Garamond', Georgia, serif);">{{ text|safe }}</div>{% endif %}{% if linkLabel and link %}<span class="mt-2 inline-flex items-center gap-1 text-sm reel-link">{{ linkLabel }}::lucide:arrow-right:sm::</span>{% endif %}</div>{% if link %}</a>{% endif %}</article>
{% endwith %}

View File

@ -1,18 +0,0 @@
{# category-list (gotham) — from the `categories` provider. list or pills. #}
{% set pills = style == "pills" %}
<section data-block-override="gotham:category-list" class="bg-background text-foreground{% if class %} {{ class }}{% endif %}">
{% if heading %}<h2 class="reel-caps mb-5 text-xs text-primary">{{ heading }}</h2>{% endif %}
{% if pills %}
<div class="flex flex-wrap gap-2">
{% for cat in categories %}
<a href="{{ cat.url }}" class="reel-caps inline-flex items-center gap-1.5 rounded-none border border-[hsl(var(--primary)/0.35)] bg-card px-4 py-1.5 text-xs text-card-foreground transition-colors hover:border-primary 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="reel-rule flex items-center justify-between px-3 py-2.5 text-sm text-muted-foreground transition-colors hover:text-primary"><span class="reel-caps">{{ cat.name }}</span>{% if showCount %}<span class="rounded-none border border-[hsl(var(--primary)/0.35)] px-2 py-0.5 text-xs text-muted-foreground">{{ cat.post_count }}</span>{% endif %}</a></li>
{% endfor %}
</ul>
{% endif %}
</section>

View File

@ -1,17 +0,0 @@
<section data-block-override="gotham:contact-card" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-4xl px-4 text-center">
{% if heading %}<h2 class="reel-caps reel-underline text-3xl md:text-4xl text-foreground" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ heading }}</h2>{% endif %}
{% if description %}<p class="mt-4 mx-auto max-w-2xl text-lg text-muted-foreground" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ description }}</p>{% endif %}
<div class="mt-12 grid grid-cols-1 gap-6 text-left {% 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="reel-frame reel-foil flex items-start gap-4 rounded-none bg-card p-6 text-card-foreground">
{% if channel.icon %}<span class="inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-none border border-[hsl(var(--primary)/0.45)] 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="reel-caps text-xs tracking-[0.16em] text-muted-foreground">{{ channel.label }}</div>{% endif %}
{% if channel.value %}<div class="mt-1 break-words text-lg text-foreground" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{% if channel.link %}<a href="{{ channel.link }}" class="reel-link transition-colors hover:text-primary">{{ channel.value }}</a>{% else %}{{ channel.value }}{% endif %}</div>{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
</section>

View File

@ -1,43 +0,0 @@
{# contact-form override — reservations enquiry. ALL form wiring preserved verbatim from builtin: bid, {% form %} action/hx attrs, honeypot, field loop, form.submitted/errors, submit target. Only inputs restyled to Art Deco. #}
{% with bid=_block_id|default:context.blockId %}
<section data-block-override="gotham:contact-form" 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="reel-frame bg-card p-10 text-center">
<div class="mx-auto mb-4 inline-flex h-12 w-12 items-center justify-center rounded-full text-primary" style="box-shadow:inset 0 0 0 1px hsl(var(--primary));">::lucide:check:lg::</div>
<p class="text-lg italic" style="font-family:var(--font-body, 'Cormorant Garamond', Georgia, serif);">{{ form.message|default:successMessage|default:"Thank you. Your table request has been received." }}</p>
</div>
{% else %}
<div class="text-center">
{% if heading %}<h2 class="reel-caps text-3xl md:text-4xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ heading }}</h2>{% endif %}
{% if description %}<p class="mt-3 text-lg italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Cormorant Garamond', Georgia, serif);">{{ description }}</p>{% endif %}
<div class="mx-auto mt-5 mb-2 h-6 w-40 reel-strip" aria-hidden="true"></div>
</div>
{% if form.errors._form %}<div class="mt-6 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="reel-caps mb-1.5 block text-xs" style="color:hsl(var(--primary));">{{ 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-none border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus:outline-none focus:border-[hsl(var(--primary))] focus:ring-1 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-none border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:border-[hsl(var(--primary))] focus:ring-1 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-none border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus:outline-none focus:border-[hsl(var(--primary))] focus:ring-1 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 flex justify-center">
<button type="submit" class="reel-btn inline-flex items-center justify-center">{{ submitLabel|default:"Request a table" }}</button>
</div>
{% endform %}
{% endwith %}
{% endif %}
</div>
</section>
{% endwith %}

View File

@ -1,48 +0,0 @@
<section data-block-override="gotham:countdown" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-4xl px-4 text-center">
{% if heading %}<h2 class="reel-caps text-3xl tracking-tight md:text-4xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-lg text-muted-foreground" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ 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" %}reel-frame min-w-[4.5rem] bg-card px-3 py-4 text-card-foreground{% endif %}"><span class="text-4xl font-bold tabular-nums text-primary sm:text-5xl" style="font-family:var(--font-mono)" data-unit="days">--</span><span class="reel-caps mt-1 text-xs text-muted-foreground">{{ labelDays|default:"Days" }}</span></div>
<div class="flex flex-col items-center {% if variant == "boxes" %}reel-frame min-w-[4.5rem] bg-card px-3 py-4 text-card-foreground{% endif %}"><span class="text-4xl font-bold tabular-nums text-primary sm:text-5xl" style="font-family:var(--font-mono)" data-unit="hours">--</span><span class="reel-caps mt-1 text-xs text-muted-foreground">{{ labelHours|default:"Hours" }}</span></div>
<div class="flex flex-col items-center {% if variant == "boxes" %}reel-frame min-w-[4.5rem] bg-card px-3 py-4 text-card-foreground{% endif %}"><span class="text-4xl font-bold tabular-nums text-primary sm:text-5xl" style="font-family:var(--font-mono)" data-unit="minutes">--</span><span class="reel-caps mt-1 text-xs text-muted-foreground">{{ labelMinutes|default:"Minutes" }}</span></div>
<div class="flex flex-col items-center {% if variant == "boxes" %}reel-frame min-w-[4.5rem] bg-card px-3 py-4 text-card-foreground{% endif %}"><span class="text-4xl font-bold tabular-nums text-primary sm:text-5xl" style="font-family:var(--font-mono)" data-unit="seconds">--</span><span class="reel-caps mt-1 text-xs text-muted-foreground">{{ labelSeconds|default:"Seconds" }}</span></div>
</div>
<div class="mt-8 hidden" data-countdown-expired>
<p class="reel-caps text-xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ expiredMessage|default:"This has started." }}</p>
{% if expiredUrl and expiredLabel %}<a href="{{ expiredUrl }}" class="reel-btn mt-4 inline-flex items-center gap-2">{{ 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,14 +0,0 @@
<section data-block-override="gotham:cta" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-6xl px-4">
<div class="{% if background == "band" %}reel-frame film-grain bg-primary px-6 py-14 text-center text-primary-foreground md:px-12{% elif background == "card" %}reel-frame bg-card px-6 py-14 text-center text-card-foreground md:px-12{% else %}reel-frame flex flex-col items-center gap-8 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="reel-caps text-3xl tracking-tight md:text-4xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ heading }}</h2>{% endif %}
{% if text %}<p class="mt-3 text-lg {% if background == "band" %}text-primary-foreground/80{% else %}text-muted-foreground{% endif %}" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ 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="reel-btn inline-flex items-center gap-2">{{ 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="reel-link inline-flex items-center">{{ secondaryLabel }}</a>{% endif %}
</div>
</div>
</div>
</section>

View File

@ -1,13 +0,0 @@
{# divider override — folds in the old divider_fan: gold geometric rules. Fields mirror builtin: variant (line|ornament|spacer-rule), weight, width, icon. ornament renders a sunburst fan crest; spacer-rule a short centred gold rule; line a hairline gold rule. #}
{% with variant=variant|default:"line" weight=weight|default:"thin" width=width|default:"full" %}
{% if variant == "ornament" %}
<div data-block-override="gotham:divider" class="bn-divider mx-auto my-10 flex flex-col items-center gap-2 {% if width == "narrow" %}max-w-[200px]{% elif width == "wide" %}max-w-3xl{% else %}w-full{% endif %}{% if class %} {{ class }}{% endif %}" role="separator">
<div class="h-8 w-40 reel-strip" aria-hidden="true"></div>
<div class="flex w-full items-center gap-4"><span class="reel-rule flex-1"></span><span style="color:hsl(var(--primary));">::{{ icon|default:"lucide:diamond" }}:sm::</span><span class="reel-rule flex-1"></span></div>
</div>
{% elif variant == "spacer-rule" %}
<div data-block-override="gotham:divider" class="bn-divider mx-auto py-10 flex justify-center {% if width == "narrow" %}max-w-[200px]{% elif width == "wide" %}max-w-3xl{% else %}w-full{% endif %}{% if class %} {{ class }}{% endif %}" role="separator"><span class="{% if weight == "thick" %}reel-rule-thick{% else %}reel-rule{% endif %} w-16"></span></div>
{% else %}
<div data-block-override="gotham:divider" class="bn-divider mx-auto my-8 flex justify-center {% if width == "narrow" %}max-w-[200px]{% elif width == "wide" %}max-w-3xl{% else %}w-full{% endif %}{% if class %} {{ class }}{% endif %}" role="separator"><span class="{% if weight == "thick" %}reel-rule-thick{% else %}reel-rule{% endif %} w-full"></span></div>
{% endif %}
{% endwith %}

View File

@ -1,40 +0,0 @@
<section data-block-override="gotham:event-list" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-6xl px-4 text-center">
{% if heading %}<h2 class="reel-caps reel-underline text-3xl md:text-4xl text-foreground" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mt-4 mx-auto max-w-2xl text-lg text-muted-foreground" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ intro }}</p>{% endif %}
{% if layout == "cards" %}
<div class="mt-12 grid grid-cols-1 gap-8 text-left sm:grid-cols-2 lg:grid-cols-3">
{% for event in events %}{% if not (hidePast and event.past) %}
<article class="reel-frame reel-foil flex flex-col overflow-hidden rounded-none bg-card text-card-foreground{% if event.past %} opacity-60{% endif %}">
{% if event.media %}<div class="reel-mat 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-6">
<div class="reel-caps text-xs tracking-[0.18em] 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="text-xl text-foreground" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ event.title }}</h3>
{% if event.venue %}<p class="reel-caps text-xs tracking-[0.14em] text-muted-foreground">{{ event.venue }}</p>{% endif %}
{% if event.description %}<p class="text-sm text-muted-foreground" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ event.description }}</p>{% endif %}
{% if event.link %}<a href="{{ event.link }}" class="reel-link mt-auto inline-flex items-center gap-2 pt-3 text-sm text-primary">{{ 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-12 divide-y divide-[hsl(var(--primary)/0.35)] border-y-2 border-[hsl(var(--primary)/0.35)] text-left">
{% for event in events %}{% if not (hidePast and event.past) %}
<li class="flex flex-col gap-3 py-6 sm:flex-row sm:items-center sm:gap-8{% if event.past %} opacity-60{% endif %}">
<div class="shrink-0 sm:w-52">
<div class="reel-caps text-sm tracking-[0.16em] text-primary"><time{% if event.datetime %} datetime="{{ event.datetime }}"{% endif %}>{{ event.start }}</time></div>
{% if event.end %}<div class="text-xs text-muted-foreground">to {{ event.end }}</div>{% endif %}
{% if event.timezone %}<div class="text-xs text-muted-foreground">{{ event.timezone }}</div>{% endif %}
</div>
<div class="min-w-0 flex-1">
<h3 class="text-lg text-foreground" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ event.title }}</h3>
{% if event.venue %}<p class="reel-caps text-xs tracking-[0.14em] text-muted-foreground">{{ event.venue }}</p>{% endif %}
{% if event.description %}<p class="mt-1 text-sm text-muted-foreground" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ event.description }}</p>{% endif %}
</div>
{% if event.link %}<a href="{{ event.link }}" class="reel-btn inline-flex shrink-0 items-center gap-2">{{ event.linkLabel|default:"Details" }}</a>{% endif %}
</li>
{% endif %}{% endfor %}
</ul>
{% endif %}
</div>
</section>

View File

@ -1,17 +0,0 @@
<section data-block-override="gotham:faq" 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="reel-caps text-center text-3xl text-foreground md:text-4xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ intro }}</p>{% endif %}
<div class="mt-10 divide-y divide-[hsl(var(--primary)/0.25)] border-y border-[hsl(var(--primary)/0.4)]">
{% 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" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">
<span>{{ 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" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ item.answer|safe }}</div>
</details>
{% endfor %}
</div>
</div>
</section>

View File

@ -1,17 +0,0 @@
<section data-block-override="gotham:feature-grid" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-6xl px-4">
{% if eyebrow %}<div class="mb-3 flex items-center justify-center gap-4"><span class="reel-rule w-12"></span><p class="reel-caps text-xs text-primary">{{ eyebrow }}</p><span class="reel-rule w-12"></span></div>{% endif %}
{% if heading %}<h2 class="reel-caps mt-2 text-center text-3xl text-foreground md:text-4xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ 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" %}reel-frame bg-card p-6 text-card-foreground reel-foil transition-shadow{% elif layout == "centered" %}text-center{% endif %}">
{% if item.icon %}<div class="mb-4 inline-flex h-11 w-11 items-center justify-center border border-[hsl(var(--primary)/0.35)] 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="reel-caps text-lg text-foreground" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{% 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" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ item.text }}</p>{% endif %}
{% if item.linkUrl and item.linkLabel %}<a href="{{ item.linkUrl }}" class="reel-link mt-3 inline-flex items-center gap-1 text-sm">{{ 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,24 +0,0 @@
{# featured-posts (gotham) — grid of cards from the `posts` provider #}
{# (params: limit, featured=true). Card: image, title, excerpt, meta. #}
<section data-block-override="gotham:featured-posts" 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="reel-caps reel-underline mb-10 text-center text-2xl md:text-3xl text-foreground" style="font-family:var(--font-heading, &quot;Antonio&quot;, &quot;Oswald&quot;, Georgia, serif)">{{ heading }}</h2>{% endif %}
<div class="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 %}">
{% for post in posts %}
<article class="reel-frame group flex flex-col overflow-hidden bg-card text-card-foreground transition-shadow">
{% 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-500 group-hover:scale-105" %}</a>{% endif %}
<div class="flex flex-1 flex-col p-6 text-center">
<h3 class="reel-caps text-lg leading-snug text-card-foreground" style="font-family:var(--font-heading, &quot;Antonio&quot;, &quot;Oswald&quot;, Georgia, serif)"><a href="{{ post.url }}" class="transition-colors hover:text-primary">{{ post.title }}</a></h3>
<span class="reel-rule mx-auto mt-3 block w-10"></span>
{% if showExcerpt and post.excerpt %}<p class="mt-3 flex-1 text-sm text-muted-foreground" style="font-family:var(--font-body, &quot;Cormorant Garamond&quot;, &quot;Cormorant&quot;, Georgia, serif)">{{ post.excerpt }}</p>{% endif %}
<div class="mt-4 flex flex-wrap items-center justify-center gap-x-3 gap-y-1 text-xs text-muted-foreground">
{% if showAuthor and post.author_name %}<span class="reel-caps text-primary">{{ 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,40 +0,0 @@
{# footer override — folds in the old footer block: centred gilded footer with gold rule, address, reservations, social. Structure/fields/signup_form preserved from builtin; reskinned to Art Deco. #}
{% set fl = layout %}
{% if not fl %}{% if template == "centered" %}{% set fl = "centered" %}{% else %}{% set fl = "columns" %}{% endif %}{% endif %}
{% set fbg = backgroundColor|default:"bg-foreground text-background" %}
{% macro sociallinks(cls) %}{% if social_items %}<div class="flex items-center gap-4 {{ cls }}">{% for s in social_items %}<a href="{{ s.url }}" target="_blank" rel="noopener" title="{{ s.label }}" class="text-background/70 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="reel-caps text-sm text-primary">{{ newsletterTitle|default:"Join the guest list" }}</h3>{% if newsletterDescription %}<p class="mt-1 text-sm italic text-background/70">{{ 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-[hsl(var(--primary)/0.4)] flex flex-col sm:flex-row items-center justify-between gap-3 text-sm text-background/60"><p>{% 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 data-block-override="gotham:footer" class="w-full border-t-2 border-[hsl(var(--primary))] {{ 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="reel-caps text-lg" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);">{{ companyName }}</span>{% endif %}{% if tagline %}<span class="text-sm italic text-background/70 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="reel-caps text-2xl text-primary" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);">{{ companyName }}</span>{% endif %}
<div class="h-6 w-40 reel-strip" aria-hidden="true"></div>
{% if tagline %}<p class="max-w-md text-sm italic text-background/70">{{ 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="reel-caps text-xs text-background/80 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="reel-caps text-xl text-primary" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);">{{ companyName }}</span>{% endif %}
{% if tagline %}<p class="mt-3 max-w-xs text-sm italic text-background/70">{{ tagline }}</p>{% endif %}
{% if address or email or phone %}<address class="mt-4 not-italic text-sm text-background/70 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="reel-caps text-xs text-primary">{{ 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-background/70 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 override — folds in the old gallery_fan: symmetric gold-framed grid. Fields + lightbox JS mirror builtin verbatim: layout, gap, columns, lightbox, images[].src/.alt/.caption. #}
{% with layout=layout|default:"grid" gap=gap|default:"md" cols=columns|default:3 %}<div data-block-override="gotham:gallery" class="bn-gallery my-8{% 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="reel-mat group relative block overflow-hidden bg-muted 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 @@
{# heading override — gilded Antonio display caps with a hairline gold underline. Fields: text, level, textClass (mirrors builtin). #}
{% if level == 1 %}<h1 data-block-override="gotham:heading" class="reel-caps reel-underline text-4xl md:text-5xl flex-1 {{ textClass }}" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ text }}</h1>
{% elif level == 3 %}<h3 data-block-override="gotham:heading" class="reel-caps reel-underline text-2xl md:text-3xl flex-1 {{ textClass }}" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ text }}</h3>
{% elif level == 4 %}<h4 data-block-override="gotham:heading" class="reel-caps reel-underline text-xl md:text-2xl flex-1 {{ textClass }}" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ text }}</h4>
{% elif level == 5 %}<h5 data-block-override="gotham:heading" class="reel-caps reel-underline text-lg md:text-xl flex-1 {{ textClass }}" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ text }}</h5>
{% elif level == 6 %}<h6 data-block-override="gotham:heading" class="reel-caps reel-underline text-base md:text-lg flex-1 {{ textClass }}" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ text }}</h6>
{% else %}<h2 data-block-override="gotham:heading" class="reel-caps reel-underline text-3xl md:text-4xl flex-1 {{ textClass }}" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ text }}</h2>{% endif %}
{% if level|integer == 1 %}<h1 class="text-4xl font-bold tracking-tight text-accent {{ textClass }}">{{ text }}</h1>{% elif level|integer == 3 %}<h3 class="text-2xl font-semibold text-accent {{ textClass }}">{{ text }}</h3>{% elif level|integer == 4 %}<h4 class="text-xl font-semibold text-accent {{ textClass }}">{{ text }}</h4>{% elif level|integer == 5 %}<h5 class="text-lg font-medium text-accent {{ textClass }}">{{ text }}</h5>{% elif level|integer == 6 %}<h6 class="text-base font-medium text-accent {{ textClass }}">{{ text }}</h6>{% else %}<h2 class="text-3xl font-semibold tracking-tight text-accent {{ textClass }}">{{ text }}</h2>{% endif %}

View File

@ -1,42 +0,0 @@
{# hero override — folds in the old marquee_hero: sunburst overlay + stepped ziggurat framing, gold-foil Antonio headline. Structure/fields/{% img %} calls preserved from builtin. Styled-text fields accept {text,color} or a bare string. #}
{% 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 %}<div class="mb-5 flex items-center gap-4 {% if align == "center" %}justify-center{% endif %}"><span class="reel-rule w-10"></span><span class="reel-caps text-xs text-primary">{{ eyebrowText }}</span><span class="reel-rule w-10"></span></div>{% endif %}
{% if headlineText %}<h1 class="reel-caps text-4xl sm:text-5xl lg:text-6xl leading-tight" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ headlineText }}</h1>{% endif %}
{% if subText %}<h2 class="mt-5 text-xl sm:text-2xl italic max-w-2xl{% if align == "center" %} mx-auto{% endif %}" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Cormorant Garamond', Georgia, serif);">{{ subText }}</h2>{% endif %}
{% if descText %}<p class="mt-4 max-w-xl leading-relaxed{% if align == "center" %} mx-auto{% endif %}" style="color:hsl(var(--muted-foreground));">{{ 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="reel-btn inline-flex items-center justify-center gap-2">{{ pText }}</a>{% endif %}
{% if sText and sUrl %}<a href="{{ sUrl }}" class="reel-link inline-flex items-center justify-center">{{ sText }}</a>{% endif %}
</div>{% endif %}
{% if trustLogos %}<div class="mt-10">
<p class="reel-caps text-xs text-muted-foreground mb-3">Acclaim</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 data-block-override="gotham:hero" class="hero grid grid-cols-1 lg:grid-cols-2 overflow-hidden bg-background text-foreground border-y border-[hsl(var(--primary)/0.5)]{% if class %} {{ class }}{% endif %}">
<div class="relative min-h-[320px] bg-muted film-grain {% 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" %}<div class="absolute inset-0 letterbox-scrim" aria-hidden="true"></div>{% endif %}</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="reveal max-w-lg text-left">{{ herocontent("left") }}</div></div>
</section>
{% else %}
{% set align = "center" %}
{% if variant == "left-aligned" %}{% set align = "left" %}{% endif %}
<section data-block-override="gotham:hero" data-spotlight class="hero relative flex flex-col overflow-hidden bg-background text-foreground film-grain {% if not hasImage %}spotlight gotham-hero-plate{% elif 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 class="absolute inset-0 letterbox-scrim" aria-hidden="true"></div></div>{% endif %}
<div class="pointer-events-none absolute inset-x-0 top-0 z-[2] h-px bg-[hsl(var(--primary))]" aria-hidden="true"></div>
<div class="pointer-events-none absolute inset-x-0 bottom-0 z-[2] h-px bg-[hsl(var(--primary))]" aria-hidden="true"></div>
<div class="pointer-events-none absolute inset-0 z-[1]" aria-hidden="true" style="background-image:radial-gradient(circle at var(--sx,50%) var(--sy,42%), hsl(var(--primary) / 0.14), transparent 60%);"></div>
<div class="reveal relative z-10 mx-auto w-full max-w-4xl px-6 py-16 sm:px-10 sm:py-20 {% if variant == "left-aligned" %}text-left{% else %}text-center{% endif %}">{{ herocontent(align) }}</div>
</section>
{% endif %}

View File

@ -1,31 +0,0 @@
{# hours-location override — gilded hours ledger + framed map. Map facade <button onclick> preserved verbatim. Fields mirror builtin: heading, hours[].day/.hours/.closed, specialHoursNote, address, phone, directionsUrl, mapEmbedUrl, mapButtonLabel. #}
<section data-block-override="gotham:hours-location" 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="reel-caps text-3xl md:text-4xl text-center" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ heading }}</h2>{% endif %}
<div class="mt-10 grid grid-cols-1 gap-10 md:grid-cols-2">
<div class="reel-frame bg-card p-6 md:p-8">
{% if hours %}
<table class="w-full text-sm">
<tbody class="divide-y divide-[hsl(var(--primary)/0.2)]">
{% for row in hours %}
<tr>
<th scope="row" class="reel-caps py-2 pr-4 text-left text-xs font-medium" style="color:hsl(var(--foreground));">{{ row.day }}</th>
<td class="py-2 text-right tabular-nums {% if row.closed %}italic{% endif %}" style="{% if row.closed %}color:hsl(var(--muted-foreground));{% else %}color:hsl(var(--primary));{% endif %}">{% if row.closed %}Closed{% else %}{{ row.hours }}{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% if specialHoursNote %}<p class="mt-4 border-t border-[hsl(var(--primary)/0.3)] pt-3 text-sm italic" style="color:hsl(var(--muted-foreground));">{{ specialHoursNote }}</p>{% endif %}
{% if address %}<div class="mt-6"><div class="reel-caps text-xs" style="color:hsl(var(--primary));">Address</div><address class="mt-1 whitespace-pre-line not-italic" style="font-family:var(--font-body, 'Cormorant Garamond', Georgia, serif);">{{ address }}</address></div>{% endif %}
{% if phone %}<div class="mt-4"><div class="reel-caps text-xs" style="color:hsl(var(--primary));">Reservations</div><a href="tel:{{ phone }}" class="reel-link mt-1 inline-block">{{ phone }}</a></div>{% endif %}
{% if directionsUrl %}<a href="{{ directionsUrl }}" target="_blank" rel="noopener noreferrer" class="reel-btn mt-6 inline-flex items-center gap-2">::lucide:navigation:sm:: Get directions</a>{% endif %}
</div>
{% if mapEmbedUrl %}
<div class="reel-mat aspect-square w-full overflow-hidden bg-muted 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="reel-caps flex h-full min-h-[16rem] w-full items-center justify-center gap-2 text-sm transition-colors hover:bg-accent hover:text-accent-foreground" style="color:hsl(var(--primary));">::lucide:map-pin:md:: {{ mapButtonLabel|default:"Load map" }}</button>
</div>
{% endif %}
</div>
</div>
</section>

View File

@ -1,11 +0,0 @@
{# image override — stepped gold Deco frame. Fields mirror builtin: src, alt, caption, size, align, aspectRatio, objectFit. Media resolved via {% img %} (media filter is not registered in the block render path). #}
{% with size=size|default:"full" align=align|default:"center" objectFit=objectFit|default:"cover" %}
<figure data-block-override="gotham:image" class="my-8 flex-1 {% if align == "left" %}flex justify-start{% elif align == "right" %}flex justify-end{% else %}flex justify-center{% endif %}{% if class %} {{ class }}{% endif %}">
<div class="reel-mat {% 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 %}">
<div class="relative overflow-hidden bg-muted {% if aspectRatio == "square" %}aspect-square{% elif aspectRatio == "video" %}aspect-video{% elif aspectRatio == "portrait" %}aspect-[3/4]{% elif aspectRatio == "wide" %}aspect-[21/9]{% 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>
</div>
{% if caption %}<figcaption class="mt-3 text-center text-sm italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Cormorant Garamond', Georgia, serif);">{{ caption }}</figcaption>{% endif %}
</figure>
{% endwith %}

View File

@ -1,15 +0,0 @@
<section data-block-override="gotham:logo-strip" 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 %}<div class="mb-8 flex items-center justify-center gap-4"><span class="reel-rule w-12"></span><p class="reel-caps text-xs text-muted-foreground">{{ heading }}</p><span class="reel-rule w-12"></span></div>{% 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 override — the supper-club menu card: centred Antonio section titles, gold leader rules, symmetric columns. Fields mirror builtin: heading, intro, columns, sections[].title/.note/.items[].name/.badges/.description/.price, currency, footnote. #}
<section data-block-override="gotham:menu-list" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-5xl px-4 text-center">
{% if heading %}<h2 class="reel-caps text-3xl md:text-4xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mx-auto mt-3 max-w-2xl text-lg italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Cormorant Garamond', Georgia, serif);">{{ intro }}</p>{% endif %}
<div class="mx-auto mt-6 mb-4 h-6 w-40 reel-strip" aria-hidden="true"></div>
<div class="mt-6 grid grid-cols-1 gap-x-14 gap-y-12 text-left {% if columns == 2 %}md:grid-cols-2{% endif %}">
{% for section in sections %}
<div class="menu-section">
{% if section.title %}<h3 class="reel-caps reel-underline pb-2 text-center text-xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--primary));">{{ section.title }}</h3>{% endif %}
{% if section.note %}<p class="mt-2 text-center text-sm italic" style="color:hsl(var(--muted-foreground));">{{ section.note }}</p>{% endif %}
<ul class="mt-5 space-y-5">
{% for item in section.items %}
<li>
<div class="flex items-baseline gap-3">
<span class="shrink-0 font-medium" style="color:hsl(var(--foreground));">{{ item.name }}</span>
<span class="min-w-6 flex-1 translate-y-[-2px] border-b border-dotted border-[hsl(var(--primary)/0.5)]" aria-hidden="true"></span>
{% if item.price %}<span class="shrink-0 tabular-nums" style="color:hsl(var(--primary));font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);">{% if currency %}{{ currency }}{% endif %}{{ item.price }}</span>{% endif %}
</div>
{% if item.badges %}<div class="mt-1 flex flex-wrap gap-1.5">{% for badge in item.badges %}{% if badge.label %}<span class="reel-caps inline-block border border-[hsl(var(--primary)/0.4)] px-2 py-0.5 text-[0.65rem]" style="color:hsl(var(--muted-foreground));">{{ badge.label }}</span>{% endif %}{% endfor %}</div>{% endif %}
{% if item.description %}<p class="mt-1 text-sm italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Cormorant Garamond', Georgia, serif);">{{ item.description }}</p>{% endif %}
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
</div>
{% if footnote %}<p class="mt-12 text-xs" style="color:hsl(var(--muted-foreground));">{{ footnote }}</p>{% endif %}
</div>
</section>

View File

@ -1,104 +0,0 @@
{# navbar override — gilded masthead. Structure, fixed drawer ids, focus-trap JS and all data-* preserved verbatim from the builtin; presentation reskinned to Art Deco. Fields identical: variant/stickyBehavior/megaMenu/logoType/logoText/logo/companyName/cta/menu.items/... #}
{% 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="reel-caps px-3 py-2 text-xs 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="reel-caps px-3 py-2 text-xs 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="reel-frame p-6 bg-card 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="reel-caps px-2 py-1 text-[0.65rem] text-primary border-b border-[hsl(var(--primary)/0.3)] 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 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="reel-frame py-2 bg-card">{% for child in item.children %}{% if child.item_type == "header" %}<div class="reel-caps px-4 py-2 text-[0.65rem] text-primary">{{ 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="reel-caps text-xl {{ logoText.color }}" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ 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="reel-caps text-xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ companyName }}</span>{% endif %}</a>{% endmacro %}
{% macro ctabtn(extra) %}{% if cta_text %}<a href="{{ cta_url|default:'#' }}" class="reel-btn inline-flex items-center {{ extra }}">{{ cta_text }}</a>{% endif %}{% endmacro %}
<nav data-block-override="gotham:navbar" 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 }} border-b border-[hsl(var(--primary)/0.4)]{% endif %} {{ class }}">
{% if showUtilityBar %}<div class="w-full text-xs {% if not showOnMobile %}hidden sm:block{% endif %} bg-foreground text-background"><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 border-l border-[hsl(var(--primary)/0.4)]" role="dialog" aria-modal="true" aria-label="Site menu">
<div class="flex items-center justify-between p-4 border-b border-[hsl(var(--primary)/0.3)]">{{ 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="reel-caps flex items-center justify-between px-6 py-3 text-sm 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="reel-caps block px-6 py-3 text-sm hover:bg-accent transition-colors">{{ item.label }}</a>{% endif %}{% endfor %}</nav>
{% if cta_text %}<div class="p-4 border-t border-[hsl(var(--primary)/0.3)]"><a href="{{ cta_url|default:'#' }}" class="reel-btn block w-full text-center">{{ 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(--primary) / 0.4);}
</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,13 +0,0 @@
{# newsletter-signup (gotham) — the {% signup_form %} tag carries the #}
{# untouchable subscribe wiring (/api/lists/subscribe HTMX post + honeypot); #}
{# this template supplies only the gotham chrome around it. #}
<section data-block-override="gotham:newsletter-signup" 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="reel-frame {% if layout == "inline" %}film-grain bg-primary px-6 py-12 text-primary-foreground md:px-12{% else %}bg-card px-6 py-12 text-card-foreground md:px-10{% endif %} text-center">
<div class="mb-4 flex items-center justify-center gap-4"><span class="reel-rule w-12"></span><span class="reel-rule w-12"></span></div>
{% if title %}<h2 class="reel-caps text-2xl md:text-3xl" style="font-family:var(--font-heading, &quot;Antonio&quot;, &quot;Oswald&quot;, Georgia, serif)">{{ title }}</h2>{% endif %}
{% if description %}<p class="mx-auto mt-4 max-w-xl {% if layout == "inline" %}text-primary-foreground/80{% else %}text-muted-foreground{% endif %}" style="font-family:var(--font-body, &quot;Cormorant Garamond&quot;, &quot;Cormorant&quot;, Georgia, serif)">{{ 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,21 +0,0 @@
{# page-suggestions override — themed 404 suggestions. Fields: title, showDescription, emptyMessage; suggestions[] from the provider (.slug/.title/.excerpt). #}
{% if suggestions %}
<div data-block-override="gotham:page-suggestions" class="page-suggestions py-10">
{% if title %}<h2 class="reel-caps reel-underline mx-auto w-fit text-xl text-center" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">{{ title }}</h2>{% endif %}
<ul class="mt-6 space-y-3 max-w-lg mx-auto">
{% for suggestion in suggestions %}
<li class="flex items-start gap-3">
<span class="mt-0.5 shrink-0" style="color:hsl(var(--primary));">::lucide:chevron-right:sm::</span>
<div>
<a href="{{ suggestion.slug }}" class="reel-link font-medium">{{ suggestion.title }}</a>
{% if showDescription and suggestion.excerpt %}<p class="mt-1 text-sm italic" style="color:hsl(var(--muted-foreground));">{{ suggestion.excerpt }}</p>{% endif %}
</div>
</li>
{% endfor %}
</ul>
</div>
{% elif emptyMessage %}
<div data-block-override="gotham:page-suggestions" class="page-suggestions py-10 text-center">
<p class="italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Cormorant Garamond', Georgia, serif);">{{ emptyMessage }}</p>
</div>
{% endif %}

View File

@ -1,13 +0,0 @@
{# password-reset-form override — request stage only, matching builtin. Wiring UNTOUCHABLE: /api/auth/request-password-reset (email), hx-target #reset-message. Reskinned to Art Deco. #}
<div data-block-override="gotham:password-reset-form" class="password-reset-form reel-frame mx-auto max-w-md bg-card p-8">
<div id="reset-message"></div>
<div id="request-reset-panel" class="text-center">
<h2 class="reel-caps text-2xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif);color:hsl(var(--foreground));">Reset password</h2>
<p class="mt-3 text-sm italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Cormorant Garamond', Georgia, serif);">Enter your email and we will send a link to reset your password.</p>
<form hx-post="/api/auth/request-password-reset" hx-target="#reset-message" hx-swap="innerHTML" class="mt-6 text-left">
<div class="mb-4"><label class="reel-caps block text-xs mb-1" style="color:hsl(var(--primary));">Email</label><input type="email" name="email" required class="w-full rounded-none border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:border-[hsl(var(--primary))] focus:ring-1 focus:ring-ring" /></div>
<button type="submit" class="reel-btn w-full" hx-disabled-elt="this">Send reset link</button>
</form>
<p class="text-center mt-4 text-sm"><a href="/login" class="reel-link">Back to sign in</a></p>
</div>
</div>

View File

@ -1,16 +0,0 @@
{# popular-posts (gotham) — compact ranked list from the `posts` provider. #}
<section data-block-override="gotham:popular-posts" class="bg-background text-foreground{% if class %} {{ class }}{% endif %}">
{% if heading %}<h2 class="reel-caps mb-5 text-xs text-primary">{{ heading }}</h2>{% endif %}
<ol class="space-y-4">
{% for post in posts %}
<li class="reel-rule flex items-start gap-3 pt-4">
{% if showRank %}<span class="mt-0.5 text-lg font-bold tabular-nums text-primary" style="font-family:var(--font-heading, &quot;Antonio&quot;, &quot;Oswald&quot;, Georgia, serif)">{{ forloop.Counter }}</span>{% endif %}
{% if showThumbnail and post.featured_image_url %}<a href="{{ post.url }}" class="reel-mat h-12 w-12 flex-shrink-0 overflow-hidden 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="reel-caps text-sm leading-snug text-foreground" style="font-family:var(--font-heading, &quot;Antonio&quot;, &quot;Oswald&quot;, Georgia, serif)"><a href="{{ post.url }}" class="transition-colors hover:text-primary">{{ post.title }}</a></h3>
{% if post.published_at %}<p class="mt-1 text-xs text-muted-foreground">{{ post.published_at|date:"M j, Y" }}</p>{% endif %}
</div>
</li>
{% endfor %}
</ol>
</section>

View File

@ -1,23 +0,0 @@
{# post-hero (gotham) — 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 data-block-override="gotham:post-hero" 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/70"></div><div class="film-grain absolute inset-0"></div></div>
<div class="relative z-10 mx-auto w-full max-w-4xl px-4 py-14 sm:px-6 lg:px-8 {% if center %}text-center{% endif %}">
{% if center %}<div class="mb-4 flex items-center justify-center gap-4"><span class="reel-rule w-12"></span><span class="reel-rule w-12"></span></div>{% endif %}
<h1 class="reel-caps text-3xl leading-tight text-white sm:text-4xl lg:text-5xl" style="font-family:var(--font-heading, &quot;Antonio&quot;, &quot;Oswald&quot;, Georgia, serif)">{{ post.title }}</h1>
{% if showExcerpt and post.excerpt %}<p class="mt-5 max-w-2xl text-lg text-white/80{% if center %} mx-auto{% endif %}" style="font-family:var(--font-body, &quot;Cormorant Garamond&quot;, &quot;Cormorant&quot;, Georgia, serif)">{{ post.excerpt }}</p>{% endif %}
{% if showMeta %}<div class="mt-6 flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-white/80{% if center %} justify-center{% endif %}">{% if post.author_name %}<span class="reel-caps 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 data-block-override="gotham:post-hero" class="bg-background py-16 text-foreground md:py-20{% if class %} {{ class }}{% endif %}">
<div class="mx-auto w-full max-w-3xl px-4 {% if center %}text-center{% endif %}">
{% if center %}<div class="mb-4 flex items-center justify-center gap-4"><span class="reel-rule w-12"></span><span class="reel-rule w-12"></span></div>{% endif %}
<h1 class="reel-caps reel-underline text-3xl leading-tight sm:text-4xl lg:text-5xl text-foreground" style="font-family:var(--font-heading, &quot;Antonio&quot;, &quot;Oswald&quot;, Georgia, serif)">{{ post.title }}</h1>
{% if showExcerpt and post.excerpt %}<p class="mt-5 text-lg text-muted-foreground{% if center %} mx-auto max-w-2xl{% endif %}" style="font-family:var(--font-body, &quot;Cormorant Garamond&quot;, &quot;Cormorant&quot;, Georgia, serif)">{{ post.excerpt }}</p>{% endif %}
{% if showMeta %}<div class="mt-6 flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-muted-foreground{% if center %} justify-center{% endif %}">{% if post.author_name %}<span class="reel-caps text-primary">{{ 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 (gotham) — author/date/reading-time row from the `post` provider. #}
{% if post %}
<div data-block-override="gotham:post-metadata" class="flex flex-wrap items-center gap-x-4 gap-y-2 text-sm text-muted-foreground{% 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 object-cover ring-1 ring-[hsl(var(--primary)/0.5)]" %}{% endif %}<span class="reel-caps text-primary">{{ 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="reel-caps rounded-none border border-[hsl(var(--primary)/0.35)] px-2 py-0.5 text-xs text-muted-foreground transition-colors hover:text-primary">{{ cat.name }}</a>{% endfor %}</span>{% endif %}
</div>
{% endif %}

View File

@ -1,25 +0,0 @@
<section data-block-override="gotham:pricing" 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="reel-caps text-center text-3xl text-foreground md:text-4xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ 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="relative flex flex-col p-6 {% if tier.highlighted %}reel-frame bg-card text-card-foreground reel-foil{% else %}rounded-none border border-[hsl(var(--primary)/0.35)] bg-card text-card-foreground{% endif %}">
{% if tier.badge %}<span class="reel-caps absolute -top-3 left-1/2 -translate-x-1/2 border border-primary bg-primary px-3 py-1 text-xs text-primary-foreground">{{ tier.badge }}</span>{% endif %}
<h3 class="reel-caps text-center text-lg text-foreground" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ tier.name }}</h3>
<div class="mx-auto my-3 flex items-center justify-center"><span class="reel-rule w-12"></span></div>
<p class="flex items-baseline justify-center gap-1">
<span class="text-4xl font-bold tracking-tight text-primary" style="font-family:var(--font-heading)">{% if currency %}{{ currency }}{% else %}${% endif %}{{ tier.price }}</span>
{% if tier.period %}<span class="text-sm text-muted-foreground">{{ tier.period }}</span>{% endif %}
</p>
{% if tier.description %}<p class="mt-3 text-center text-sm text-muted-foreground" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ 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 %}{% if tier.highlighted %}<a href="{{ tier.ctaUrl }}" class="reel-btn mt-8 inline-flex w-full items-center justify-center">{{ tier.ctaLabel }}</a>{% else %}<a href="{{ tier.ctaUrl }}" class="reel-link mt-8 inline-flex w-full items-center justify-center">{{ tier.ctaLabel }}</a>{% endif %}{% endif %}
</div>
{% endfor %}
</div>
{% if billingNote %}<p class="mt-8 text-center text-sm text-muted-foreground">{{ billingNote }}</p>{% endif %}
</div>
</section>

View File

@ -1,8 +0,0 @@
{# quote override — supper-club press pull-quote: large italic Cormorant flanked by gold sunburst crests. Fields mirror builtin: quote, style, align, cite, attribution, role. #}
{% with style=style|default:"block" align=align|default:"left" %}
<figure data-block-override="gotham:quote" class="bn-quote my-10 {% if style == "inline-card" %}reel-frame bg-card p-8 md:p-10{% elif style == "pull" %}border-l-2 border-[hsl(var(--primary))] pl-6 md:pl-8 py-2{% else %}px-2 py-1{% endif %}{% if align == "center" %} text-center{% endif %}{% if class %} {{ class }}{% endif %}">
{% if align == "center" %}<div class="mb-5 h-6 reel-strip" aria-hidden="true"></div>{% endif %}
<blockquote{% if cite %} cite="{{ cite }}"{% endif %} class="{% if style == "pull" %}text-2xl md:text-3xl leading-snug{% else %}text-xl md:text-2xl leading-relaxed{% endif %} italic" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif);color:hsl(var(--foreground));">{{ quote|safe }}</blockquote>
{% if attribution or role or cite %}<figcaption class="mt-4 text-sm{% if align == "center" %} flex flex-col items-center{% endif %}" style="color:hsl(var(--muted-foreground));">{% if attribution %}<span class="reel-caps text-xs" style="color:hsl(var(--primary));">{{ attribution }}</span>{% endif %}{% if role %}<span class="{% if attribution %}block {% endif %}italic">{{ role }}</span>{% endif %}{% if cite %}<a href="{{ cite }}" class="reel-link {% if attribution or role %}mt-1 {% endif %}inline-block text-xs">Source</a>{% endif %}</figcaption>{% endif %}
</figure>
{% endwith %}

View File

@ -1,17 +0,0 @@
{# related-posts (gotham) — grid from the `posts` provider. #}
{% if posts %}
<section data-block-override="gotham:related-posts" class="reel-rule-thick py-12 md:py-16 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
<div class="mx-auto max-w-6xl px-4">
<h2 class="reel-caps reel-underline mb-10 text-center text-2xl md:text-3xl text-foreground" style="font-family:var(--font-heading, &quot;Antonio&quot;, &quot;Oswald&quot;, Georgia, serif)">{{ heading|default:"Related posts" }}</h2>
<div class="grid grid-cols-1 gap-8 sm:grid-cols-2 {% if columns == 2 %}lg:grid-cols-2{% else %}lg:grid-cols-3{% endif %}">
{% for post in posts %}
<article class="group flex flex-col text-center">
{% if post.featured_image_url %}<a href="{{ post.url }}" class="reel-mat mb-4 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-500 group-hover:scale-105" %}</a>{% endif %}
<h3 class="reel-caps text-base leading-snug text-foreground" style="font-family:var(--font-heading, &quot;Antonio&quot;, &quot;Oswald&quot;, Georgia, serif)"><a href="{{ post.url }}" class="transition-colors hover:text-primary">{{ post.title }}</a></h3>
{% if post.published_at %}<p class="mt-2 text-xs text-muted-foreground">{{ post.published_at|date:"M j, Y" }}</p>{% endif %}
</article>
{% endfor %}
</div>
</div>
</section>
{% endif %}

View File

@ -1,13 +0,0 @@
<section data-block-override="gotham:rich-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 %}<div class="mb-3 flex items-center gap-4"><span class="reel-rule w-12"></span><p class="reel-caps text-xs text-primary">{{ eyebrow }}</p></div>{% endif %}
{% if heading %}<h2 class="reel-caps reel-underline mt-2 text-3xl text-foreground md:text-4xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ 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" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ body|safe }}</div>{% endif %}
{% if ctaUrl and ctaLabel %}<a href="{{ ctaUrl }}" class="reel-btn mt-6 inline-flex items-center gap-2">{{ 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="reel-mat overflow-hidden">{% img media alt=mediaAlt class="w-full h-auto object-cover" %}</div>{% endif %}
</div>
</div>
</section>

View File

@ -1,2 +0,0 @@
{# rich-text override — long-form Cormorant editorial column with gilded links. Field: html (mirrors builtin). #}
{% if html %}<article data-block-override="gotham:rich-text" class="rich-text prose prose-lg max-w-none [&_a]:text-primary [&_a]:no-underline [&_a]:border-b [&_a:hover]:text-accent [&_h1]:reel-caps [&_h2]:reel-caps [&_h3]:reel-caps [&_blockquote]:border-l-2 [&_blockquote]:border-[hsl(var(--primary))] [&_blockquote]:italic" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif);color:hsl(var(--foreground));font-size:1.1rem;line-height:1.75;">{{ html|safe }}</article>{% endif %}

View File

@ -1,15 +0,0 @@
{# sidebar-nav (gotham) — 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 data-block-override="gotham:sidebar-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-[hsl(var(--primary)/0.35)]{% endif %}{% if class %} {{ class }}{% endif %}">
{% if title %}<h3 class="reel-caps reel-underline {% if compact %}px-2 mb-4 text-xs text-primary{% else %}px-3 mb-5 text-sm text-foreground{% endif %}" style="font-family:var(--font-heading, &quot;Antonio&quot;, &quot;Oswald&quot;, Georgia, serif)">{{ 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="reel-caps flex items-center rounded-none text-sm 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 text-primary" 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,13 +0,0 @@
<div data-block-override="gotham:social-links" class="social-links{% if class %} {{ class }}{% endif %}">
{% if heading %}<p class="reel-caps mb-4 text-xs tracking-[0.18em] text-primary">{{ 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="reel-foil inline-flex items-center gap-2 rounded-none border border-[hsl(var(--primary)/0.45)] text-foreground transition-colors hover:bg-primary/10 hover:text-primary {% if style == "stack" %}w-full px-4 py-2.5{% else %}p-2.5{% endif %}">
{% if link.network %}<svg class="h-5 w-5 text-primary" 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="reel-caps text-xs tracking-[0.14em]">{{ link.label }}</span>{% endif %}
</a>
</li>
{% endif %}{% endfor %}
</ul>
</div>

View File

@ -1,48 +0,0 @@
<section data-block-override="gotham:stats" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}"{% if countUp %} data-stats{% endif %}>
<div class="mx-auto max-w-6xl px-4">
{% if heading %}<h2 class="reel-caps text-center text-3xl text-foreground md:text-4xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ intro }}</p>{% endif %}
<dl class="mt-12 grid grid-cols-2 gap-8 divide-x divide-[hsl(var(--primary)/0.25)] {% 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="px-2 text-center">
<dd class="text-4xl font-bold tracking-tight text-primary 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="reel-caps mt-2 text-xs 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,21 +0,0 @@
<section data-block-override="gotham:team" 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="reel-caps text-center text-3xl text-foreground md:text-4xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ 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" %}reel-frame flex items-start gap-5 bg-card p-5 text-card-foreground{% 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 border border-[hsl(var(--primary)/0.4)] 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="reel-caps text-lg text-foreground" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ member.name }}</h3>{% endif %}
{% if member.role %}<p class="reel-caps text-xs text-primary">{{ member.role }}</p>{% endif %}
{% if member.bio %}<p class="mt-2 text-sm text-muted-foreground" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ 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 @@
<section data-block-override="gotham:testimonial" 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="reel-caps text-center text-3xl text-foreground md:text-4xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ 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="reel-frame flex flex-col bg-card p-6 text-card-foreground{% if layout == "carousel" %} min-w-[18rem] max-w-sm shrink-0 snap-start{% endif %}">
{% if q.rating %}<div class="mb-3 flex justify-center 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-center text-lg leading-relaxed" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{% if q.quote %}&ldquo;{{ q.quote }}&rdquo;{% endif %}</blockquote>
<div class="mx-auto my-4 flex items-center justify-center"><span class="reel-rule w-16"></span></div>
<figcaption class="flex items-center justify-center gap-3">
{% if q.avatar %}<span class="h-10 w-10 shrink-0 overflow-hidden border border-[hsl(var(--primary)/0.4)] bg-muted">{% img q.avatar alt=q.name class="h-full w-full object-cover" %}</span>{% endif %}
<span class="text-center">
{% if q.name %}<span class="reel-caps block text-sm text-foreground">{{ q.name }}</span>{% endif %}
{% if q.role %}<span class="block text-sm text-muted-foreground">{{ q.role }}</span>{% endif %}
</span>
</figcaption>
</figure>
{% endfor %}
</div>
</div>
</section>

View File

@ -1,2 +1 @@
{# text override — Cormorant body prose with gilded links. Fields: text, proseClass (mirrors builtin). Dropcap is reserved for the article template, not forced on every text block. #}
<div data-block-override="gotham:text" class="prose max-w-none flex-1 [&_a]:text-primary [&_a]:no-underline [&_a]:border-b [&_a]:border-[hsl(var(--primary))] [&_a:hover]:text-accent {{ proseClass }}" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif);color:hsl(var(--foreground));font-size:1.075rem;line-height:1.75;">{{ text|safe }}</div>
<div class="prose prose-invert prose-amber max-w-none flex-1 {{ class }}">{{ text|safe }}</div>

View File

@ -1,21 +0,0 @@
<section data-block-override="gotham:timeline" 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="reel-caps text-center text-3xl text-foreground md:text-4xl" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ 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-[hsl(var(--primary)/0.4)]{% endif %}">
{% for step in steps %}
<li class="{% if orientation == "horizontal" %}text-center{% else %}relative pl-14{% endif %}">
<span class="reel-caps flex h-8 w-8 items-center justify-center border border-primary bg-primary text-sm text-primary-foreground {% 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 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="reel-caps text-xs text-primary">{{ step.label }}</p>{% endif %}
{% if step.title %}<h3 class="reel-caps mt-1 text-lg text-foreground" style="font-family:var(--font-heading, 'Antonio', 'Oswald', 'Bebas Neue', sans-serif)">{{ step.title }}</h3>{% endif %}
{% if step.text %}<p class="mt-1 text-muted-foreground" style="font-family:var(--font-body, 'Cormorant Garamond', 'Cormorant', Georgia, serif)">{{ step.text }}</p>{% endif %}
{% if step.media %}<div class="reel-mat mt-3 overflow-hidden">{% 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 override — thin lacquer contact strip, gold text. Fields mirror builtin: variant, showOnMobile, leftItems/rightItems {icon,label,link}. #}
{% set ubcls = "bg-foreground text-background" %}
{% 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="reel-caps inline-flex items-center gap-1.5 hover:text-primary transition-colors">{% if it.icon %}::{{ it.icon }}::{% endif %}{% if it.label %}<span>{{ it.label }}</span>{% endif %}</a>{% else %}<span class="reel-caps 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 data-block-override="gotham:utility-bar" class="w-full text-[0.7rem] {% 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-5 flex-wrap">{% for it in leftItems %}{{ ubitem(it) }}{% endfor %}</div>
<div class="flex items-center gap-5 flex-wrap justify-end">{% for it in rightItems %}{{ ubitem(it) }}{% endfor %}</div>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More