diff --git a/PORT-NOTES.md b/PORT-NOTES.md deleted file mode 100644 index cdb294a..0000000 --- a/PORT-NOTES.md +++ /dev/null @@ -1,74 +0,0 @@ -# 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 }}`. `` 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/` 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. diff --git a/README.md b/README.md index 8691645..3e6ae31 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,56 @@ # Gotham -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. +The after-dark identity of a nightlife venue or private members' club. Cinematic +near-black luxe, brass hairline accents, letterboxed heroes and tall condensed +display type. A codeless BlockNinja `.bnp` theme (no Go, no `plugin.wasm`) — every +block, page template, override and the email wrapper is a host-rendered +`.ninjatpl`. -## Page templates +## Identity -- `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 +- **Palettes** — five dual-mode presets. Light is silver-screen platinum; dark + is velvet black. Both are intentionally tuned (`presets.json`). +- **Type** — Antonio (condensed display headings), Cormorant Garamond (elegant + body), Inter (meta), IBM Plex Mono (mono). Bundled OFL woff2 in + `assets/fonts/`; always consumed via `var(--font-heading|body|mono, ...)`. +- **Motion** — MID tier. JS scroll reveals and a spotlight-follow on the hero. + No canvas. `prefers-reduced-motion` honoured; renders fully with no JS. +- **Utilities** — `reel-*`, `film-grain`, `spotlight`, `letterbox-scrim`, + `reveal` in `manifest.yaml` `css.input_css_append`. Colour is always + `hsl(var(--token))`. -## Blocks +## Page templates (`templates/gotham/`) -- `gotham:stats` — stat row with configurable items -- `gotham:features` — feature grid -- `gotham:footer` — branded footer with optional signup form +`default`, `landing`, `article`, `full-width`, `blog-index`, `contact`, `auth`, +plus `centered` (retained extra key). + +## Overrides (`templates/overrides/gotham/`) + +All 49 mandatory builtin keys are re-skinned as template overrides (chrome, +marketing, primitives, commerce/local, blog family, auth surface, the themed +404). Fields, provider reads, form wiring and block JS are preserved verbatim +from the frozen builtins; only presentation changes. + +## Theme blocks (`blocks/`) + +True persona furniture only: + +- `gotham:door_strip` — sticky door strip (reservations line, guest-list CTA, hours) +- `gotham:events_marquee` — line-up of nights and residencies +- `gotham:membership_tiers` — membership cards +- `gotham:bottle_menu` — cellar / bottle-service list + +## Seed (`seed/`) + +`seed.json` (`demo: true`) installs a private-club demo: home, about, a journal +with three posts, a door/contact page (form + `contact_submissions` data table), +and a members' login. `workflows.json` emails admins on a form submission. ## Build ``` -make # builds gotham.so locally -make rebuild # rebuilds plugin inside an instance container +make # pack gotham-.bnp (codeless, no wasm) +make verify # loader-identical checks +make archive-check +make publish # ninja plugin publish --bnp ``` - -## 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. diff --git a/RECOMMENDED_FONTS.md b/RECOMMENDED_FONTS.md new file mode 100644 index 0000000..86243d2 --- /dev/null +++ b/RECOMMENDED_FONTS.md @@ -0,0 +1,29 @@ +# Recommended fonts — Gotham + +Gotham is admin-font-controlled. Every family is consumed through +`var(--font-heading|body|mono, )`, 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. diff --git a/RECOMMENDED_ICONS.md b/RECOMMENDED_ICONS.md new file mode 100644 index 0000000..cb70d08 --- /dev/null +++ b/RECOMMENDED_ICONS.md @@ -0,0 +1,21 @@ +# Recommended icons — Gotham + +Gotham renders icons from the sprite system only, as +`` (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. diff --git a/assets/fonts/.gitkeep b/assets/fonts/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/assets/fonts/LICENSES.md b/assets/fonts/LICENSES.md new file mode 100644 index 0000000..7a6733a --- /dev/null +++ b/assets/fonts/LICENSES.md @@ -0,0 +1,16 @@ +# 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, )`. diff --git a/assets/fonts/antonio-latin-ext.woff2 b/assets/fonts/antonio-latin-ext.woff2 new file mode 100644 index 0000000..2825535 Binary files /dev/null and b/assets/fonts/antonio-latin-ext.woff2 differ diff --git a/assets/fonts/antonio-latin.woff2 b/assets/fonts/antonio-latin.woff2 new file mode 100644 index 0000000..4fbb342 Binary files /dev/null and b/assets/fonts/antonio-latin.woff2 differ diff --git a/assets/fonts/cormorant-garamond-400-italic.woff2 b/assets/fonts/cormorant-garamond-400-italic.woff2 new file mode 100644 index 0000000..613b1b0 Binary files /dev/null and b/assets/fonts/cormorant-garamond-400-italic.woff2 differ diff --git a/assets/fonts/cormorant-garamond-400.woff2 b/assets/fonts/cormorant-garamond-400.woff2 new file mode 100644 index 0000000..38cbfcc Binary files /dev/null and b/assets/fonts/cormorant-garamond-400.woff2 differ diff --git a/assets/fonts/cormorant-garamond-600.woff2 b/assets/fonts/cormorant-garamond-600.woff2 new file mode 100644 index 0000000..b085294 Binary files /dev/null and b/assets/fonts/cormorant-garamond-600.woff2 differ diff --git a/assets/fonts/cormorant-garamond-700.woff2 b/assets/fonts/cormorant-garamond-700.woff2 new file mode 100644 index 0000000..d17b70b Binary files /dev/null and b/assets/fonts/cormorant-garamond-700.woff2 differ diff --git a/assets/fonts/ibm-plex-mono-400.woff2 b/assets/fonts/ibm-plex-mono-400.woff2 new file mode 100644 index 0000000..0804aaf Binary files /dev/null and b/assets/fonts/ibm-plex-mono-400.woff2 differ diff --git a/assets/fonts/ibm-plex-mono-500.woff2 b/assets/fonts/ibm-plex-mono-500.woff2 new file mode 100644 index 0000000..090f82f Binary files /dev/null and b/assets/fonts/ibm-plex-mono-500.woff2 differ diff --git a/assets/fonts/inter-400.woff2 b/assets/fonts/inter-400.woff2 new file mode 100644 index 0000000..f15b025 Binary files /dev/null and b/assets/fonts/inter-400.woff2 differ diff --git a/assets/fonts/inter-500.woff2 b/assets/fonts/inter-500.woff2 new file mode 100644 index 0000000..54f0a59 Binary files /dev/null and b/assets/fonts/inter-500.woff2 differ diff --git a/assets/fonts/inter-700.woff2 b/assets/fonts/inter-700.woff2 new file mode 100644 index 0000000..a68fb10 Binary files /dev/null and b/assets/fonts/inter-700.woff2 differ diff --git a/assets/fonts/placeholder.txt b/assets/fonts/placeholder.txt deleted file mode 100644 index 9dad2fe..0000000 --- a/assets/fonts/placeholder.txt +++ /dev/null @@ -1 +0,0 @@ -/* placeholder for fonts */ diff --git a/assets/style.css b/assets/style.css deleted file mode 100644 index 2791c99..0000000 --- a/assets/style.css +++ /dev/null @@ -1,33 +0,0 @@ -/* 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); -} diff --git a/blocks/blocks.yaml b/blocks/blocks.yaml index 9501a62..61a590c 100644 --- a/blocks/blocks.yaml +++ b/blocks/blocks.yaml @@ -1,26 +1,32 @@ -# Gotham theme block definitions (codeless .bnp, WO-WZ-021). -# Keys are fully qualified (gotham:) 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. +# 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:) — the +# codeless loader registers definitions verbatim; the qualified keys match the +# data-block attributes, master_pages.json and seed references. blocks: - - 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 + - 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 diff --git a/blocks/bottle_menu.ninjatpl b/blocks/bottle_menu.ninjatpl new file mode 100644 index 0000000..2a860f3 --- /dev/null +++ b/blocks/bottle_menu.ninjatpl @@ -0,0 +1,29 @@ +{# bottle_menu — cellar list. sections[] and section.items[] are arrays of maps. #} +
+
+
+ {% if eyebrow %}
{{ eyebrow }}
{% endif %} + {% if title %}

{{ title }}

{% endif %} +
+ {% if sections %} +
+ {% for section in sections %} +
+ {% if section.title %}

{{ section.title }}

{% endif %} +
    + {% for pour in section.items %} +
  • + {{ pour.name }} + + {% if pour.price %}{{ pour.price }}{% endif %} + {% if pour.note %}{% endif %} +
  • + {% endfor %} +
+
+ {% endfor %} +
+ {% endif %} + {% if footnote %}

{{ footnote }}

{% endif %} +
+
diff --git a/blocks/bottle_menu.schema.json b/blocks/bottle_menu.schema.json new file mode 100644 index 0000000..b59755e --- /dev/null +++ b/blocks/bottle_menu.schema.json @@ -0,0 +1,36 @@ +{ + "$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" } + } + } + } + } + } + } + } +} diff --git a/blocks/door_strip.ninjatpl b/blocks/door_strip.ninjatpl new file mode 100644 index 0000000..9be28e5 --- /dev/null +++ b/blocks/door_strip.ninjatpl @@ -0,0 +1,11 @@ +{# door_strip — sticky brass-on-black door strip. Iterates hours[] (array of strings). #} +
+
+
+ {{ label|default:"The Door" }} + {% if phone %}{{ phone }}{% endif %} +
+ {% if hours %}{% endif %} + {% if reserveUrl %}{{ reserveLabel|default:"Guest list" }}{% endif %} +
+
diff --git a/blocks/door_strip.schema.json b/blocks/door_strip.schema.json new file mode 100644 index 0000000..b040ffa --- /dev/null +++ b/blocks/door_strip.schema.json @@ -0,0 +1,48 @@ +{ + "$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" } + } + } + } + } +} diff --git a/blocks/events_marquee.ninjatpl b/blocks/events_marquee.ninjatpl new file mode 100644 index 0000000..151cfd0 --- /dev/null +++ b/blocks/events_marquee.ninjatpl @@ -0,0 +1,24 @@ +{# events_marquee — letterboxed line-up. Iterates events[] (array of maps). #} +
+
+
+ {% if eyebrow %}
{{ eyebrow }}
{% endif %} + {% if title %}

{{ title }}

{% endif %} +
+ {% if events %} +
    + {% for event in events %} +
  • + {{ event.date }} +
    + {{ event.name }} + {% if event.room %}{{ event.room }}{% endif %} + {% if event.note %}

    {{ event.note }}

    {% endif %} +
    + {% if event.url %}Book::lucide:arrow-right:sm::{% endif %} +
  • + {% endfor %} +
+ {% endif %} +
+
diff --git a/blocks/events_marquee.schema.json b/blocks/events_marquee.schema.json new file mode 100644 index 0000000..fb85499 --- /dev/null +++ b/blocks/events_marquee.schema.json @@ -0,0 +1,36 @@ +{ + "$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" } + } + } + } + } +} diff --git a/blocks/features.ninjatpl b/blocks/features.ninjatpl deleted file mode 100644 index 3ed3b8c..0000000 --- a/blocks/features.ninjatpl +++ /dev/null @@ -1 +0,0 @@ -{% if features %}
{% if section_title %}

{{ section_title }}

{% endif %}
{% for feature in features %}
{% if feature.icon %}
{% if feature.icon == "chart" %}{% elif feature.icon == "users" %}{% elif feature.icon == "clock" %}{% elif feature.icon == "star" %}{% else %}{% endif %}
{% endif %}

{{ feature.title }}

{% if feature.description %}

{{ feature.description }}

{% endif %}
{% endfor %}
{% endif %} \ No newline at end of file diff --git a/blocks/features.schema.json b/blocks/features.schema.json deleted file mode 100644 index 6c9de70..0000000 --- a/blocks/features.schema.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "$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"] - } - } - } -} diff --git a/blocks/footer.ninjatpl b/blocks/footer.ninjatpl deleted file mode 100644 index 5e37a72..0000000 --- a/blocks/footer.ninjatpl +++ /dev/null @@ -1 +0,0 @@ -
{% if columns %}
{% for col in columns %}
{% if col.heading %}

{{ col.heading }}

{% endif %}{% if col.links %}{% endif %}
{% endfor %}
{% endif %}{% if copyright %}

{{ copyright }}

{% endif %}
\ No newline at end of file diff --git a/blocks/footer.schema.json b/blocks/footer.schema.json deleted file mode 100644 index a743593..0000000 --- a/blocks/footer.schema.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "$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"] - } - } - } -} diff --git a/blocks/hero.ninjatpl b/blocks/hero.ninjatpl deleted file mode 100644 index 7ff61c5..0000000 --- a/blocks/hero.ninjatpl +++ /dev/null @@ -1 +0,0 @@ -
{% if background_url %}
{% endif %}

{{ headline }}

{% if subheadline %}

{{ subheadline }}

{% endif %}{% if cta_text and cta_url %}{{ cta_text }}{% endif %}
diff --git a/blocks/hero.schema.json b/blocks/hero.schema.json deleted file mode 100644 index 8a15d18..0000000 --- a/blocks/hero.schema.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "$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"] -} diff --git a/blocks/membership_tiers.ninjatpl b/blocks/membership_tiers.ninjatpl new file mode 100644 index 0000000..2fb7190 --- /dev/null +++ b/blocks/membership_tiers.ninjatpl @@ -0,0 +1,35 @@ +{# membership_tiers — brass-framed membership cards. tiers[] and tier.features[] are arrays of maps. #} +
+
+
+ {% if eyebrow %}
{{ eyebrow }}
{% endif %} + {% if title %}

{{ title }}

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

{{ intro }}

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

{{ tier.name }}

+ {% if tier.featured %}House pick{% endif %} +
+
+ {{ tier.price }} + {% if tier.cadence %}{{ tier.cadence }}{% endif %} +
+ {% if tier.description %}

{{ tier.description }}

{% endif %} + {% if tier.features %} +
    + {% for feature in tier.features %} +
  • ::lucide:check:sm::{{ feature.label }}
  • + {% endfor %} +
+ {% endif %} + {% if tier.applyUrl %}{{ tier.applyLabel|default:"Apply" }}{% endif %} +
+ {% endfor %} +
+ {% endif %} +
+
diff --git a/blocks/membership_tiers.schema.json b/blocks/membership_tiers.schema.json new file mode 100644 index 0000000..226cdb9 --- /dev/null +++ b/blocks/membership_tiers.schema.json @@ -0,0 +1,40 @@ +{ + "$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" } + } + } + } + } + } + } + } +} diff --git a/blocks/stats.ninjatpl b/blocks/stats.ninjatpl deleted file mode 100644 index 114bedd..0000000 --- a/blocks/stats.ninjatpl +++ /dev/null @@ -1 +0,0 @@ -{% if items %}
{% for stat in items %}
{% if stat.icon %}
{% if stat.icon == "chart" %}{% elif stat.icon == "users" %}{% elif stat.icon == "clock" %}{% elif stat.icon == "star" %}{% else %}{% endif %}
{% endif %}
{{ stat.value }}
{{ stat.label }}
{% endfor %}
{% else %}

Click + to add stat items

{% endif %} \ No newline at end of file diff --git a/blocks/stats.schema.json b/blocks/stats.schema.json deleted file mode 100644 index 2cd5487..0000000 --- a/blocks/stats.schema.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "$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": [] -} diff --git a/fonts.json b/fonts.json index b34bed6..810ff5e 100644 --- a/fonts.json +++ b/fonts.json @@ -1,88 +1,38 @@ [ { - "name": "Gotham", - "family": "Gotham", + "name": "Antonio", + "family": "Antonio", "variants": [ - { - "weight": "100", - "style": "normal", - "file": "fonts/web/gothamthin-webfont.woff2" - }, - { - "weight": "100", - "style": "italic", - "file": "fonts/web/gothamthinitalic-webfont.woff2" - }, - { - "weight": "200", - "style": "normal", - "file": "fonts/web/gothamxlight-webfont.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" - } + { "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" } + ] + }, + { + "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" } + ] + }, + { + "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" } + ] + }, + { + "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" } ] } ] diff --git a/manifest.yaml b/manifest.yaml index b459c7f..466d788 100644 --- a/manifest.yaml +++ b/manifest.yaml @@ -1,9 +1,10 @@ -# Gotham theme — codeless .bnp manifest (WO-WZ-021 Phase B). +# Gotham theme — codeless .bnp manifest (WO-TF-018, Wave A). # Synthesized into manifest.pb by `ninja plugin build` (no plugin.wasm). # -# 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 automatically (head_html) when assets/style.css exists. +# 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, ...). theme_presets: presets.json bundled_fonts: fonts.json master_pages: master_pages.json @@ -11,33 +12,214 @@ master_pages: master_pages.json system_templates: - key: gotham title: Gotham - description: "Dark, modern theme with bold typography" + description: "After-dark identity for a nightlife venue or private members' club — cinematic near-black luxe, brass accents and condensed display type." page_templates: - system: gotham key: default title: Default - description: "Standard dark page layout with header, main content, and footer" + description: "Centred near-black page with brass rails, header, main and footer." slots: [header, main, footer] - system: gotham key: landing - title: Landing Page - description: "Full-width hero sections, ideal for marketing pages" + title: Marquee Landing + description: "Letterboxed hero, door strip and a closing CTA for the main room." 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: Full Width - description: "Edge-to-edge content without max-width constraints" + title: Grand Room + description: "Edge-to-edge gallery layout for the room, lookbooks and galleries." 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 Content - description: "Narrow, centered content for articles and documentation" + title: Centered + description: "Narrow centred column for documents and single statements." 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, ). 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(18px); transition: opacity .7s ease, transform .7s 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; } + } + } diff --git a/master_pages.json b/master_pages.json index ec98fb8..c56c98e 100644 --- a/master_pages.json +++ b/master_pages.json @@ -2,11 +2,22 @@ { "key": "gotham:default-master", "title": "Gotham Default Master", - "page_templates": ["default", "centered"], + "page_templates": ["default", "article", "blog-index", "contact", "centered", "full-width"], "blocks": [ - { "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 } + { "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 } ] } ] diff --git a/plugin.mod b/plugin.mod index cc4623f..d606176 100644 --- a/plugin.mod +++ b/plugin.mod @@ -3,7 +3,8 @@ name = "gotham" display_name = "Gotham" scope = "@themes" version = "1.0.0" -description = "Dark, high-contrast BlockNinja theme with bold typography and modern accent panels." +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." kind = "theme" categories = ["templates"] -tags = ["dark", "high-contrast", "agency", "blog", "professional", "modern", "serif"] +tags = ["dark", "nightlife", "luxury", "club", "cinematic", "brass", "condensed", "hospitality", "events"] +required_icon_packs = ["lucide"] diff --git a/presets.json b/presets.json index 34bd460..9b33a0f 100644 --- a/presets.json +++ b/presets.json @@ -1,502 +1,252 @@ [ { - "id": "dark-knight", - "name": "The Dark Knight", - "description": "Classic Gotham - dark with amber accents", + "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.", "theme": { + "mode": "both", "lightColors": { - "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%" + "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%" }, "darkColors": { - "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" + "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%" + } } }, { - "id": "joker", - "name": "The Joker", - "description": "Chaotic purple and toxic green", + "id": "midnight-brass", + "name": "Midnight Brass", + "description": "Cool blue-black steel with bright brass. A colder, later room.", "theme": { + "mode": "both", "lightColors": { - "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%" + "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%" }, "darkColors": { - "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" + "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%" + } } }, { - "id": "riddler", - "name": "The Riddler", - "description": "Bright neon green with purple mystery", + "id": "noir-rose", + "name": "Noir Rose", + "description": "Near-black warmed with rose-gold. The velvet-rope room.", "theme": { + "mode": "both", "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": "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%" + "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%" }, "darkColors": { - "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" + "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%" + } } }, { - "id": "harley-quinn", - "name": "Harley Quinn", - "description": "Bold red, black, and playful pink", + "id": "smoke-silver", + "name": "Smoke & Silver", + "description": "Graphite and cool silver. Restrained, low on gold. The quiet booth.", "theme": { + "mode": "both", "lightColors": { - "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%" + "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%" }, "darkColors": { - "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" + "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%" + } } }, { - "id": "mr-freeze", - "name": "Mr. Freeze", - "description": "Cold ice blue and frozen white", + "id": "bourbon", + "name": "Bourbon", + "description": "Espresso black and whisky amber. The last-pour palette.", "theme": { + "mode": "both", "lightColors": { - "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%" + "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%" }, "darkColors": { - "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" + "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%" + } } } ] diff --git a/seed/seed.json b/seed/seed.json new file mode 100644 index 0000000..4a58254 --- /dev/null +++ b/seed/seed.json @@ -0,0 +1,255 @@ +{ + "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": "

Gotham House keeps late hours behind an unmarked door. We run four nights a week and hold the rest of the week back.

The rooms are dark by design. The bar is long. The list is short. Members come first, and their guests come with them.

" } }, + { "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": "

The list, the card and the guests. What to send and when.

", "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": "

From the first pour to the last record. How the evening runs.

", "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": "

What we pour after midnight and why the list stays short.

", "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": "

Send your name and party by nine. We hold the list until then and reply the same day.

Members lead. Guests come with them, and there is a cap. Bring your card. The door checks it.

" } } + ] + }, + { + "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": "

Doors open at nine. The first set starts slow. The main room fills after eleven and holds until the lights come up.

The cellar bar stays quiet. That is the point. Records, a nightcap and the last pour.

" } } + ] + }, + { + "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": "

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.

Tell the host what you are drinking. The rest follows.

" } } + ] + }, + { + "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": "

Members book priority nights and private booths.

" } } + ] + } + ], + "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 } + ] +} diff --git a/seed/workflows.json b/seed/workflows.json new file mode 100644 index 0000000..7abcff2 --- /dev/null +++ b/seed/workflows.json @@ -0,0 +1,19 @@ +{ + "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" } + } + ] + } + ] +} diff --git a/templates/email/gotham.ninjatpl b/templates/email/gotham.ninjatpl index 883b9fc..bf995b7 100644 --- a/templates/email/gotham.ninjatpl +++ b/templates/email/gotham.ninjatpl @@ -1,99 +1,64 @@ - - - - - - - {{ site_name }} - - - - - {% if preview_text %}
{{ preview_text }}
 ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌
{% endif %} - - - - - -
- - - - - - - - - - - - - - - -
- + + + + + + + {{ site_name }} + + + + {% if preview_text %}
{{ preview_text }}
{% endif %} + + + + +
+ + + + + + + + + + + + + + +
+ diff --git a/templates/gotham/article.ninjatpl b/templates/gotham/article.ninjatpl new file mode 100644 index 0000000..a0ea1f6 --- /dev/null +++ b/templates/gotham/article.ninjatpl @@ -0,0 +1,23 @@ + + +{{ head_html|safe }} + + {{ admin_banner_html|safe }} +
+
{{ slots.header|safe }}
+
+
+
+ {% if slots.main %}
{{ slots.main|safe }}
{% else %}

No content blocks assigned to this page.

{% endif %} +
+ +
+
+
{{ slots.footer|safe }}
+
+ {{ body_end_html|safe }} + + + diff --git a/templates/gotham/auth.ninjatpl b/templates/gotham/auth.ninjatpl new file mode 100644 index 0000000..420c839 --- /dev/null +++ b/templates/gotham/auth.ninjatpl @@ -0,0 +1,21 @@ + + +{{ head_html|safe }} + + {{ admin_banner_html|safe }} +
+
+ + {% if site_settings.Logo %}{{ site_settings.LogoAlt }}{% else %}{{ site_settings.Title|default:"BlockNinja" }}{% endif %} + + +
{% if slots.main %}{{ slots.main|safe }}{% else %}

No form assigned to this page.

{% endif %}
+ {% if slots.footer %}
{{ slots.footer|safe }}
{% endif %} +
+
+ {{ body_end_html|safe }} + + + diff --git a/templates/gotham/blog-index.ninjatpl b/templates/gotham/blog-index.ninjatpl new file mode 100644 index 0000000..0b17027 --- /dev/null +++ b/templates/gotham/blog-index.ninjatpl @@ -0,0 +1,26 @@ + + +{{ head_html|safe }} + + {{ admin_banner_html|safe }} + {% if slots.header %}
{{ slots.header|safe }}
{% endif %} +
+
+ {% if title %} +
+

{{ title }}

+ {% if page_meta.MetaDescription %}

{{ page_meta.MetaDescription }}

{% endif %} + +
+ {% endif %} + {% if slots.featured %}
{{ slots.featured|safe }}
{% endif %} + {% if slots.main %}
{{ slots.main|safe }}
{% else %}

No posts published yet.

{% endif %} +
+
+ {% if slots.footer %}
{{ slots.footer|safe }}
{% endif %} + {{ body_end_html|safe }} + + + diff --git a/templates/gotham/centered.ninjatpl b/templates/gotham/centered.ninjatpl index 039f7af..ff43912 100644 --- a/templates/gotham/centered.ninjatpl +++ b/templates/gotham/centered.ninjatpl @@ -1,11 +1,16 @@ - + {{ head_html|safe }} - + {{ admin_banner_html|safe }} -
{{ slots.header|safe }}
-
{% if slots.main %}
{{ slots.main|safe }}
{% else %}

No content blocks assigned to this page.

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

No content blocks assigned to this page.

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

No content blocks assigned to this page.

{% endif %}
+ {% endif %} +
+
+ {% if slots.footer %}
{{ slots.footer|safe }}
{% endif %} + {{ body_end_html|safe }} + + + diff --git a/templates/gotham/default.ninjatpl b/templates/gotham/default.ninjatpl index a1a18bc..2117930 100644 --- a/templates/gotham/default.ninjatpl +++ b/templates/gotham/default.ninjatpl @@ -1,11 +1,20 @@ - + {{ head_html|safe }} - + {{ admin_banner_html|safe }} -
{{ slots.header|safe }}
-
{% if slots.main %}{{ slots.main|safe }}{% else %}

No content blocks assigned to this page.

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

No content blocks assigned to this page.

{% endif %} +
+
+
{{ slots.footer|safe }}
+
{{ body_end_html|safe }} + diff --git a/templates/gotham/full-width.ninjatpl b/templates/gotham/full-width.ninjatpl index 9fafdf1..4313c3a 100644 --- a/templates/gotham/full-width.ninjatpl +++ b/templates/gotham/full-width.ninjatpl @@ -1,11 +1,18 @@ - + {{ head_html|safe }} - + {{ admin_banner_html|safe }}
{{ slots.header|safe }}
-
{% if slots.main %}{{ slots.main|safe }}{% else %}

No content blocks assigned to this page.

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

No content blocks assigned to this page.

{% endif %} +
+
+
{{ slots.footer|safe }}
+
{{ body_end_html|safe }} + diff --git a/templates/gotham/landing.ninjatpl b/templates/gotham/landing.ninjatpl index 283fc81..9b8d9c6 100644 --- a/templates/gotham/landing.ninjatpl +++ b/templates/gotham/landing.ninjatpl @@ -1,12 +1,19 @@ - + {{ head_html|safe }} - + {{ admin_banner_html|safe }}
{{ slots.hero|safe }}
-
{% if slots.main %}
{{ slots.main|safe }}
{% endif %}
+
+ {% if slots.main %}{{ slots.main|safe }}{% endif %} +
{{ slots.cta|safe }}
-
{{ slots.footer|safe }}
+
+
{{ slots.footer|safe }}
+
{{ body_end_html|safe }} + diff --git a/templates/overrides/gotham/announcement-bar.ninjatpl b/templates/overrides/gotham/announcement-bar.ninjatpl new file mode 100644 index 0000000..40b2af6 --- /dev/null +++ b/templates/overrides/gotham/announcement-bar.ninjatpl @@ -0,0 +1,21 @@ +{# announcement-bar override — gilded banner. Dismiss {% endif %} diff --git a/templates/overrides/gotham/audio-embed.ninjatpl b/templates/overrides/gotham/audio-embed.ninjatpl new file mode 100644 index 0000000..fa071d1 --- /dev/null +++ b/templates/overrides/gotham/audio-embed.ninjatpl @@ -0,0 +1,16 @@ +{# audio-embed override — gold-framed player card. Fields + facade JS mirror builtin verbatim: url, media, artwork, title, artist, download. #} +
{% if artwork %}
{% img artwork alt=title|default:"" class="h-full w-full object-cover" %}
{% endif %}
{% if title %}

{{ title }}

{% endif %}{% if artist %}

{{ artist }}

{% endif %}
{% if download %}::lucide:download:sm:: Download{% endif %}
diff --git a/templates/overrides/gotham/auth-form.ninjatpl b/templates/overrides/gotham/auth-form.ninjatpl new file mode 100644 index 0000000..2ac6861 --- /dev/null +++ b/templates/overrides/gotham/auth-form.ninjatpl @@ -0,0 +1,33 @@ +{# 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 %} +
+

You are signed in as {{ context.publicUsername }}

+ My account +
+{% else %} +
+
+
+ + +
+
+
+
+
+ {% if _captcha_widget %}{{ _captcha_widget|safe }}{% endif %} + +

Forgot password?

+
+
+
+
+
+
+
+ {% if _captcha_widget %}{{ _captcha_widget|safe }}{% endif %} + +
+
+
+{% endif %} diff --git a/templates/overrides/gotham/auth-status.ninjatpl b/templates/overrides/gotham/auth-status.ninjatpl new file mode 100644 index 0000000..b468f73 --- /dev/null +++ b/templates/overrides/gotham/auth-status.ninjatpl @@ -0,0 +1,31 @@ +{# 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 %} +
+ + + +
+{% else %} + +{% endif %} diff --git a/templates/overrides/gotham/author-bio-hero.ninjatpl b/templates/overrides/gotham/author-bio-hero.ninjatpl new file mode 100644 index 0000000..064c3cf --- /dev/null +++ b/templates/overrides/gotham/author-bio-hero.ninjatpl @@ -0,0 +1,22 @@ +{# author-bio-hero (gotham) — author-page header from the `authors` provider. #} +{% if author %} +
+
+{% if showAvatar and author.avatar_url %}
{% img author.avatar_url alt=author.display_name class="h-full w-full object-cover" %}
{% endif %} +
+

{{ author.display_name }}

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

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

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

{{ author.bio }}

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

{{ title }}

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

{{ subtitle }}

{% endif %} +
+{% if posts %} +
+{% for post in posts %} + +{% endfor %} +
+{% endif %} +
+
diff --git a/templates/overrides/gotham/breadcrumbs.ninjatpl b/templates/overrides/gotham/breadcrumbs.ninjatpl new file mode 100644 index 0000000..fd5ea69 --- /dev/null +++ b/templates/overrides/gotham/breadcrumbs.ninjatpl @@ -0,0 +1,15 @@ +{# 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" %} + diff --git a/templates/overrides/gotham/button.ninjatpl b/templates/overrides/gotham/button.ninjatpl new file mode 100644 index 0000000..1d072c3 --- /dev/null +++ b/templates/overrides/gotham/button.ninjatpl @@ -0,0 +1,10 @@ +{# 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" %} + +{% endwith %} diff --git a/templates/overrides/gotham/card.ninjatpl b/templates/overrides/gotham/card.ninjatpl new file mode 100644 index 0000000..e24cb18 --- /dev/null +++ b/templates/overrides/gotham/card.ninjatpl @@ -0,0 +1,4 @@ +{# card override — angular gold Deco panel. Fields mirror builtin: layout, link, media, title, text, linkLabel. Media via {% img %}. #} +{% with layout=layout|default:"vertical" %} + +{% endwith %} diff --git a/templates/overrides/gotham/category-list.ninjatpl b/templates/overrides/gotham/category-list.ninjatpl new file mode 100644 index 0000000..db3ed27 --- /dev/null +++ b/templates/overrides/gotham/category-list.ninjatpl @@ -0,0 +1,18 @@ +{# category-list (gotham) — from the `categories` provider. list or pills. #} +{% set pills = style == "pills" %} +
+{% if heading %}

{{ heading }}

{% endif %} +{% if pills %} +
+{% for cat in categories %} +{{ cat.name }}{% if showCount %}{{ cat.post_count }}{% endif %} +{% endfor %} +
+{% else %} + +{% endif %} +
diff --git a/templates/overrides/gotham/contact-card.ninjatpl b/templates/overrides/gotham/contact-card.ninjatpl new file mode 100644 index 0000000..1222344 --- /dev/null +++ b/templates/overrides/gotham/contact-card.ninjatpl @@ -0,0 +1,17 @@ +
+
+ {% if heading %}

{{ heading }}

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

{{ description }}

{% endif %} +
+ {% for channel in channels %} +
+ {% if channel.icon %}{% endif %} +
+ {% if channel.label %}
{{ channel.label }}
{% endif %} + {% if channel.value %}
{% if channel.link %}{{ channel.value }}{% else %}{{ channel.value }}{% endif %}
{% endif %} +
+
+ {% endfor %} +
+
+
diff --git a/templates/overrides/gotham/contact-form.ninjatpl b/templates/overrides/gotham/contact-form.ninjatpl new file mode 100644 index 0000000..6142918 --- /dev/null +++ b/templates/overrides/gotham/contact-form.ninjatpl @@ -0,0 +1,43 @@ +{# 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 %} +
+
+ {% if form.submitted %} +
+
::lucide:check:lg::
+

{{ form.message|default:successMessage|default:"Thank you. Your table request has been received." }}

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

{{ heading }}

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

{{ description }}

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

{{ heading }}

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

{{ intro }}

{% endif %} +
+
+
--{{ labelDays|default:"Days" }}
+
--{{ labelHours|default:"Hours" }}
+
--{{ labelMinutes|default:"Minutes" }}
+
--{{ labelSeconds|default:"Seconds" }}
+
+ +
+ +
+
+ diff --git a/templates/overrides/gotham/cta.ninjatpl b/templates/overrides/gotham/cta.ninjatpl new file mode 100644 index 0000000..1643bbc --- /dev/null +++ b/templates/overrides/gotham/cta.ninjatpl @@ -0,0 +1,14 @@ +
+
+
+
+ {% if heading %}

{{ heading }}

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

{{ text }}

{% endif %} +
+
+ {% if primaryUrl and primaryLabel %}{{ primaryLabel }}{% endif %} + {% if secondaryUrl and secondaryLabel %}{{ secondaryLabel }}{% endif %} +
+
+
+
diff --git a/templates/overrides/gotham/divider.ninjatpl b/templates/overrides/gotham/divider.ninjatpl new file mode 100644 index 0000000..7a1f029 --- /dev/null +++ b/templates/overrides/gotham/divider.ninjatpl @@ -0,0 +1,13 @@ +{# 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" %} + +{% elif variant == "spacer-rule" %} + +{% else %} + +{% endif %} +{% endwith %} diff --git a/templates/overrides/gotham/event-list.ninjatpl b/templates/overrides/gotham/event-list.ninjatpl new file mode 100644 index 0000000..d87941c --- /dev/null +++ b/templates/overrides/gotham/event-list.ninjatpl @@ -0,0 +1,40 @@ +
+
+ {% if heading %}

{{ heading }}

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

{{ intro }}

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

{{ event.title }}

+ {% if event.venue %}

{{ event.venue }}

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

{{ event.description }}

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

    {{ event.title }}

    + {% if event.venue %}

    {{ event.venue }}

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

    {{ event.description }}

    {% endif %} +
    + {% if event.link %}{{ event.linkLabel|default:"Details" }}{% endif %} +
  • + {% endif %}{% endfor %} +
+ {% endif %} +
+
diff --git a/templates/overrides/gotham/faq.ninjatpl b/templates/overrides/gotham/faq.ninjatpl new file mode 100644 index 0000000..1ce6337 --- /dev/null +++ b/templates/overrides/gotham/faq.ninjatpl @@ -0,0 +1,17 @@ +
+
+ {% if heading %}

{{ heading }}

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

{{ intro }}

{% endif %} +
+ {% for item in items %} +
+ + {{ item.question }} + + +
{{ item.answer|safe }}
+
+ {% endfor %} +
+
+
diff --git a/templates/overrides/gotham/feature-grid.ninjatpl b/templates/overrides/gotham/feature-grid.ninjatpl new file mode 100644 index 0000000..44da4b1 --- /dev/null +++ b/templates/overrides/gotham/feature-grid.ninjatpl @@ -0,0 +1,17 @@ +
+
+ {% if eyebrow %}

{{ eyebrow }}

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

{{ heading }}

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

{{ intro }}

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

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

+ {% if item.text %}

{{ item.text }}

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

{{ heading }}

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

{{ post.title }}

+ +{% if showExcerpt and post.excerpt %}

{{ post.excerpt }}

{% endif %} +
+{% if showAuthor and post.author_name %}{{ post.author_name }}{% endif %} +{% if showDate and post.published_at %}{{ post.published_at|date:"M j, Y" }}{% endif %} +{% if post.reading_time %}{{ post.reading_time }} min read{% endif %} +
+
+
+{% endfor %} +
+
+
diff --git a/templates/overrides/gotham/footer.ninjatpl b/templates/overrides/gotham/footer.ninjatpl new file mode 100644 index 0000000..774a65d --- /dev/null +++ b/templates/overrides/gotham/footer.ninjatpl @@ -0,0 +1,40 @@ +{# 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 %}{% endif %}{% endmacro %} +{% macro newsletter() %}{% if showNewsletter %}

{{ newsletterTitle|default:"Join the guest list" }}

{% if newsletterDescription %}

{{ newsletterDescription }}

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

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

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

{{ tagline }}

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

{{ tagline }}

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

{{ col.header }}

{% endfor %} +{% if showNewsletter %}
{{ newsletter() }}
{% endif %} +
+{{ legalbar() }} +{% endif %} +
+
diff --git a/templates/overrides/gotham/gallery.ninjatpl b/templates/overrides/gotham/gallery.ninjatpl new file mode 100644 index 0000000..b43c393 --- /dev/null +++ b/templates/overrides/gotham/gallery.ninjatpl @@ -0,0 +1,52 @@ +{# 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 %}{% if lightbox %}{% endif %}{% endwith %} diff --git a/templates/overrides/gotham/heading.ninjatpl b/templates/overrides/gotham/heading.ninjatpl index 295e213..3a66ff2 100644 --- a/templates/overrides/gotham/heading.ninjatpl +++ b/templates/overrides/gotham/heading.ninjatpl @@ -1 +1,7 @@ -{% if level|integer == 1 %}

{{ text }}

{% elif level|integer == 3 %}

{{ text }}

{% elif level|integer == 4 %}

{{ text }}

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

{{ text }}

{% endif %} \ No newline at end of file +{# heading override — gilded Antonio display caps with a hairline gold underline. Fields: text, level, textClass (mirrors builtin). #} +{% if level == 1 %}

{{ text }}

+{% elif level == 3 %}

{{ text }}

+{% elif level == 4 %}

{{ text }}

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

{{ text }}

{% endif %} diff --git a/templates/overrides/gotham/hero.ninjatpl b/templates/overrides/gotham/hero.ninjatpl new file mode 100644 index 0000000..00b5b96 --- /dev/null +++ b/templates/overrides/gotham/hero.ninjatpl @@ -0,0 +1,42 @@ +{# 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 %}
{{ eyebrowText }}
{% endif %} +{% if headlineText %}

{{ headlineText }}

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

{{ subText }}

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

{{ descText }}

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

Acclaim

+
{% for logo in trustLogos %}{% if logo.url %}{% img logo.image alt=logo.alt class="h-8 w-auto" %}{% else %}{% img logo.image alt=logo.alt class="h-8 w-auto opacity-60" %}{% endif %}{% endfor %}
+
{% endif %} +{% endmacro %} +{% if variant == "split" %} +
+
{% if hasImage %}{% img backgroundImage alt=headlineText class="absolute inset-0 h-full w-full object-cover" layout="hero" %}{% endif %}
+
{{ herocontent("left") }}
+
+{% else %} +{% set align = "center" %} +{% if variant == "left-aligned" %}{% set align = "left" %}{% endif %} +
+{% if hasImage %}
{% img backgroundImage alt=headlineText class="h-full w-full object-cover" layout="hero" %}{% if showOverlay %}
{% endif %}
{% endif %} + + + +
{{ herocontent(align) }}
+
+{% endif %} diff --git a/templates/overrides/gotham/hours-location.ninjatpl b/templates/overrides/gotham/hours-location.ninjatpl new file mode 100644 index 0000000..0e81600 --- /dev/null +++ b/templates/overrides/gotham/hours-location.ninjatpl @@ -0,0 +1,31 @@ +{# hours-location override — gilded hours ledger + framed map. Map facade + + {% endif %} + + + diff --git a/templates/overrides/gotham/image.ninjatpl b/templates/overrides/gotham/image.ninjatpl new file mode 100644 index 0000000..ff3f827 --- /dev/null +++ b/templates/overrides/gotham/image.ninjatpl @@ -0,0 +1,11 @@ +{# 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" %} +
+
+
+ {% if objectFit == "contain" %}{% img src alt=alt class="w-full h-full object-contain" layout=size %}{% elif objectFit == "fill" %}{% img src alt=alt class="w-full h-full object-fill" layout=size %}{% elif objectFit == "none" %}{% img src alt=alt class="w-full h-full object-none" layout=size %}{% else %}{% img src alt=alt class="w-full h-full object-cover" layout=size %}{% endif %} +
+
+ {% if caption %}
{{ caption }}
{% endif %} +
+{% endwith %} diff --git a/templates/overrides/gotham/logo-strip.ninjatpl b/templates/overrides/gotham/logo-strip.ninjatpl new file mode 100644 index 0000000..1b5f721 --- /dev/null +++ b/templates/overrides/gotham/logo-strip.ninjatpl @@ -0,0 +1,15 @@ +
+
+ {% if heading %}

{{ heading }}

{% endif %} +
+
+ {% for logo in logos %} + {% if logo.url %}{% endif %} + {% img logo.image alt=logo.alt class="h-8 w-auto object-contain" %} + {% if logo.url %}{% endif %} + {% endfor %} +
+
+
+
+{% if marquee %}{% endif %} diff --git a/templates/overrides/gotham/menu-list.ninjatpl b/templates/overrides/gotham/menu-list.ninjatpl new file mode 100644 index 0000000..7add3ab --- /dev/null +++ b/templates/overrides/gotham/menu-list.ninjatpl @@ -0,0 +1,30 @@ +{# 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. #} +
+
+ {% if heading %}

{{ heading }}

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

{{ intro }}

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

{{ footnote }}

{% endif %} +
+
diff --git a/templates/overrides/gotham/navbar.ninjatpl b/templates/overrides/gotham/navbar.ninjatpl new file mode 100644 index 0000000..ae7c133 --- /dev/null +++ b/templates/overrides/gotham/navbar.ninjatpl @@ -0,0 +1,104 @@ +{# 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) %}{{ item.label }}{% endmacro %} +{% macro dropdown(item) %}
+ +{% if use_mega %} +{% else %}{% endif %} +
{% endmacro %} +{% macro brand(size) %}{% if logoType == "text" and logoText.text %}{{ logoText.text }}{% elif logo %}{{ alt|default:companyName }}{% elif companyName %}{{ companyName }}{% endif %}{% endmacro %} +{% macro ctabtn(extra) %}{% if cta_text %}{{ cta_text }}{% endif %}{% endmacro %} + + + diff --git a/templates/overrides/gotham/newsletter-signup.ninjatpl b/templates/overrides/gotham/newsletter-signup.ninjatpl new file mode 100644 index 0000000..c34a2d0 --- /dev/null +++ b/templates/overrides/gotham/newsletter-signup.ninjatpl @@ -0,0 +1,13 @@ +{# 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. #} +
+
+
+
+{% if title %}

{{ title }}

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

{{ description }}

{% endif %} +
{% signup_form list=listSlug source=source submit=submitText success_message=successMessage placeholder=placeholder %}
+
+
+
diff --git a/templates/overrides/gotham/page-suggestions.ninjatpl b/templates/overrides/gotham/page-suggestions.ninjatpl new file mode 100644 index 0000000..d3f7811 --- /dev/null +++ b/templates/overrides/gotham/page-suggestions.ninjatpl @@ -0,0 +1,21 @@ +{# page-suggestions override — themed 404 suggestions. Fields: title, showDescription, emptyMessage; suggestions[] from the provider (.slug/.title/.excerpt). #} +{% if suggestions %} +
+ {% if title %}

{{ title }}

{% endif %} +
    + {% for suggestion in suggestions %} +
  • + ::lucide:chevron-right:sm:: +
    + {{ suggestion.title }} + {% if showDescription and suggestion.excerpt %}

    {{ suggestion.excerpt }}

    {% endif %} +
    +
  • + {% endfor %} +
+
+{% elif emptyMessage %} +
+

{{ emptyMessage }}

+
+{% endif %} diff --git a/templates/overrides/gotham/password-reset-form.ninjatpl b/templates/overrides/gotham/password-reset-form.ninjatpl new file mode 100644 index 0000000..8b038bb --- /dev/null +++ b/templates/overrides/gotham/password-reset-form.ninjatpl @@ -0,0 +1,13 @@ +{# 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. #} +
+
+
+

Reset password

+

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

+
+
+ +
+

Back to sign in

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

{{ heading }}

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

    {{ post.title }}

    +{% if post.published_at %}

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

    {% endif %} +
    +
  2. +{% endfor %} +
+
diff --git a/templates/overrides/gotham/post-hero.ninjatpl b/templates/overrides/gotham/post-hero.ninjatpl new file mode 100644 index 0000000..b2293ff --- /dev/null +++ b/templates/overrides/gotham/post-hero.ninjatpl @@ -0,0 +1,23 @@ +{# 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 %} +
+
{% img post.featured_image_url alt=post.featured_image_alt class="h-full w-full object-cover" layout="hero" %}
+
+{% if center %}
{% endif %} +

{{ post.title }}

+{% if showExcerpt and post.excerpt %}

{{ post.excerpt }}

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

{{ post.title }}

+{% if showExcerpt and post.excerpt %}

{{ post.excerpt }}

{% endif %} +{% if showMeta %}
{% if post.author_name %}{{ post.author_name }}{% endif %}{% if post.published_at %}{{ post.published_at|date:"F j, Y" }}{% endif %}{% if post.reading_time %}{{ post.reading_time }} min read{% endif %}
{% endif %} +
+
+{% endif %} diff --git a/templates/overrides/gotham/post-metadata.ninjatpl b/templates/overrides/gotham/post-metadata.ninjatpl new file mode 100644 index 0000000..00b0b1b --- /dev/null +++ b/templates/overrides/gotham/post-metadata.ninjatpl @@ -0,0 +1,9 @@ +{# post-metadata (gotham) — author/date/reading-time row from the `post` provider. #} +{% if post %} +
+{% if showAuthor and post.author_name %}{% if showAvatar and post.author_avatar %}{% img post.author_avatar alt=post.author_name class="h-7 w-7 rounded-full object-cover ring-1 ring-[hsl(var(--primary)/0.5)]" %}{% endif %}{{ post.author_name }}{% endif %} +{% if showDate and post.published_at %}{% endif %} +{% if showReadingTime and post.reading_time %}{{ post.reading_time }} min read{% endif %} +{% if post.categories %}{% for cat in post.categories %}{{ cat.name }}{% endfor %}{% endif %} +
+{% endif %} diff --git a/templates/overrides/gotham/pricing.ninjatpl b/templates/overrides/gotham/pricing.ninjatpl new file mode 100644 index 0000000..699ee87 --- /dev/null +++ b/templates/overrides/gotham/pricing.ninjatpl @@ -0,0 +1,25 @@ +
+
+ {% if heading %}

{{ heading }}

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

{{ intro }}

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

{{ tier.name }}

+
+

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

+ {% if tier.description %}

{{ tier.description }}

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

{{ billingNote }}

{% endif %} +
+
diff --git a/templates/overrides/gotham/quote.ninjatpl b/templates/overrides/gotham/quote.ninjatpl new file mode 100644 index 0000000..8ee1dc6 --- /dev/null +++ b/templates/overrides/gotham/quote.ninjatpl @@ -0,0 +1,8 @@ +{# 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" %} +
+ {% if align == "center" %}{% endif %} + {{ quote|safe }} + {% if attribution or role or cite %}
{% if attribution %}{{ attribution }}{% endif %}{% if role %}{{ role }}{% endif %}{% if cite %}Source{% endif %}
{% endif %} +
+{% endwith %} diff --git a/templates/overrides/gotham/related-posts.ninjatpl b/templates/overrides/gotham/related-posts.ninjatpl new file mode 100644 index 0000000..383ed4d --- /dev/null +++ b/templates/overrides/gotham/related-posts.ninjatpl @@ -0,0 +1,17 @@ +{# related-posts (gotham) — grid from the `posts` provider. #} +{% if posts %} +
+
+

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

+
+{% for post in posts %} + +{% endfor %} +
+
+
+{% endif %} diff --git a/templates/overrides/gotham/rich-section.ninjatpl b/templates/overrides/gotham/rich-section.ninjatpl new file mode 100644 index 0000000..f39be39 --- /dev/null +++ b/templates/overrides/gotham/rich-section.ninjatpl @@ -0,0 +1,13 @@ +
+
+
+ {% if eyebrow %}

{{ eyebrow }}

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

{{ heading }}

{% endif %} + {% if body %}
{{ body|safe }}
{% endif %} + {% if ctaUrl and ctaLabel %}{{ ctaLabel }}{% endif %} +
+
+ {% if media %}
{% img media alt=mediaAlt class="w-full h-auto object-cover" %}
{% endif %} +
+
+
diff --git a/templates/overrides/gotham/rich-text.ninjatpl b/templates/overrides/gotham/rich-text.ninjatpl new file mode 100644 index 0000000..2a9ba04 --- /dev/null +++ b/templates/overrides/gotham/rich-text.ninjatpl @@ -0,0 +1,2 @@ +{# rich-text override — long-form Cormorant editorial column with gilded links. Field: html (mirrors builtin). #} +{% if html %}
{{ html|safe }}
{% endif %} diff --git a/templates/overrides/gotham/sidebar-nav.ninjatpl b/templates/overrides/gotham/sidebar-nav.ninjatpl new file mode 100644 index 0000000..ac3e00b --- /dev/null +++ b/templates/overrides/gotham/sidebar-nav.ninjatpl @@ -0,0 +1,15 @@ +{# 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" %} + +{% if highlightCurrent %}{% endif %} diff --git a/templates/overrides/gotham/social-links.ninjatpl b/templates/overrides/gotham/social-links.ninjatpl new file mode 100644 index 0000000..4991148 --- /dev/null +++ b/templates/overrides/gotham/social-links.ninjatpl @@ -0,0 +1,13 @@ + diff --git a/templates/overrides/gotham/stats.ninjatpl b/templates/overrides/gotham/stats.ninjatpl new file mode 100644 index 0000000..10624d4 --- /dev/null +++ b/templates/overrides/gotham/stats.ninjatpl @@ -0,0 +1,48 @@ +
+
+ {% if heading %}

{{ heading }}

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

{{ intro }}

{% endif %} +
+ {% for item in items %} +
+
{% if item.prefix %}{{ item.prefix }}{% endif %}{{ item.value }}{% if item.suffix %}{{ item.suffix }}{% endif %}
+ {% if item.label %}
{{ item.label }}
{% endif %} +
+ {% endfor %} +
+
+
+{% if countUp %}{% endif %} diff --git a/templates/overrides/gotham/team.ninjatpl b/templates/overrides/gotham/team.ninjatpl new file mode 100644 index 0000000..1b21b08 --- /dev/null +++ b/templates/overrides/gotham/team.ninjatpl @@ -0,0 +1,21 @@ +
+
+ {% if heading %}

{{ heading }}

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

{{ intro }}

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

{{ member.name }}

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

{{ member.role }}

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

{{ member.bio }}

{% endif %} + {% if member.socials %}
+ {% for social in member.socials %}{% if social.url %}{% endif %}{% endfor %} +
{% endif %} +
+
+ {% endfor %} +
+
+
diff --git a/templates/overrides/gotham/testimonial.ninjatpl b/templates/overrides/gotham/testimonial.ninjatpl new file mode 100644 index 0000000..83eb2a5 --- /dev/null +++ b/templates/overrides/gotham/testimonial.ninjatpl @@ -0,0 +1,27 @@ +
+
+ {% if heading %}

{{ heading }}

{% endif %} +
+ {% for q in quotes %} +
+ {% if q.rating %}
+ + + + + +
{% endif %} +
{% if q.quote %}“{{ q.quote }}”{% endif %}
+
+
+ {% if q.avatar %}{% img q.avatar alt=q.name class="h-full w-full object-cover" %}{% endif %} + + {% if q.name %}{{ q.name }}{% endif %} + {% if q.role %}{{ q.role }}{% endif %} + +
+
+ {% endfor %} +
+
+
diff --git a/templates/overrides/gotham/text.ninjatpl b/templates/overrides/gotham/text.ninjatpl index efa6fb3..e854e8f 100644 --- a/templates/overrides/gotham/text.ninjatpl +++ b/templates/overrides/gotham/text.ninjatpl @@ -1 +1,2 @@ -
{{ text|safe }}
\ No newline at end of file +{# 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. #} +
{{ text|safe }}
diff --git a/templates/overrides/gotham/timeline.ninjatpl b/templates/overrides/gotham/timeline.ninjatpl new file mode 100644 index 0000000..47ac53f --- /dev/null +++ b/templates/overrides/gotham/timeline.ninjatpl @@ -0,0 +1,21 @@ +
+
+ {% if heading %}

{{ heading }}

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

{{ intro }}

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

    {{ step.label }}

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

    {{ step.title }}

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

    {{ step.text }}

    {% endif %} + {% if step.media %}
    {% img step.media alt=step.title class="w-full h-auto object-cover" %}
    {% endif %} +
    +
  2. + {% endfor %} +
+
+
diff --git a/templates/overrides/gotham/utility-bar.ninjatpl b/templates/overrides/gotham/utility-bar.ninjatpl new file mode 100644 index 0000000..bf3d711 --- /dev/null +++ b/templates/overrides/gotham/utility-bar.ninjatpl @@ -0,0 +1,10 @@ +{# 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 %}{% if it.icon %}::{{ it.icon }}::{% endif %}{% if it.label %}{{ it.label }}{% endif %}{% else %}{% if it.icon %}::{{ it.icon }}::{% endif %}{% if it.label %}{{ it.label }}{% endif %}{% endif %}{% endmacro %} +
+
+
{% for it in leftItems %}{{ ubitem(it) }}{% endfor %}
+
{% for it in rightItems %}{{ ubitem(it) }}{% endfor %}
+
+
diff --git a/templates/overrides/gotham/video-embed.ninjatpl b/templates/overrides/gotham/video-embed.ninjatpl new file mode 100644 index 0000000..3374fcc --- /dev/null +++ b/templates/overrides/gotham/video-embed.ninjatpl @@ -0,0 +1,23 @@ +{# video-embed override — gold-framed facade; play button in a gold-ringed medallion. Fields + facade JS mirror builtin verbatim: aspect, url, media, poster, title, caption. #} +{% with aspect=aspect|default:"16:9" %}
{% if caption %}
{{ caption }}
{% endif %}
{% endwith %}