) — 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 %}
{{ menuName }} {% endif %}
- {% if showSocial != "false" %}
Instagram · Facebook · LinkedIn
{% 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 %} {% 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 %}. #}
+
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 }}
+
+
+
+ {{ 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 %}{% endif %}
+
+
+ {% if title %}
+
+
{{ title }}
+ {% if page_meta.MetaDescription %}
{{ page_meta.MetaDescription }}
{% endif %}
+
+
+ {% endif %}
+ {% if slots.featured %}
{{ slots.featured|safe }} {% endif %}
+ {% if slots.main %}
{% else %}
{% endif %}
+
+
+ {% if slots.footer %}{% 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 %}{% 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 %}{% 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 %}
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 %}
+
+{% 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 %}
+
+
+ {{ context.publicDisplayName|default:context.publicUsername|first|upper }}
+ {{ context.publicDisplayName|default:context.publicUsername }}
+
+
+
+
+
+{% 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 %}
+
+{% if heading %}{{ heading }}
{% endif %}
+
+{% if showAvatar and post.author_avatar %}
{% img post.author_avatar alt=post.author_name class="h-full w-full object-cover" %}
{% endif %}
+
+
{% if post.author_slug %}{{ post.author_name }} {% else %}{{ post.author_name }}{% endif %}
+
+{% if post.author_bio %}
{{ post.author_bio }}
{% endif %}
+
+
+
+{% 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 %}
+
+{% 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" %}
+
+
+{% if showHome %}{{ homeLabel|default:"Home" }} {% endif %}
+{% for item in breadcrumbs %}
+
+{% if showHome or not forloop.First %}{% if arrows %} {% else %}{{ separator|default:"/" }} {% endif %}{% endif %}
+{% if item.is_current %}{% if showCurrent %}{{ item.label }} {% endif %}{% else %}{{ item.label }} {% endif %}
+
+{% endfor %}
+
+
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 %}{{ label }} {% 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" %}
+{% if link %}{% endif %}{% if media %}{% if layout == "horizontal" %}{% img media alt=title|default:"" class="h-full w-full object-cover" %}{% else %}{% img media alt=title|default:"" class="aspect-video h-full w-full object-cover" %}{% endif %}
{% endif %}{% if title %}
{{ title }} {% endif %}{% if text %}
{{ text|safe }}
{% endif %}{% if linkLabel and link %}
{{ linkLabel }}::lucide:arrow-right:sm:: {% endif %}
{% if link %} {% endif %}
+{% 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 %}
+
+{% 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 %}{{ field.label }}{% if field.required %} * {% endif %} {% endif %}
+ {% if field.type == "textarea" %}
+
+ {% elif field.type == "select" %}
+
+ {% if field.placeholder %}{{ field.placeholder }} {% endif %}
+ {% for opt in field.options %}{{ opt.label|default:opt.value }} {% endfor %}
+
+ {% else %}
+
+ {% endif %}
+
+ {% endfor %}
+
Leave this field empty
+
+ {{ submitLabel|default:"Request a table" }}
+
+ {% 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" }}
+
+
+
{{ expiredMessage|default:"This has started." }}
+ {% if expiredUrl and expiredLabel %}
{{ expiredLabel }} {% endif %}
+
+
+
Counting down to {{ target }} .
+
+
+
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 %}
+
+
+
+
+
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" %}
+
+
+
::{{ icon|default:"lucide:diamond" }}:sm::
+
+{% 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 %}
{% 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 %}
+
+
+
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 %}
{% for l in legal_items %}{{ l.label }} {% endfor %} {% endif %}
{% endmacro %}
+
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 %}{% endif %}
+{% if trustLogos %}{% endif %}
+{% endmacro %}
+{% if variant == "split" %}
+
+{% if hasImage %}{% img backgroundImage alt=headlineText class="absolute inset-0 h-full w-full object-cover" layout="hero" %}{% endif %}
+
+
+{% 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 %}
+
+
+
+{% 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 preserved verbatim. Fields mirror builtin: heading, hours[].day/.hours/.closed, specialHoursNote, address, phone, directionsUrl, mapEmbedUrl, mapButtonLabel. #}
+
+
+ {% if heading %}
{{ heading }} {% endif %}
+
+
+ {% if hours %}
+
+
+ {% for row in hours %}
+
+ {{ row.day }}
+ {% if row.closed %}Closed{% else %}{{ row.hours }}{% endif %}
+
+ {% endfor %}
+
+
+ {% endif %}
+ {% if specialHoursNote %}
{{ specialHoursNote }}
{% endif %}
+ {% if address %}
{% endif %}
+ {% if phone %}
{% endif %}
+ {% if directionsUrl %}
::lucide:navigation:sm:: Get directions {% endif %}
+
+ {% if mapEmbedUrl %}
+
+ ::lucide:map-pin:md:: {{ mapButtonLabel|default:"Load map" }}
+
+ {% 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 @@
-{% 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 %}
{% endif %}
+
+
+
+{% 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) %}
+
{{ item.label }}
+{% if use_mega %}
{% for child in item.children %}
{% endfor %}
+{% else %}
{% for child in item.children %}{% if child.item_type == "header" %}
{{ child.label }}
{% else %}
{{ child.label }} {% endif %}{% endfor %}
{% endif %}
+
{% endmacro %}
+{% macro brand(size) %}{% if logoType == "text" and logoText.text %}{{ logoText.text }} {% elif logo %} {% elif companyName %}{{ companyName }} {% endif %} {% endmacro %}
+{% macro ctabtn(extra) %}{% if cta_text %}{% endif %}{% endmacro %}
+
+{% if showUtilityBar %}{{ utilityBarText|safe }}
{% endif %}
+
+
+{% if nv == "centered-logo" %}
+
{% for item in menu.items %}{% if item.children %}{{ dropdown(item) }}{% else %}{{ navlink(item) }}{% endif %}{% endfor %}
+
{{ brand("h-9") }}
+
{{ ctabtn("") }}
+{% elif nv == "split" %}
+
{% for item in menu.items %}{% if forloop.Counter0 * 2 < menu.items|length %}{% if item.children %}{{ dropdown(item) }}{% else %}{{ navlink(item) }}{% endif %}{% endif %}{% endfor %}
+
{{ brand("h-9") }}
+
{% for item in menu.items %}{% if forloop.Counter0 * 2 >= menu.items|length %}{% if item.children %}{{ dropdown(item) }}{% else %}{{ navlink(item) }}{% endif %}{% endif %}{% endfor %}{{ ctabtn("ml-3") }}
+{% else %}
+
{{ brand("h-8") }}
+
{% for item in menu.items %}{% if item.children %}{{ dropdown(item) }}{% else %}{{ navlink(item) }}{% endif %}{% endfor %}{{ ctabtn("ml-3") }}
+{% endif %}
+
+
+
+{# --- Mobile off-canvas drawer: pure-CSS checkbox baseline (works with no JS) --- #}
+
+
+
+
{{ brand("h-8") }}
Close menu
+
{% for item in menu.items %}{% if item.children %}{{ item.label }} {% for child in item.children %}{% if child.item_type != "header" %}
{{ child.label }} {% endif %}{% endfor %}
{% else %}{{ item.label }} {% endif %}{% endfor %}
+{% if cta_text %}
{% endif %}
+
+
+
+
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 %}
+
+{% 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. #}
+
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 %}
+
+{% 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 %}
+
+
+{% if post.published_at %}
{{ post.published_at|date:"M j, Y" }}
{% endif %}
+
+
+{% 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 %}
{{ post.published_at|date:"F j, Y" }} {% 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" }}
+
+
+
+{% 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 @@
+
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 title %}{{ title }} {% endif %}
+
+
+{% 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 @@
+
+ {% if heading %}
{{ heading }}
{% endif %}
+
+
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 %}
+
+
+ {% 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 %}
+
+
+ {% 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 poster %}{% img poster alt=title|default:"" class="absolute inset-0 h-full w-full object-cover" %}{% endif %}::lucide:play:xl::
{% if caption %}{{ caption }} {% endif %} {% endwith %}