diff --git a/RECOMMENDED_FONTS.md b/RECOMMENDED_FONTS.md index 2af1260..debbf0c 100644 --- a/RECOMMENDED_FONTS.md +++ b/RECOMMENDED_FONTS.md @@ -1,53 +1,36 @@ # Recommended Fonts — Art Deco -This theme ships `fonts.json = []` (Wave-1 policy, see `themes/docs/FONTS.md`). -The intended typographic identity is achieved by an admin assigning the -following Google Fonts to the theme's typography slots after install. +This theme **bundles** its display and body families as subset woff2 files in +`assets/fonts/` (see `fonts.json`). They install as `source=template` rows and +are pre-selected for the theme's typography slots — no admin action is required +for the intended look. The families below are the fallbacks baked into every +`var(--font-heading|body|mono, …)` call, so the theme still reads correctly if +an admin reassigns a slot. -## Picker workflow +## Bundled (shipped in this theme) -1. Sign in as an admin and open **Settings → Typography**. -2. Switch to the **Google Fonts** tab in the font picker. -3. Search for the family, click **Add**, then assign it to the listed slot. +| Slot | Family | Weights | File(s) | License | +|------|--------|---------|---------|---------| +| Heading | **Cinzel** | 400, 700 | `assets/fonts/cinzel-*.woff2` | SIL OFL 1.1 | +| Body | **Cormorant Garamond** | 400, 400 italic, 600, 700 | `assets/fonts/cormorant-garamond-*.woff2` | SIL OFL 1.1 | -## Display heading — Italiana +License record and copyright attribution: `assets/fonts/OFL.txt`. The woff2s are +Latin subsets of the upstream OFL families (subsetting is permitted by the OFL). -- **Source**: `google:Italiana` -- **Slot**: Heading -- **Why**: The spec calls for stamped Art Deco titles in tight tracking. Italiana - is the closest curated Google Fonts match to the high-contrast 1920s display - cuts the brief describes. -- **Fallback already in CSS**: `var(--font-heading, "Italiana", "Cinzel", Georgia, serif)`. +## Mono slot (not bundled — assign if needed) -### Alternative +The theme uses `var(--font-mono, "JetBrains Mono", ui-monospace, monospace)` for +reservation IDs and tabular figures. To match the intended look, an admin can +assign a mono family: -- **Source**: `google:Cinzel` (also acceptable per spec §3) - Pick this if Italiana feels too thin at small sizes. +1. Sign in as an admin, open **Settings → Typography**. +2. Switch to the **Google Fonts** tab and add **JetBrains Mono**. +3. Assign it to the **Mono** slot. -## Body — Cormorant Garamond +If left unset, the CSS falls back to the system monospace stack. -- **Source**: `google:Cormorant Garamond` -- **Slot**: Body -- **Why**: Classical italic and book weights for long-form prose, exactly the - "Cormorant for long-form prose with classical italics" pairing the spec calls - out. -- **Fallback already in CSS**: `var(--font-body, "Cormorant Garamond", "Cormorant", Georgia, serif)`. +## Alternatives (optional) -## Mono — JetBrains Mono - -- **Source**: `google:JetBrains Mono` -- **Slot**: Mono -- **Why**: Spec wants a precise monospace for reservation IDs and room numbers. - JetBrains Mono ships across all curated Google Fonts and is the cleanest - utilitarian option. -- **Fallback already in CSS**: `var(--font-mono, "JetBrains Mono", ui-monospace, monospace)`. - -## Notes for Wave-2 - -- If Italiana proves too brittle on retina at large sizes, commission a - redistributable Art Deco display face and bundle it via `fonts.json`. The - rest of the CSS is already variable-driven and will pick the new family up - without further edits. -- Body italic for the drop-cap relies on the body family having an italic - variant; both Cormorant Garamond and Cormorant ship italics in the curated - Google Fonts list — no extra picker action needed. +- Heading: `google:Italiana` — a thinner, higher-contrast display cut; already + the second fallback in the heading `var()` list. +- Body: the system serif stack (`Georgia, serif`) is the final fallback. diff --git a/RECOMMENDED_ICONS.md b/RECOMMENDED_ICONS.md new file mode 100644 index 0000000..3f8e015 --- /dev/null +++ b/RECOMMENDED_ICONS.md @@ -0,0 +1,24 @@ +# Recommended Icons — Art Deco + +This theme references icons from a single bundled pack. `plugin.mod` declares +it in `required_icon_packs`; until the loader auto-installs declared packs, an +admin should confirm it is enabled before activation. + +## Required pack + +- **lucide** — ships bundled with the CMS (v0.460). Enabled by default. No + action needed in most installs; if icons render as empty boxes, open + `/admin/icons` and confirm **Lucide** is installed and enabled. + +## Icons used by the theme chrome and overrides + +All via the sprite pipeline (`` +or the `::lucide:::` shorthand): + +`arrow-right`, `check`, `chevron-down`, `chevron-left`, `chevron-right`, `x`, +`play`, `download`, `navigation`, `map-pin`, `diamond`, `star`, `flag`, +`globe`. + +Block content that stores an icon (e.g. feature-grid items, social links, +contact channels) may reference any installed pack as a `pack:name` string; +the schemas default to `lucide` so the theme renders without extra setup. diff --git a/assets/fonts/OFL.txt b/assets/fonts/OFL.txt new file mode 100644 index 0000000..2edb6ef --- /dev/null +++ b/assets/fonts/OFL.txt @@ -0,0 +1,23 @@ +Bundled fonts — license record (SIL Open Font License, Version 1.1) +==================================================================== + +The woff2 files in this directory are subsets (Latin) of the following +open-source families, both distributed under the SIL Open Font License 1.1 +(https://openfontlicense.org). Subsetting and format conversion (TTF to +woff2) are expressly permitted by the OFL; the derived subsets remain under +the same license. + +Cinzel + cinzel-400.woff2, cinzel-700.woff2 + Copyright (c) Natanael Gama (https://ndiscover.com), with Reserved Font + Name "Cinzel". + +Cormorant Garamond + cormorant-garamond-400.woff2, cormorant-garamond-400-italic.woff2, + cormorant-garamond-600.woff2, cormorant-garamond-700.woff2 + Copyright (c) Christian Thalmann, Catharsis Fonts + (https://catharsisfonts.com), with Reserved Font Name "Cormorant". + +The full SIL Open Font License 1.1 text applies to each family and is +available at https://openfontlicense.org. This file records the license and +copyright attribution as required for redistribution under the OFL. diff --git a/assets/fonts/cinzel-400.woff2 b/assets/fonts/cinzel-400.woff2 new file mode 100644 index 0000000..8db97d4 Binary files /dev/null and b/assets/fonts/cinzel-400.woff2 differ diff --git a/assets/fonts/cinzel-700.woff2 b/assets/fonts/cinzel-700.woff2 new file mode 100644 index 0000000..2232956 Binary files /dev/null and b/assets/fonts/cinzel-700.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/blocks/blocks.yaml b/blocks/blocks.yaml index fea7dd8..77ca398 100644 --- a/blocks/blocks.yaml +++ b/blocks/blocks.yaml @@ -1,8 +1,10 @@ -# Art Deco theme block definitions (codeless .bnp, WO-WZ-021). -# Keys are fully qualified (art-deco:) because the codeless loader -# registers definitions verbatim — there is no PluginBlockRegistry prefixing -# in the codeless path (unlike the wasm/templ path). The qualified keys match -# the hardcoded data-block attributes and the master_pages.json references. +# Art Deco theme block definitions (codeless .bnp, WO-WZ-021 / WO-TF-011). +# Only TRUE persona furniture remains as theme blocks. Everything that +# duplicated a builtin (footer, hero/marquee, gallery, divider, pull-quote) +# is now a template OVERRIDE of the builtin key (templates/overrides/art-deco/). +# Keys are fully qualified (art-deco:) — the codeless loader registers +# definitions verbatim; the qualified keys match the data-block attributes, +# master_pages.json and seed references. blocks: - key: art-deco:reservation title: Reservation Strip @@ -12,37 +14,19 @@ blocks: template: reservation.ninjatpl - key: art-deco:menu title: Tasting Menu - description: Symmetric two-column menu card listing courses with description and price. + description: Symmetric menu card listing courses with tasting note and price. category: content schema: menu.schema.json template: menu.ninjatpl - - key: art-deco:gallery_fan - title: Fan Gallery - description: Mirrored gallery framed by sunburst — collapses to single column under 640px. + - key: art-deco:private_dining + title: Private Dining + description: Private dining room showcase — framed image with room details and an enquiry CTA. category: content - schema: gallery_fan.schema.json - template: gallery_fan.ninjatpl - - key: art-deco:divider_fan - title: Fan Divider - description: Pure-CSS gold geometric divider — sunburst, scallop or ziggurat. - category: layout - schema: divider_fan.schema.json - template: divider_fan.ninjatpl - - key: art-deco:footer - title: Footer - description: Centered Art Deco footer with gold rule, address, reservations email and optional social row. - category: layout - schema: footer.schema.json - template: footer.ninjatpl - - key: art-deco:marquee_hero - title: Marquee Hero - description: Wide hero with stepped ziggurat frame and sunburst overlay. + schema: private_dining.schema.json + template: private_dining.ninjatpl + - key: art-deco:chef_signature + title: Chef's Signature + description: Centred signature-dish feature with a chef's note between sunburst crests. category: content - schema: marquee_hero.schema.json - template: marquee_hero.ninjatpl - - key: art-deco:press_quote - title: Press Quote - description: Italic Cormorant pull-quote between fan dividers, optional star rating. - category: content - schema: press_quote.schema.json - template: press_quote.ninjatpl + schema: chef_signature.schema.json + template: chef_signature.ninjatpl diff --git a/blocks/chef_signature.ninjatpl b/blocks/chef_signature.ninjatpl new file mode 100644 index 0000000..2b74741 --- /dev/null +++ b/blocks/chef_signature.ninjatpl @@ -0,0 +1,11 @@ +{# chef_signature persona block — centred signature-dish feature with sunburst crests. Media via {% img %}. #} +
+
+ {% if eyebrow %}
{{ eyebrow }}
{% endif %} + {% if image %}
{% img image alt=dish class="w-full h-auto object-cover" %}
{% endif %} + + {% if dish %}

{{ dish }}

{% endif %} + {% if note %}
{{ note|safe }}
{% endif %} + {% if chef %}

{{ chef }}

{% endif %} +
+
diff --git a/blocks/chef_signature.schema.json b/blocks/chef_signature.schema.json new file mode 100644 index 0000000..c19a6d9 --- /dev/null +++ b/blocks/chef_signature.schema.json @@ -0,0 +1,39 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Chef's Signature", + "description": "Centred signature-dish feature with a chef's note between sunburst crests.", + "type": "object", + "properties": { + "eyebrow": { + "type": "string", + "title": "Eyebrow", + "x-editor": "text", + "default": "Chef's signature" + }, + "dish": { + "type": "string", + "title": "Dish name", + "x-editor": "text", + "default": "" + }, + "note": { + "type": "string", + "title": "Chef's note", + "description": "A short tasting note or story about the dish.", + "x-editor": "richtext", + "default": "" + }, + "chef": { + "type": "string", + "title": "Chef name", + "x-editor": "text", + "default": "" + }, + "image": { + "type": "string", + "title": "Dish image", + "x-editor": "media", + "default": "" + } + } +} diff --git a/blocks/divider_fan.ninjatpl b/blocks/divider_fan.ninjatpl deleted file mode 100644 index 7ef1b92..0000000 --- a/blocks/divider_fan.ninjatpl +++ /dev/null @@ -1 +0,0 @@ -{% if variant == "scallop" %}{% elif variant == "ziggurat" %}{% else %}{% endif %} diff --git a/blocks/divider_fan.schema.json b/blocks/divider_fan.schema.json deleted file mode 100644 index 1ba530b..0000000 --- a/blocks/divider_fan.schema.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Fan Divider", - "description": "Pure-CSS geometric gold divider — sunburst, scallop or ziggurat.", - "type": "object", - "properties": { - "variant": { - "type": "string", - "title": "Variant", - "description": "Choose the divider motif.", - "x-editor": "select", - "default": "sunburst", - "enum": ["sunburst", "scallop", "ziggurat"] - } - } -} diff --git a/blocks/footer.ninjatpl b/blocks/footer.ninjatpl deleted file mode 100644 index 31898e5..0000000 --- a/blocks/footer.ninjatpl +++ /dev/null @@ -1,10 +0,0 @@ -
-
-
- {% if address %}
{% for line in address|lines %}
{{ line }}
{% endfor %}
{% endif %} - {% if reservationsEmail %}

{{ reservationsEmail }}

{% endif %} - {% if menuName %}{% endif %} - {% if showSocial != "false" %}
{% endif %} -
-
-
diff --git a/blocks/footer.schema.json b/blocks/footer.schema.json deleted file mode 100644 index 2ad2443..0000000 --- a/blocks/footer.schema.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Footer", - "description": "Centered Art Deco footer with gold rule, address, reservations email and optional social row.", - "type": "object", - "properties": { - "address": { - "type": "string", - "title": "Address", - "description": "Multi-line postal address.", - "x-editor": "textarea", - "default": "" - }, - "reservationsEmail": { - "type": "string", - "title": "Reservations email", - "description": "Public email shown in the footer for reservations enquiries.", - "x-editor": "text", - "default": "" - }, - "showSocial": { - "type": "string", - "title": "Show social row", - "description": "Whether to render the social-icon row.", - "x-editor": "select", - "default": "true", - "enum": ["true", "false"] - }, - "menuName": { - "type": "string", - "title": "Footer menu", - "description": "Name of the menu to render in the footer.", - "x-editor": "menu-select", - "default": "" - } - } -} diff --git a/blocks/gallery_fan.ninjatpl b/blocks/gallery_fan.ninjatpl deleted file mode 100644 index 255b2cd..0000000 --- a/blocks/gallery_fan.ninjatpl +++ /dev/null @@ -1,12 +0,0 @@ -
-
-
- -
- {% if not images %}
Add images to populate the fan gallery.
{% endif %} - {% for img in images %}
{% if img.src %}{{ img.caption }}{% else %}{% endif %}{% if img.caption %}
{{ img.caption }}
{% endif %}
{% endfor %} -
- -
-
-
diff --git a/blocks/gallery_fan.schema.json b/blocks/gallery_fan.schema.json deleted file mode 100644 index 25a2678..0000000 --- a/blocks/gallery_fan.schema.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Fan Gallery", - "description": "Mirrored gallery framed by sunburst rays; collapses to single column under 640px.", - "type": "object", - "properties": { - "images": { - "type": "array", - "title": "Images", - "description": "Images to mirror across the fan.", - "x-editor": "array", - "default": [], - "items": { - "type": "object", - "properties": { - "src": { - "type": "string", - "title": "Image", - "x-editor": "media" - }, - "caption": { - "type": "string", - "title": "Caption", - "x-editor": "text" - } - } - } - }, - "columns": { - "type": "integer", - "title": "Columns", - "description": "Columns on desktop (2, 3 or 4).", - "x-editor": "select", - "default": 3, - "enum": [2, 3, 4] - } - } -} diff --git a/blocks/marquee_hero.ninjatpl b/blocks/marquee_hero.ninjatpl deleted file mode 100644 index f471353..0000000 --- a/blocks/marquee_hero.ninjatpl +++ /dev/null @@ -1,12 +0,0 @@ -
- {% if image %}{% endif %} - -
-
- {% if eyebrow %}

{{ eyebrow }}

{% endif %} - {% if title %}

{{ title }}

{% endif %} - {% if subtitle %}
{{ subtitle|safe }}
{% endif %} - {% if ctaLabel and ctaHref %}{{ ctaLabel }}{% endif %} -
-
-
diff --git a/blocks/marquee_hero.schema.json b/blocks/marquee_hero.schema.json deleted file mode 100644 index b84c477..0000000 --- a/blocks/marquee_hero.schema.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Marquee Hero", - "description": "Wide hero with stepped ziggurat frame and sunburst overlay.", - "type": "object", - "properties": { - "eyebrow": { - "type": "string", - "title": "Eyebrow", - "description": "Small caps line above the title.", - "x-editor": "text", - "default": "" - }, - "title": { - "type": "string", - "title": "Title", - "description": "Display headline in Italiana caps.", - "x-editor": "text", - "default": "" - }, - "subtitle": { - "type": "string", - "title": "Subtitle", - "description": "Long-form supporting copy (rich text).", - "x-editor": "richtext", - "default": "" - }, - "ctaLabel": { - "type": "string", - "title": "CTA label", - "x-editor": "text", - "default": "" - }, - "ctaHref": { - "type": "string", - "title": "CTA URL", - "x-editor": "link", - "default": "" - }, - "image": { - "type": "string", - "title": "Background image", - "x-editor": "media", - "default": "" - } - } -} diff --git a/blocks/press_quote.ninjatpl b/blocks/press_quote.ninjatpl deleted file mode 100644 index c087de3..0000000 --- a/blocks/press_quote.ninjatpl +++ /dev/null @@ -1,9 +0,0 @@ -
-
- -
{% if quote %}{{ quote|safe }}{% else %}Add a press quote to populate this block.{% endif %}
- {% if stars|integer > 0 %}
{% if stars|integer >= 1 %}{% else %}{% endif %}{% if stars|integer >= 2 %}{% else %}{% endif %}{% if stars|integer >= 3 %}{% else %}{% endif %}{% if stars|integer >= 4 %}{% else %}{% endif %}{% if stars|integer >= 5 %}{% else %}{% endif %}
{% endif %} - {% if source %}

{{ source }}

{% endif %} - -
-
diff --git a/blocks/press_quote.schema.json b/blocks/press_quote.schema.json deleted file mode 100644 index 80135c3..0000000 --- a/blocks/press_quote.schema.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Press Quote", - "description": "Italic Cormorant pull-quote between fan dividers.", - "type": "object", - "properties": { - "quote": { - "type": "string", - "title": "Quote", - "description": "The press quotation (rich text).", - "x-editor": "richtext", - "default": "" - }, - "source": { - "type": "string", - "title": "Source", - "description": "Attribution line — e.g. 'The Daily, March 2024'.", - "x-editor": "text", - "default": "" - }, - "stars": { - "type": "integer", - "title": "Stars", - "description": "Filled-star count, clamped to 0–5.", - "x-editor": "number", - "default": 0, - "minimum": 0, - "maximum": 5 - } - } -} diff --git a/blocks/private_dining.ninjatpl b/blocks/private_dining.ninjatpl new file mode 100644 index 0000000..b1c5e24 --- /dev/null +++ b/blocks/private_dining.ninjatpl @@ -0,0 +1,15 @@ +{# private_dining persona block — framed room image + gilded details panel. Media via {% img %}. #} +
+
+
+ {% if image %}
{% img image alt=title class="w-full h-auto object-cover" %}
{% endif %} +
+
+ {% if eyebrow %}
{{ eyebrow }}
{% endif %} + {% if title %}

{{ title }}

{% endif %} + {% if description %}
{{ description|safe }}
{% endif %} + {% if seats or minimum %}
{% if seats %}
Capacity
{{ seats }}
{% endif %}{% if minimum %}
Minimum
{{ minimum }}
{% endif %}
{% endif %} + {% if enquireUrl %}{{ enquireLabel|default:"Enquire" }}{% endif %} +
+
+
diff --git a/blocks/private_dining.schema.json b/blocks/private_dining.schema.json new file mode 100644 index 0000000..6f06df8 --- /dev/null +++ b/blocks/private_dining.schema.json @@ -0,0 +1,67 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Private Dining", + "description": "Private dining room showcase — framed image with room details and an enquiry CTA.", + "type": "object", + "properties": { + "eyebrow": { + "type": "string", + "title": "Eyebrow", + "description": "Small caps label above the heading.", + "x-editor": "text", + "default": "Private dining" + }, + "title": { + "type": "string", + "title": "Room name", + "x-editor": "text", + "default": "" + }, + "description": { + "type": "string", + "title": "Description", + "description": "Room description and what the experience includes.", + "x-editor": "richtext", + "default": "" + }, + "image": { + "type": "string", + "title": "Room image", + "x-editor": "media", + "default": "" + }, + "seats": { + "type": "string", + "title": "Seats", + "description": "e.g. 'Seats 2 to 14'.", + "x-editor": "text", + "default": "" + }, + "minimum": { + "type": "string", + "title": "Minimum spend", + "description": "e.g. 'From $2,400'.", + "x-editor": "text", + "default": "" + }, + "enquireLabel": { + "type": "string", + "title": "Enquire label", + "x-editor": "text", + "default": "Enquire" + }, + "enquireUrl": { + "type": "string", + "title": "Enquire URL", + "x-editor": "link", + "default": "" + }, + "mediaPosition": { + "type": "string", + "title": "Image side", + "enum": ["left", "right"], + "x-editor": "select", + "default": "left" + } + } +} diff --git a/fonts.json b/fonts.json index fe51488..68378a4 100644 --- a/fonts.json +++ b/fonts.json @@ -1 +1,20 @@ -[] +[ + { + "name": "Cinzel", + "family": "Cinzel", + "variants": [ + { "weight": "400", "style": "normal", "file": "fonts/cinzel-400.woff2" }, + { "weight": "700", "style": "normal", "file": "fonts/cinzel-700.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" } + ] + } +] diff --git a/manifest.yaml b/manifest.yaml index 340dbdb..97c3db4 100644 --- a/manifest.yaml +++ b/manifest.yaml @@ -30,12 +30,79 @@ page_templates: title: Grand Ballroom description: "Edge-to-edge gallery layout for venues and lookbooks." slots: [header, main, footer] + - system: art-deco + key: blog-index + title: Editorial Index + description: "Blog index with gilded masthead, featured and post-grid slots." + slots: [header, featured, main, footer] + - system: art-deco + key: contact + title: Reservations + description: "Contact layout with optional aside for hours and location." + slots: [header, main, aside, footer] + - system: art-deco + key: auth + title: Members + description: "Centred auth shell with wordmark and sunburst crest." + slots: [main, footer] template_overrides: + # Chrome + - { template: art-deco, block: navbar } + - { template: art-deco, block: footer } + - { template: art-deco, block: utility-bar } + - { template: art-deco, block: announcement-bar } + # Marketing + - { template: art-deco, block: hero } + - { template: art-deco, block: feature-grid } + - { template: art-deco, block: rich-section } + - { template: art-deco, block: stats } + - { template: art-deco, block: testimonial } + - { template: art-deco, block: logo-strip } + - { template: art-deco, block: pricing } + - { template: art-deco, block: team } + - { template: art-deco, block: faq } + - { template: art-deco, block: timeline } + - { template: art-deco, block: cta } + - { template: art-deco, block: countdown } + # Primitives - { template: art-deco, block: heading } - { template: art-deco, block: text } - - { template: art-deco, block: button } + - { template: art-deco, block: rich-text } - { template: art-deco, block: image } + - { template: art-deco, block: quote } + - { template: art-deco, block: divider } + - { template: art-deco, block: button } + - { template: art-deco, block: card } + - { template: art-deco, block: gallery } + - { template: art-deco, block: video-embed } + - { template: art-deco, block: audio-embed } + # Commerce / local + - { template: art-deco, block: menu-list } + - { template: art-deco, block: event-list } + - { template: art-deco, block: contact-form } + - { template: art-deco, block: contact-card } + - { template: art-deco, block: hours-location } + - { template: art-deco, block: social-links } + # Blog family + - { template: art-deco, block: blog-hero } + - { template: art-deco, block: featured-posts } + - { template: art-deco, block: related-posts } + - { template: art-deco, block: popular-posts } + - { template: art-deco, block: category-list } + - { template: art-deco, block: post-hero } + - { template: art-deco, block: post-metadata } + - { template: art-deco, block: author-bio } + - { template: art-deco, block: author-bio-hero } + - { template: art-deco, block: newsletter-signup } + - { template: art-deco, block: breadcrumbs } + - { template: art-deco, block: sidebar-nav } + # Auth surface + - { template: art-deco, block: auth-form } + - { template: art-deco, block: password-reset-form } + - { template: art-deco, block: auth-status } + # System + - { template: art-deco, block: page-suggestions } email_wrappers: - art-deco diff --git a/master_pages.json b/master_pages.json index 6d64ed4..4713d0c 100644 --- a/master_pages.json +++ b/master_pages.json @@ -2,12 +2,12 @@ { "key": "art-deco:default-master", "title": "Art Deco Default Master", - "page_templates": ["default", "article"], + "page_templates": ["default", "article", "blog-index", "contact"], "blocks": [ - { "block_key": "navbar", "title": "Brass Navigation", "content": { "menuName": "main" }, "slot": "header", "sort_order": 0 }, - { "block_key": "art-deco:divider_fan", "title": "Header Fan Divider", "content": { "variant": "sunburst" }, "slot": "header", "sort_order": 1 }, + { "block_key": "navbar", "title": "Gilded Navigation", "content": { "menuName": "main", "logoType": "text", "logoText": { "text": "Maison Doré" } }, "slot": "header", "sort_order": 0 }, + { "block_key": "divider", "title": "Header Fan Divider", "content": { "variant": "ornament", "icon": "lucide:diamond" }, "slot": "header", "sort_order": 1 }, { "block_key": "slot", "title": "Main Content", "content": { "slotName": "main", "placeholder": "Page content" }, "slot": "main", "sort_order": 0 }, - { "block_key": "art-deco:footer", "title": "Site Footer", "content": { "address": "", "reservationsEmail": "", "showSocial": "true" }, "slot": "footer", "sort_order": 0 } + { "block_key": "footer", "title": "Site Footer", "content": { "layout": "centered", "companyName": "Maison Doré", "tagline": "A supper club in the grand manner." }, "slot": "footer", "sort_order": 0 } ] }, { @@ -15,11 +15,11 @@ "title": "Art Deco Marquee Master", "page_templates": ["landing", "full-width"], "blocks": [ - { "block_key": "navbar", "title": "Brass Navigation", "content": { "menuName": "main" }, "slot": "marquee", "sort_order": 0 }, - { "block_key": "art-deco:reservation", "title": "Reservation Strip", "content": { "phone": "", "openTable": "" }, "slot": "marquee", "sort_order": 1 }, + { "block_key": "navbar", "title": "Gilded Navigation", "content": { "menuName": "main", "logoType": "text", "logoText": { "text": "Maison Doré" } }, "slot": "marquee", "sort_order": 0 }, + { "block_key": "art-deco:reservation", "title": "Reservation Strip", "content": { "phone": "", "openTable": "", "hours": [] }, "slot": "marquee", "sort_order": 1 }, { "block_key": "slot", "title": "Main Content", "content": { "slotName": "main" }, "slot": "main", "sort_order": 0 }, - { "block_key": "art-deco:divider_fan", "title": "CTA Fan Divider", "content": { "variant": "scallop" }, "slot": "cta", "sort_order": 0 }, - { "block_key": "art-deco:footer", "title": "Site Footer", "content": { "showSocial": "true" }, "slot": "footer", "sort_order": 0 } + { "block_key": "divider", "title": "CTA Fan Divider", "content": { "variant": "ornament", "icon": "lucide:diamond" }, "slot": "cta", "sort_order": 0 }, + { "block_key": "footer", "title": "Site Footer", "content": { "layout": "centered", "companyName": "Maison Doré", "tagline": "A supper club in the grand manner." }, "slot": "footer", "sort_order": 0 } ] } ] diff --git a/plugin.mod b/plugin.mod index 236b79f..332474f 100644 --- a/plugin.mod +++ b/plugin.mod @@ -7,3 +7,4 @@ description = "Luxury Art Deco theme with champagne gold gradients, jet black, i kind = "theme" categories = ["templates"] tags = ["luxury", "gold", "hospitality", "hotel", "wedding", "jewellery", "vintage", "glam", "fine-dining"] +required_icon_packs = ["lucide"] diff --git a/presets.json b/presets.json index 51f6d84..78943c3 100644 --- a/presets.json +++ b/presets.json @@ -52,9 +52,9 @@ { "id": "ivory-rose", "name": "Ivory & Rose Gold", - "description": "Soft ivory with rose-gold flourishes for weddings and jewellers.", + "description": "Soft ivory by day, warm ember by night, with rose-gold flourishes for weddings and jewellers.", "theme": { - "mode": "light", + "mode": "both", "lightColors": { "background": "36 40% 97%", "foreground": "20 15% 14%", @@ -75,15 +75,57 @@ "border": "25 30% 82%", "input": "25 25% 86%", "ring": "18 55% 58%" + }, + "darkColors": { + "background": "20 18% 8%", + "foreground": "30 30% 92%", + "card": "20 18% 12%", + "cardForeground": "30 30% 92%", + "popover": "20 18% 12%", + "popoverForeground": "30 30% 92%", + "primary": "18 60% 62%", + "primaryForeground": "20 20% 8%", + "secondary": "20 15% 16%", + "secondaryForeground": "30 25% 88%", + "muted": "20 12% 14%", + "mutedForeground": "25 15% 62%", + "accent": "15 62% 62%", + "accentForeground": "20 20% 8%", + "destructive": "0 72% 52%", + "destructiveForeground": "30 30% 94%", + "border": "20 20% 24%", + "input": "20 18% 20%", + "ring": "18 60% 62%" } } }, { "id": "velvet-onyx", "name": "Velvet Onyx", - "description": "Deep velvet onyx with gold and rose stage-lights for theatre and after-dark dining.", + "description": "Velvet-tinted ivory by day, deep onyx and gold stage-light by night, for theatre and after-dark dining.", "theme": { - "mode": "dark", + "mode": "both", + "lightColors": { + "background": "280 15% 96%", + "foreground": "260 20% 12%", + "card": "280 12% 99%", + "cardForeground": "260 20% 12%", + "popover": "280 12% 99%", + "popoverForeground": "260 20% 12%", + "primary": "42 60% 48%", + "primaryForeground": "280 20% 8%", + "secondary": "280 15% 92%", + "secondaryForeground": "260 20% 12%", + "muted": "280 12% 93%", + "mutedForeground": "260 12% 40%", + "accent": "320 35% 52%", + "accentForeground": "280 20% 96%", + "destructive": "0 70% 45%", + "destructiveForeground": "280 15% 96%", + "border": "280 15% 82%", + "input": "280 12% 86%", + "ring": "42 60% 48%" + }, "darkColors": { "background": "260 20% 6%", "foreground": "42 40% 92%", @@ -106,5 +148,55 @@ "ring": "42 70% 60%" } } + }, + { + "id": "emerald-gilt", + "name": "Emerald & Gilt", + "description": "Deco emerald and gilt — parchment and jade by day, emerald lacquer and gold by night.", + "theme": { + "mode": "both", + "lightColors": { + "background": "45 35% 95%", + "foreground": "160 25% 12%", + "card": "45 30% 98%", + "cardForeground": "160 25% 12%", + "popover": "45 30% 98%", + "popoverForeground": "160 25% 12%", + "primary": "42 55% 48%", + "primaryForeground": "160 25% 8%", + "secondary": "150 15% 88%", + "secondaryForeground": "160 25% 12%", + "muted": "150 12% 91%", + "mutedForeground": "160 12% 38%", + "accent": "160 40% 30%", + "accentForeground": "45 35% 95%", + "destructive": "0 70% 45%", + "destructiveForeground": "45 35% 95%", + "border": "150 20% 78%", + "input": "150 18% 83%", + "ring": "42 55% 48%" + }, + "darkColors": { + "background": "162 25% 6%", + "foreground": "45 35% 92%", + "card": "162 22% 9%", + "cardForeground": "45 35% 92%", + "popover": "162 22% 9%", + "popoverForeground": "45 35% 92%", + "primary": "42 68% 58%", + "primaryForeground": "162 25% 8%", + "secondary": "162 18% 13%", + "secondaryForeground": "45 30% 88%", + "muted": "162 15% 11%", + "mutedForeground": "45 15% 62%", + "accent": "158 45% 45%", + "accentForeground": "162 25% 6%", + "destructive": "0 72% 52%", + "destructiveForeground": "45 35% 95%", + "border": "162 20% 20%", + "input": "162 18% 16%", + "ring": "42 68% 58%" + } + } } ] diff --git a/seed/seed.json b/seed/seed.json new file mode 100644 index 0000000..19f63d3 --- /dev/null +++ b/seed/seed.json @@ -0,0 +1,200 @@ +{ + "demo": true, + "settings": { + "merge": { + "companyName": "Maison Doré", + "tagline": "A supper club in the grand manner." + } + }, + "data_tables": [ + { + "key": "contact_submissions", + "name": "Reservation Enquiries", + "description": "Seeded contact-form submissions for the Maison Doré 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": "Maison Doré", + "template_key": "landing", + "blocks": [ + { + "block_key": "hero", + "title": "Hero", + "slot": "main", + "sort_order": 1, + "content": { + "template": "centered", + "eyebrow": "Established 1926", + "headline": "Maison Doré", + "subheadline": "A supper club in the grand manner.", + "description": "Dinner and late suppers, served under gilded ceilings. A short tasting menu, a long cellar, and a room built for the evening.", + "ctaPrimary": { "text": "Reserve a table", "url": "/contact" }, + "ctaSecondary": { "text": "Read the menu", "url": "#tasting" } + } + }, + { + "block_key": "art-deco:chef_signature", + "title": "Chef's Signature", + "slot": "main", + "sort_order": 2, + "content": { + "eyebrow": "Chef's signature", + "dish": "Gold-leaf oyster, champagne mignonette", + "note": "The dish that opened the room in 1926. We still serve it the same way, on ice, one at a time.", + "chef": "Elise Marchand, Head Chef" + } + }, + { + "block_key": "art-deco:menu", + "title": "Tasting Menu", + "slot": "main", + "sort_order": 3, + "content": { + "title": "Chef's Tasting", + "courses": [ + { "name": "Oysters, champagne mignonette", "description": "Half a dozen, on ice.", "price": "$32" }, + { "name": "Consommé doré", "description": "Clarified beef broth, tarragon, gold leaf.", "price": "$24" }, + { "name": "Turbot, beurre blanc", "description": "Line-caught, roasted on the bone.", "price": "$58" }, + { "name": "Crown of lamb", "description": "Herbs, salt-baked roots, jus.", "price": "$62" }, + { "name": "Grand Marnier soufflé", "description": "To finish. Order at the start.", "price": "$28" } + ] + } + }, + { + "block_key": "art-deco:private_dining", + "title": "Private Dining", + "slot": "main", + "sort_order": 4, + "content": { + "eyebrow": "Private dining", + "title": "The Gold Room", + "description": "A room of your own behind the main floor. One long table, a dedicated maître d', and the full cellar. We write the menu with you.", + "seats": "Seats 2 to 14", + "minimum": "From $2,400", + "enquireLabel": "Enquire", + "enquireUrl": "/contact", + "mediaPosition": "left" + } + } + ] + }, + { + "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": "

Maison Doré opened on a narrow street in 1926 and has kept its hours ever since. We take reservations for dinner and hold a few tables back for late suppers.

The kitchen runs a short tasting menu that changes with the season. The cellar runs long. The room stays the same, which is the point.

" } }, + { "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 for dinner, stay for the last pour.", "attribution": "The House" } } + ] + }, + { + "slug": "/blog", + "title": "From the Floor", + "template_key": "blog-index", + "blocks": [ + { "block_key": "card", "title": "Post: Gold Standard", "slot": "main", "sort_order": 1, "content": { "layout": "vertical", "title": "The gold standard", "text": "

Why one oyster, served one at a time, still opens every table.

", "link": "/blog/the-gold-standard", "linkLabel": "Read on" } }, + { "block_key": "card", "title": "Post: A Night", "slot": "main", "sort_order": 2, "content": { "layout": "vertical", "title": "A night at the club", "text": "

From the first cocktail to the last soufflé. How an evening unfolds.

", "link": "/blog/a-night-at-the-club", "linkLabel": "Read on" } }, + { "block_key": "card", "title": "Post: Cellar", "slot": "main", "sort_order": 3, "content": { "layout": "vertical", "title": "Notes from the cellar", "text": "

What we pour and why. A short walk through the list.

", "link": "/blog/notes-from-the-cellar", "linkLabel": "Read on" } } + ] + }, + { + "slug": "/blog/the-gold-standard", + "title": "The gold standard", + "template_key": "article", + "parent_slug": "/blog", + "blocks": [ + { "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "The gold standard", "level": 1 } }, + { "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "

The oyster came first, in 1926, and it has not left the menu since. We serve it on ice, one at a time, with a champagne mignonette and a single leaf of gold.

The point is restraint. One oyster, done right, sets the tone for the meal that follows.

" } } + ] + }, + { + "slug": "/blog/a-night-at-the-club", + "title": "A night at the club", + "template_key": "article", + "parent_slug": "/blog", + "blocks": [ + { "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "A night at the club", "level": 1 } }, + { "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "

Doors open at six. The first cocktail arrives before the menu. Dinner runs five courses, unhurried, and the soufflé is ordered at the start.

The band plays after nine. Late suppers run until the last table is ready to leave.

" } } + ] + }, + { + "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 long and the choices are few, by design. We pour champagne by the glass, keep a deep bench of burgundy, and hold a small shelf of older bottles for the ones who ask.

Tell the sommelier what you are eating. The rest takes care of itself.

" } } + ] + }, + { + "slug": "/contact", + "title": "Reservations", + "template_key": "contact", + "blocks": [ + { + "block_key": "contact-form", + "title": "Reservation Enquiry", + "slot": "main", + "sort_order": 1, + "content": { + "heading": "Request a table", + "description": "Tell us the date, the hour and the party. We will confirm by email.", + "submitLabel": "Request a table", + "successMessage": "Thank you. Your table request has been received. We will confirm 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": "Date, time and any notes", "type": "textarea", "required": true, "width": "full" } + ], + "formConfig": { "enabled": true, "captchaEnabled": false, "targetTable": "contact_submissions" } + } + }, + { + "block_key": "hours-location", + "title": "Hours & Location", + "slot": "aside", + "sort_order": 1, + "content": { + "heading": "Hours", + "hours": [ + { "day": "Tuesday to Thursday", "hours": "6pm to late" }, + { "day": "Friday to Saturday", "hours": "5pm to late" }, + { "day": "Sunday", "hours": "5pm to 10pm" }, + { "day": "Monday", "closed": true } + ], + "specialHoursNote": "Late suppers from 10pm, Friday and Saturday.", + "address": "12 Gilt Lane\nThe Old Quarter", + "phone": "+1 555 0126" + } + } + ] + }, + { + "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 tables and private-room dates.

" } } + ] + } + ], + "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": "From the Floor", "page_slug": "/blog", "sort_order": 3 }, + { "menu": "main", "label": "Reservations", "page_slug": "/contact", "sort_order": 4 } + ] +} diff --git a/seed/workflows.json b/seed/workflows.json new file mode 100644 index 0000000..9998606 --- /dev/null +++ b/seed/workflows.json @@ -0,0 +1,19 @@ +{ + "workflows": [ + { + "key": "contact_notify_admins", + "name": "Reservation enquiry to admins", + "description": "On a reservation-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/art-deco/auth.ninjatpl b/templates/art-deco/auth.ninjatpl new file mode 100644 index 0000000..4f8af2d --- /dev/null +++ b/templates/art-deco/auth.ninjatpl @@ -0,0 +1,18 @@ + + +{{ 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/art-deco/blog-index.ninjatpl b/templates/art-deco/blog-index.ninjatpl new file mode 100644 index 0000000..e10741f --- /dev/null +++ b/templates/art-deco/blog-index.ninjatpl @@ -0,0 +1,23 @@ + + +{{ 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/art-deco/contact.ninjatpl b/templates/art-deco/contact.ninjatpl new file mode 100644 index 0000000..d8aa1e7 --- /dev/null +++ b/templates/art-deco/contact.ninjatpl @@ -0,0 +1,22 @@ + + +{{ 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/overrides/art-deco/announcement-bar.ninjatpl b/templates/overrides/art-deco/announcement-bar.ninjatpl new file mode 100644 index 0000000..2dfdf04 --- /dev/null +++ b/templates/overrides/art-deco/announcement-bar.ninjatpl @@ -0,0 +1,21 @@ +{# announcement-bar override — gilded banner. Dismiss {% endif %} diff --git a/templates/overrides/art-deco/audio-embed.ninjatpl b/templates/overrides/art-deco/audio-embed.ninjatpl new file mode 100644 index 0000000..b477e08 --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/auth-form.ninjatpl b/templates/overrides/art-deco/auth-form.ninjatpl new file mode 100644 index 0000000..8cc50ad --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/auth-status.ninjatpl b/templates/overrides/art-deco/auth-status.ninjatpl new file mode 100644 index 0000000..9e197b5 --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/author-bio-hero.ninjatpl b/templates/overrides/art-deco/author-bio-hero.ninjatpl new file mode 100644 index 0000000..2798eae --- /dev/null +++ b/templates/overrides/art-deco/author-bio-hero.ninjatpl @@ -0,0 +1,22 @@ +{# author-bio-hero (art-deco) — 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/art-deco/author-bio.ninjatpl b/templates/overrides/art-deco/author-bio.ninjatpl new file mode 100644 index 0000000..122a425 --- /dev/null +++ b/templates/overrides/art-deco/author-bio.ninjatpl @@ -0,0 +1,14 @@ +{# author-bio (art-deco) — byline card sourced from the `post` provider's author_* fields. #} +{% if post.author_name %} + +{% endif %} diff --git a/templates/overrides/art-deco/blog-hero.ninjatpl b/templates/overrides/art-deco/blog-hero.ninjatpl new file mode 100644 index 0000000..1e2f875 --- /dev/null +++ b/templates/overrides/art-deco/blog-hero.ninjatpl @@ -0,0 +1,24 @@ +{# blog-hero (art-deco) — 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/art-deco/breadcrumbs.ninjatpl b/templates/overrides/art-deco/breadcrumbs.ninjatpl new file mode 100644 index 0000000..9542b35 --- /dev/null +++ b/templates/overrides/art-deco/breadcrumbs.ninjatpl @@ -0,0 +1,15 @@ +{# breadcrumbs (art-deco) — 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/art-deco/button.ninjatpl b/templates/overrides/art-deco/button.ninjatpl index 253b950..a5743a9 100644 --- a/templates/overrides/art-deco/button.ninjatpl +++ b/templates/overrides/art-deco/button.ninjatpl @@ -1 +1,10 @@ -{% if label %}{% if url %}{{ label }}{% else %}{% endif %}{% endif %} +{# 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/art-deco/card.ninjatpl b/templates/overrides/art-deco/card.ninjatpl new file mode 100644 index 0000000..38d247f --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/category-list.ninjatpl b/templates/overrides/art-deco/category-list.ninjatpl new file mode 100644 index 0000000..74443d3 --- /dev/null +++ b/templates/overrides/art-deco/category-list.ninjatpl @@ -0,0 +1,18 @@ +{# category-list (art-deco) — 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/art-deco/contact-card.ninjatpl b/templates/overrides/art-deco/contact-card.ninjatpl new file mode 100644 index 0000000..82831f8 --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/contact-form.ninjatpl b/templates/overrides/art-deco/contact-form.ninjatpl new file mode 100644 index 0000000..a193ac6 --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/countdown.ninjatpl b/templates/overrides/art-deco/countdown.ninjatpl new file mode 100644 index 0000000..3727214 --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/cta.ninjatpl b/templates/overrides/art-deco/cta.ninjatpl new file mode 100644 index 0000000..8ad2c4a --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/divider.ninjatpl b/templates/overrides/art-deco/divider.ninjatpl new file mode 100644 index 0000000..014bce5 --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/event-list.ninjatpl b/templates/overrides/art-deco/event-list.ninjatpl new file mode 100644 index 0000000..e0286d6 --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/faq.ninjatpl b/templates/overrides/art-deco/faq.ninjatpl new file mode 100644 index 0000000..403888d --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/feature-grid.ninjatpl b/templates/overrides/art-deco/feature-grid.ninjatpl new file mode 100644 index 0000000..e9d842c --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/featured-posts.ninjatpl b/templates/overrides/art-deco/featured-posts.ninjatpl new file mode 100644 index 0000000..e35f0a3 --- /dev/null +++ b/templates/overrides/art-deco/featured-posts.ninjatpl @@ -0,0 +1,24 @@ +{# featured-posts (art-deco) — 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/art-deco/footer.ninjatpl b/templates/overrides/art-deco/footer.ninjatpl new file mode 100644 index 0000000..c41181f --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/gallery.ninjatpl b/templates/overrides/art-deco/gallery.ninjatpl new file mode 100644 index 0000000..386d3cd --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/heading.ninjatpl b/templates/overrides/art-deco/heading.ninjatpl index 4573ef2..7232b25 100644 --- a/templates/overrides/art-deco/heading.ninjatpl +++ b/templates/overrides/art-deco/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 %} +{# heading override — gilded Cinzel 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/art-deco/hero.ninjatpl b/templates/overrides/art-deco/hero.ninjatpl new file mode 100644 index 0000000..b3259ca --- /dev/null +++ b/templates/overrides/art-deco/hero.ninjatpl @@ -0,0 +1,40 @@ +{# hero override — folds in the old marquee_hero: sunburst overlay + stepped ziggurat framing, gold-foil Cinzel 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/art-deco/hours-location.ninjatpl b/templates/overrides/art-deco/hours-location.ninjatpl new file mode 100644 index 0000000..23980c4 --- /dev/null +++ b/templates/overrides/art-deco/hours-location.ninjatpl @@ -0,0 +1,31 @@ +{# hours-location override — gilded hours ledger + framed map. Map facade + + {% endif %} + + + diff --git a/templates/overrides/art-deco/image.ninjatpl b/templates/overrides/art-deco/image.ninjatpl index 4230174..72b0f19 100644 --- a/templates/overrides/art-deco/image.ninjatpl +++ b/templates/overrides/art-deco/image.ninjatpl @@ -1 +1,11 @@ -
{{ alt }}
{% if caption %}
{{ caption }}
{% endif %}
+{# 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/art-deco/logo-strip.ninjatpl b/templates/overrides/art-deco/logo-strip.ninjatpl new file mode 100644 index 0000000..fe0a72b --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/menu-list.ninjatpl b/templates/overrides/art-deco/menu-list.ninjatpl new file mode 100644 index 0000000..b9c5460 --- /dev/null +++ b/templates/overrides/art-deco/menu-list.ninjatpl @@ -0,0 +1,30 @@ +{# menu-list override — the supper-club menu card: centred Cinzel 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/art-deco/navbar.ninjatpl b/templates/overrides/art-deco/navbar.ninjatpl new file mode 100644 index 0000000..dfb8d02 --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/newsletter-signup.ninjatpl b/templates/overrides/art-deco/newsletter-signup.ninjatpl new file mode 100644 index 0000000..769a86e --- /dev/null +++ b/templates/overrides/art-deco/newsletter-signup.ninjatpl @@ -0,0 +1,13 @@ +{# newsletter-signup (art-deco) — the {% signup_form %} tag carries the #} +{# untouchable subscribe wiring (/api/lists/subscribe HTMX post + honeypot); #} +{# this template supplies only the art-deco 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/art-deco/page-suggestions.ninjatpl b/templates/overrides/art-deco/page-suggestions.ninjatpl new file mode 100644 index 0000000..639a05b --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/password-reset-form.ninjatpl b/templates/overrides/art-deco/password-reset-form.ninjatpl new file mode 100644 index 0000000..5611bfb --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/popular-posts.ninjatpl b/templates/overrides/art-deco/popular-posts.ninjatpl new file mode 100644 index 0000000..f1a86ff --- /dev/null +++ b/templates/overrides/art-deco/popular-posts.ninjatpl @@ -0,0 +1,16 @@ +{# popular-posts (art-deco) — 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/art-deco/post-hero.ninjatpl b/templates/overrides/art-deco/post-hero.ninjatpl new file mode 100644 index 0000000..c9e45ba --- /dev/null +++ b/templates/overrides/art-deco/post-hero.ninjatpl @@ -0,0 +1,23 @@ +{# post-hero (art-deco) — 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/art-deco/post-metadata.ninjatpl b/templates/overrides/art-deco/post-metadata.ninjatpl new file mode 100644 index 0000000..80b7c8d --- /dev/null +++ b/templates/overrides/art-deco/post-metadata.ninjatpl @@ -0,0 +1,9 @@ +{# post-metadata (art-deco) — 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/art-deco/pricing.ninjatpl b/templates/overrides/art-deco/pricing.ninjatpl new file mode 100644 index 0000000..7cbff79 --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/quote.ninjatpl b/templates/overrides/art-deco/quote.ninjatpl new file mode 100644 index 0000000..0eb2ea3 --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/related-posts.ninjatpl b/templates/overrides/art-deco/related-posts.ninjatpl new file mode 100644 index 0000000..c73465a --- /dev/null +++ b/templates/overrides/art-deco/related-posts.ninjatpl @@ -0,0 +1,17 @@ +{# related-posts (art-deco) — grid from the `posts` provider. #} +{% if posts %} +
+
+

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

+
+{% for post in posts %} + +{% endfor %} +
+
+
+{% endif %} diff --git a/templates/overrides/art-deco/rich-section.ninjatpl b/templates/overrides/art-deco/rich-section.ninjatpl new file mode 100644 index 0000000..b071fa1 --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/rich-text.ninjatpl b/templates/overrides/art-deco/rich-text.ninjatpl new file mode 100644 index 0000000..2ac8298 --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/sidebar-nav.ninjatpl b/templates/overrides/art-deco/sidebar-nav.ninjatpl new file mode 100644 index 0000000..3511d61 --- /dev/null +++ b/templates/overrides/art-deco/sidebar-nav.ninjatpl @@ -0,0 +1,15 @@ +{# sidebar-nav (art-deco) — 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/art-deco/social-links.ninjatpl b/templates/overrides/art-deco/social-links.ninjatpl new file mode 100644 index 0000000..87dc729 --- /dev/null +++ b/templates/overrides/art-deco/social-links.ninjatpl @@ -0,0 +1,13 @@ + diff --git a/templates/overrides/art-deco/stats.ninjatpl b/templates/overrides/art-deco/stats.ninjatpl new file mode 100644 index 0000000..3c1ae15 --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/team.ninjatpl b/templates/overrides/art-deco/team.ninjatpl new file mode 100644 index 0000000..a48e305 --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/testimonial.ninjatpl b/templates/overrides/art-deco/testimonial.ninjatpl new file mode 100644 index 0000000..75993a8 --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/text.ninjatpl b/templates/overrides/art-deco/text.ninjatpl index 73aa570..567b19d 100644 --- a/templates/overrides/art-deco/text.ninjatpl +++ b/templates/overrides/art-deco/text.ninjatpl @@ -1 +1,2 @@ -
{{ text|safe }}
+{# 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/art-deco/timeline.ninjatpl b/templates/overrides/art-deco/timeline.ninjatpl new file mode 100644 index 0000000..8e605fd --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/utility-bar.ninjatpl b/templates/overrides/art-deco/utility-bar.ninjatpl new file mode 100644 index 0000000..5487689 --- /dev/null +++ b/templates/overrides/art-deco/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/art-deco/video-embed.ninjatpl b/templates/overrides/art-deco/video-embed.ninjatpl new file mode 100644 index 0000000..e0cadb1 --- /dev/null +++ b/templates/overrides/art-deco/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 %}