feat(magazine-bold): Wave A theme lane (WO-TF-021) — streetwear / fashion drops
Full Wave A build to the OVERRIDE-CONTRACT (frozen cms a0f07e2ae): - 49 mandatory builtin overrides in templates/overrides/magazine-bold/, re-skinned to streetwear cover-story chrome (massive condensed uppercase Archivo Black headlines, heavy 2px rules, hard square edges, mono datelines, fluoro accent slabs). Every content field, provider var, custom tag and JS block preserved verbatim from the frozen builtin defaults; legacy button (link/style/label) and card (media/text) field reads already reconciled. - 7 page templates: default, full-width, landing, article, blog-index, contact, auth. - 4 persona blocks (deleted masthead/photo_essay/pull_quote/issue_archive/ colophon — now overrides of navbar/gallery/quote/post-index/footer): cover_story, lookbook, drop_calendar, credits_ticker. - 5 dual-mode presets (newsprint+voltage light / ink-black+fluoro dark), all 19 tokens, dark tuned not inverted. - Bundled OFL woff2: Archivo Black (display), Inter (body), IBM Plex Mono (labels); fonts.json + assets/fonts/LICENSES.md. All font-family via var(--font-heading|body|mono, fallback). - Themed email-safe wrapper (tables, inline styles, streetwear defaults). - Master pages (navbar/footer/slot chrome) + seed demo site (OFFCUT label: home, about, blog + 3 posts, contact with data table + row_inserted → email-admins workflow, login) in fashion-label voice. - MID motion tier: marquee ticker, scroll reveal, duotone hover, scroll progress bar — all prefers-reduced-motion honored with static fallback, no canvas. - Icon packs (lucide, simple-icons) declared in plugin.mod + RECOMMENDED_ICONS. Verified: `make build` exit 0; `check-safety` 32 checks 19 ok 13 skip -> OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
989f1fb515
commit
98293cf8ab
@ -1,56 +1,44 @@
|
|||||||
# Magazine Bold — Recommended fonts
|
# Magazine Bold — fonts
|
||||||
|
|
||||||
Per `themes/docs/FONTS.md` (wave-1 policy), this theme ships `fonts.json = []`
|
This theme **bundles** a Latin subset of three open-licence (SIL OFL 1.1)
|
||||||
and provides CSS fallback stacks via `--font-heading`, `--font-body`,
|
families, so it looks correct the moment it is activated — no admin setup
|
||||||
`--font-mono`. The site admin assigns real fonts via the typography settings
|
required. The bundled faces are declared in `fonts.json` and land in the
|
||||||
panel; the recommendations below match the spec §5 intent.
|
typography picker as `source=template` rows.
|
||||||
|
|
||||||
## Display (heading slot)
|
| Slot | Bundled family | Weights / styles | Role |
|
||||||
|
|---------|----------------|------------------------|---------------------------------------------------------------|
|
||||||
|
| Heading | Archivo Black | 400 normal | Massive condensed uppercase cover headlines and the wordmark |
|
||||||
|
| Body | Inter | 400 / 500 / 700 normal | Body copy, decks and UI text |
|
||||||
|
| Mono | IBM Plex Mono | 400 / 500 normal | Kickers, datelines, folio numbers, captions and button labels |
|
||||||
|
|
||||||
Spec calls for **PP Editorial New** (Regular Italic + Ultralight) at 180–280pt
|
The bundled woff2 files and their licence record live in `assets/fonts/`
|
||||||
for cover-story hits, with **Migra** as a secondary serif drop-in. Both are
|
(`LICENSES.md`). Every `font-family` in the theme is routed through
|
||||||
commercial; the open-source picks below render the same italic-display energy.
|
`var(--font-heading, ...)`, `var(--font-body, ...)` and `var(--font-mono, ...)`
|
||||||
|
with a fallback stack, so the layout is usable before any font is assigned and
|
||||||
|
the admin font picker stays in full control.
|
||||||
|
|
||||||
Open-source picks (assign one to **Heading**):
|
## Assigning the bundled faces
|
||||||
|
|
||||||
- `google:Playfair Display` — the closest open-source approximation of
|
1. In the BlockNinja admin, open **Settings → Theme → Typography**.
|
||||||
PP Editorial New's contrast and italic axis. Open the typography panel,
|
2. In the **Bundled** tab, assign **Archivo Black** to the *Heading* slot.
|
||||||
pick "Playfair Display" from the Google Fonts tab, assign to Heading.
|
3. Assign **Inter** to the *Body* slot.
|
||||||
- `google:Fraunces` — a more contemporary alternative with optical sizing;
|
4. Assign **IBM Plex Mono** to the *Mono* slot.
|
||||||
works well as the secondary serif (`font-serif-sub`).
|
|
||||||
- `google:Cormorant Garamond` — slightly higher-contrast classic serif if
|
|
||||||
you want a more traditional broadsheet feel.
|
|
||||||
|
|
||||||
Commercial picks (require admin licensing + upload):
|
## Optional swaps (Google Fonts tab)
|
||||||
|
|
||||||
- `upload:PP Editorial New` — purchase from pangrampangram.com, upload via the
|
The identity is condensed-heavy display + neutral sans body + mono details.
|
||||||
font picker's "Upload" tab.
|
Any of these drop in via the CSS variables with no template change:
|
||||||
- `upload:Migra` — purchase from pangrampangram.com (Migra is a sibling
|
|
||||||
family), upload via the picker.
|
|
||||||
|
|
||||||
## Body
|
- **Anton** (`google:Anton`) — a taller, more condensed display face for the
|
||||||
|
*Heading* slot; the loudest cover-headline option and the WO's alternate.
|
||||||
|
- **Archivo** (`google:Archivo`) — for the *Body* slot if you want the body to
|
||||||
|
echo the display family.
|
||||||
|
- **Space Grotesk** (`google:Space Grotesk`) — an alternative body / deck face.
|
||||||
|
- **Space Mono** (`google:Space Mono`) — a chunkier mono for the *Mono* slot.
|
||||||
|
|
||||||
Spec calls for **Inter** 16–18px with tight leading and generous tracking on
|
## Bundling a commercial display face (out of scope for Wave A)
|
||||||
small-caps kickers. Inter is already in the curated Google Fonts list:
|
|
||||||
|
|
||||||
- `google:Inter` — open the typography panel, pick "Inter" from the Google
|
If the brand locks in a licensed display face in place of Archivo Black, licence
|
||||||
Fonts tab, assign to Body. Aim for weights 400 + 600.
|
and subset the woff2 set, drop it in `assets/fonts/`, and add the family +
|
||||||
|
variants to `fonts.json`. The CSS does not change — the same `var(--font-heading)`
|
||||||
## Mono (captions, folios, metadata only)
|
consumer picks up whichever face an admin assigns.
|
||||||
|
|
||||||
Spec calls for **JetBrains Mono** at caption sizes for kickers, folios and
|
|
||||||
the colophon strip. Curated:
|
|
||||||
|
|
||||||
- `google:JetBrains Mono` — open the typography panel, pick "JetBrains Mono"
|
|
||||||
from the Google Fonts tab, assign to Mono. Weight 400 is enough.
|
|
||||||
|
|
||||||
Alternates: `google:Space Mono`, `google:IBM Plex Mono`.
|
|
||||||
|
|
||||||
## How the theme degrades when nothing is picked
|
|
||||||
|
|
||||||
The compiled CSS shipped with this `.so` uses the variables with sensible
|
|
||||||
fallbacks so a fresh install still reads like an editorial magazine:
|
|
||||||
|
|
||||||
- `--font-heading` falls back to `"Playfair Display", "Cormorant Garamond", Georgia, "Times New Roman", serif`
|
|
||||||
- `--font-body` falls back to `"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif`
|
|
||||||
- `--font-mono` falls back to `"JetBrains Mono", "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace`
|
|
||||||
|
|||||||
30
RECOMMENDED_ICONS.md
Normal file
30
RECOMMENDED_ICONS.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Recommended Icons — Magazine Bold
|
||||||
|
|
||||||
|
Magazine Bold is a typographic, rule-heavy theme. It leans on condensed
|
||||||
|
headlines, mono labels and heavy rules, not iconography, so it references only
|
||||||
|
two bundled packs. `plugin.mod` declares them in `required_icon_packs`; until
|
||||||
|
the loader auto-installs declared packs, an admin should confirm they are
|
||||||
|
enabled before activation.
|
||||||
|
|
||||||
|
## Required packs
|
||||||
|
|
||||||
|
- **lucide** — ships bundled with the CMS (v0.460). Enabled by default. Used
|
||||||
|
for chrome and UI glyphs (arrows, chevrons, close, check, play, map-pin,
|
||||||
|
navigation, mail, rss, star, flag). If icons render as empty boxes, open
|
||||||
|
`/admin/icons` and confirm **Lucide** is installed and enabled.
|
||||||
|
- **simple-icons** — ships bundled with the CMS (v14.0). Used only for brand
|
||||||
|
glyphs in `author-bio-hero`, `author-bio` and `social-links` (x, linkedin,
|
||||||
|
github). Confirm it is enabled at `/admin/icons` if brand marks are blank.
|
||||||
|
|
||||||
|
## Icons used by the theme chrome and overrides
|
||||||
|
|
||||||
|
All via the sprite pipeline (`<svg><use href="/icons/<pack>.svg#<name>"/></svg>`
|
||||||
|
or the `::<pack>:<name>::` shorthand):
|
||||||
|
|
||||||
|
`arrow-right`, `chevron-down`, `chevron-left`, `chevron-right`, `x`, `check`,
|
||||||
|
`play`, `download`, `map-pin`, `navigation`, `mail`, `rss`, `star`, `flag`,
|
||||||
|
`diamond`, `globe` (lucide); `x`, `linkedin`, `github` (simple-icons).
|
||||||
|
|
||||||
|
Block content that stores an icon (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.
|
||||||
17
assets/fonts/LICENSES.md
Normal file
17
assets/fonts/LICENSES.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Bundled font licenses
|
||||||
|
|
||||||
|
All bundled woff2 files in this directory are latin-subset builds redistributed
|
||||||
|
under the SIL Open Font License 1.1 (OFL). No modifications were made beyond
|
||||||
|
subsetting to the latin range.
|
||||||
|
|
||||||
|
| Family | Files | License | Source |
|
||||||
|
|--------|-------|---------|--------|
|
||||||
|
| Archivo Black | archivo-black-400.woff2 | SIL OFL 1.1 | Omnibus-Type / Google Fonts |
|
||||||
|
| Inter | inter-400.woff2, inter-500.woff2, inter-700.woff2 | SIL OFL 1.1 | Rasmus Andersson / Google Fonts |
|
||||||
|
| IBM Plex Mono | ibm-plex-mono-400.woff2, ibm-plex-mono-500.woff2 | SIL OFL 1.1 | IBM / Google Fonts |
|
||||||
|
|
||||||
|
Full OFL text: https://openfontlicense.org/. Font-family is still admin-controlled;
|
||||||
|
these bundled families appear as `source=template` rows in the fonts picker and
|
||||||
|
are consumed only via `var(--font-heading|body|mono, <fallback>)`. Archivo Black
|
||||||
|
drives the condensed display headlines, Inter the body, IBM Plex Mono the
|
||||||
|
kickers, datelines and captions.
|
||||||
BIN
assets/fonts/archivo-black-400.woff2
Normal file
BIN
assets/fonts/archivo-black-400.woff2
Normal file
Binary file not shown.
BIN
assets/fonts/ibm-plex-mono-400.woff2
Normal file
BIN
assets/fonts/ibm-plex-mono-400.woff2
Normal file
Binary file not shown.
BIN
assets/fonts/ibm-plex-mono-500.woff2
Normal file
BIN
assets/fonts/ibm-plex-mono-500.woff2
Normal file
Binary file not shown.
BIN
assets/fonts/inter-400.woff2
Normal file
BIN
assets/fonts/inter-400.woff2
Normal file
Binary file not shown.
BIN
assets/fonts/inter-500.woff2
Normal file
BIN
assets/fonts/inter-500.woff2
Normal file
Binary file not shown.
BIN
assets/fonts/inter-700.woff2
Normal file
BIN
assets/fonts/inter-700.woff2
Normal file
Binary file not shown.
@ -1,42 +1,34 @@
|
|||||||
# Magazine Bold theme block definitions (codeless .bnp, WO-WZ-021).
|
# Magazine Bold theme block definitions (codeless .bnp, WO-TF-021).
|
||||||
# Keys are fully qualified (magazine-bold:<key>) because the codeless loader
|
#
|
||||||
# registers definitions verbatim — there is no PluginBlockRegistry prefixing
|
# These are the theme's TRUE persona blocks — streetwear / fashion-drop
|
||||||
# in the codeless path (unlike the wasm/templ path). The qualified keys match
|
# furniture with no builtin equivalent. Everything the theme used to ship that
|
||||||
# the hardcoded data-block attributes and the master_pages.json references.
|
# duplicated a builtin (masthead, photo essay, pull quote, issue archive,
|
||||||
|
# colophon) is now a re-skin OVERRIDE of a builtin (navbar, gallery, quote,
|
||||||
|
# post-index, footer) under templates/overrides/magazine-bold/. Keys are fully
|
||||||
|
# qualified (magazine-bold:<key>) because the codeless loader registers
|
||||||
|
# definitions verbatim.
|
||||||
blocks:
|
blocks:
|
||||||
- key: magazine-bold:masthead
|
|
||||||
title: Issue Masthead
|
|
||||||
description: Wordmark, issue number and tagline strip — sits at the top of every page.
|
|
||||||
category: layout
|
|
||||||
schema: masthead.schema.json
|
|
||||||
template: masthead.ninjatpl
|
|
||||||
- key: magazine-bold:cover_story
|
- key: magazine-bold:cover_story
|
||||||
title: Cover Story
|
title: Cover Story
|
||||||
description: 12-column asymmetric hero with kicker, oversized display headline, deck and cover image.
|
description: Asymmetric magazine cover — mono folio kicker, massive condensed headline, deck and a duotone cover image with a shop/read CTA.
|
||||||
category: content
|
category: theme
|
||||||
schema: cover_story.schema.json
|
schema: cover_story.schema.json
|
||||||
template: cover_story.ninjatpl
|
template: cover_story.ninjatpl
|
||||||
- key: magazine-bold:photo_essay
|
- key: magazine-bold:lookbook
|
||||||
title: Photo Essay
|
title: Lookbook
|
||||||
description: Numbered photo-essay with asymmetric frame spans (half / full / tall) and mono captions.
|
description: Grid of duotone look frames with mono numbering, captions and optional shop links — the drop's lookbook spread.
|
||||||
category: content
|
category: theme
|
||||||
schema: photo_essay.schema.json
|
schema: lookbook.schema.json
|
||||||
template: photo_essay.ninjatpl
|
template: lookbook.ninjatpl
|
||||||
- key: magazine-bold:pull_quote
|
- key: magazine-bold:drop_calendar
|
||||||
title: Pull Quote
|
title: Drop Calendar
|
||||||
description: Color-block pull quote with rotating accent (pink / blue / lime).
|
description: Dated list of upcoming and past drops — mono datelines, condensed names and a live/soon/sold status tag.
|
||||||
category: content
|
category: theme
|
||||||
schema: pull_quote.schema.json
|
schema: drop_calendar.schema.json
|
||||||
template: pull_quote.ninjatpl
|
template: drop_calendar.ninjatpl
|
||||||
- key: magazine-bold:issue_archive
|
- key: magazine-bold:credits_ticker
|
||||||
title: Issue Archive
|
title: Credits Ticker
|
||||||
description: Numbered list of back-issues driven by a bucket query.
|
description: Marquee strip of credits (photographer, stylist, cast) — MID-motion scroll, paused on hover, static under prefers-reduced-motion.
|
||||||
category: navigation
|
category: theme
|
||||||
schema: issue_archive.schema.json
|
schema: credits_ticker.schema.json
|
||||||
template: issue_archive.ninjatpl
|
template: credits_ticker.ninjatpl
|
||||||
- key: magazine-bold:colophon
|
|
||||||
title: Colophon
|
|
||||||
description: Mono caption strip with credits and an optional social row.
|
|
||||||
category: layout
|
|
||||||
schema: colophon.schema.json
|
|
||||||
template: colophon.ninjatpl
|
|
||||||
|
|||||||
@ -1,4 +0,0 @@
|
|||||||
<div data-block="magazine-bold:colophon" class="py-8 font-mono mb-caption">
|
|
||||||
{% if credits %}<div class="mb-4">{{ credits|safe }}</div>{% endif %}
|
|
||||||
{% if showSocial == "true" or showSocial == true %}<ul class="flex items-center gap-6 mb-hairline-top pt-4 mt-4"><li><a href="#" class="hover:text-foreground">Instagram</a></li><li><a href="#" class="hover:text-foreground">Twitter</a></li><li><a href="#" class="hover:text-foreground">RSS</a></li></ul>{% endif %}
|
|
||||||
</div>
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
||||||
"title": "Colophon",
|
|
||||||
"description": "Mono caption strip with credits and optional social row.",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"credits": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Credits",
|
|
||||||
"description": "Mono-set credits block (rich text).",
|
|
||||||
"x-editor": "richtext"
|
|
||||||
},
|
|
||||||
"showSocial": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Show Social Row",
|
|
||||||
"description": "Toggle the social row beneath the credits.",
|
|
||||||
"enum": ["true", "false"],
|
|
||||||
"default": "false",
|
|
||||||
"x-editor": "select"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +1,23 @@
|
|||||||
<section data-block="magazine-bold:cover_story" class="w-full py-12">
|
{# cover_story (magazine-bold) — asymmetric cover: mono folio kicker, massive condensed headline, deck, duotone cover image, shop/read CTA. Fields: kicker, headline (richtext), deck (richtext), image (media), link, linkLabel. Image via {% img %} (media filter not registered in block render path). MID motion: mb-reveal + duotone hover, reduced-motion safe. #}
|
||||||
<div class="max-w-6xl mx-auto px-6">
|
<section data-block="magazine-bold:cover_story" class="w-full py-12 md:py-16">
|
||||||
<div class="mb-grid-asym-cover">
|
<div class="mx-auto max-w-6xl px-4 sm:px-6">
|
||||||
<div class="mb-col-folio">{% if kicker %}<div class="font-mono text-kicker text-muted-foreground mb-4">{{ kicker }}</div>{% endif %}</div>
|
<div class="grid grid-cols-1 gap-6 lg:grid-cols-12 lg:items-end">
|
||||||
<div class="mb-col-headline"><h1 class="font-display text-folio text-foreground">{% if headline %}{{ headline|safe }}{% else %}Cover story{% endif %}</h1></div>
|
<div class="lg:col-span-2">{% if kicker %}<div class="mb-caps text-muted-foreground">{{ kicker }}</div>{% endif %}</div>
|
||||||
<div class="mb-col-deck">{% if deck %}<div class="font-sans text-deck text-muted-foreground">{{ deck|safe }}</div>{% endif %}</div>
|
<div class="lg:col-span-7"><h1 class="text-folio text-foreground">{% if headline %}{{ headline|safe }}{% else %}Cover story{% endif %}</h1></div>
|
||||||
|
<div class="lg:col-span-3">{% if deck %}<div class="text-deck text-muted-foreground">{{ deck|safe }}</div>{% endif %}</div>
|
||||||
</div>
|
</div>
|
||||||
{% if image %}<div class="mt-10"><img src="{{ image }}" alt="" class="w-full h-auto block" loading="lazy"></div>{% endif %}
|
{% if image %}<div class="mb-reveal mb-duotone mt-10 aspect-[16/9] w-full overflow-hidden">{% img image alt="" class="h-full w-full object-cover" layout="hero" %}</div>{% endif %}
|
||||||
{% if link %}<div class="mt-6"><a href="{{ link }}" class="mb-button">Read the story →</a></div>{% endif %}
|
{% if link %}<div class="mt-6"><a href="{{ link }}" class="mb-button">{{ linkLabel|default:"Read the story" }}</a></div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
if(window.__mbReveal)return;window.__mbReveal=1;
|
||||||
|
document.documentElement.classList.remove('no-js');
|
||||||
|
var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
|
var els=document.querySelectorAll('.mb-reveal');
|
||||||
|
if(reduce||!('IntersectionObserver' in window)){els.forEach(function(el){el.classList.add('mb-in');});return;}
|
||||||
|
var io=new IntersectionObserver(function(entries){entries.forEach(function(e){if(e.isIntersecting){e.target.classList.add('mb-in');io.unobserve(e.target);}});},{threshold:0.15});
|
||||||
|
els.forEach(function(el){io.observe(el);});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"title": "Cover Story",
|
"title": "Cover Story",
|
||||||
"description": "12-column asymmetric hero anchoring the issue landing page.",
|
"description": "Asymmetric magazine cover anchoring the drop / landing page.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"kicker": {
|
"kicker": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"title": "Kicker",
|
"title": "Kicker",
|
||||||
"description": "Small-caps label above the headline.",
|
"description": "Mono label above the headline (e.g. \"Drop 04 / Spring\").",
|
||||||
"x-editor": "text"
|
"x-editor": "text"
|
||||||
},
|
},
|
||||||
"headline": {
|
"headline": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"title": "Headline",
|
"title": "Headline",
|
||||||
"description": "Display headline (rich text — allows italics).",
|
"description": "Massive display headline (rich text — allows italics).",
|
||||||
"x-editor": "richtext"
|
"x-editor": "richtext"
|
||||||
},
|
},
|
||||||
"deck": {
|
"deck": {
|
||||||
@ -25,14 +25,21 @@
|
|||||||
"image": {
|
"image": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"title": "Cover Image",
|
"title": "Cover Image",
|
||||||
"description": "Hero image for the cover story.",
|
"description": "Duotone-treated cover image.",
|
||||||
"x-editor": "media"
|
"x-editor": "media"
|
||||||
},
|
},
|
||||||
"link": {
|
"link": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"title": "Read Story Link",
|
"title": "CTA Link",
|
||||||
"description": "URL to the full feature.",
|
"description": "URL to the full feature or the shop.",
|
||||||
"x-editor": "link"
|
"x-editor": "link"
|
||||||
|
},
|
||||||
|
"linkLabel": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "CTA Label",
|
||||||
|
"description": "Button label.",
|
||||||
|
"x-editor": "text",
|
||||||
|
"default": "Read the story"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
blocks/credits_ticker.ninjatpl
Normal file
10
blocks/credits_ticker.ninjatpl
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{# credits_ticker (magazine-bold) — marquee strip of credits. Fields: items[].label. MID motion: CSS marquee, paused on hover, static under prefers-reduced-motion. The track renders the item list TWICE so the -50% translate loops seamlessly (engine can't repeat N times). #}
|
||||||
|
{% if items %}
|
||||||
|
<section data-block="magazine-bold:credits_ticker" class="colorblock-accent w-full border-y-2 border-[hsl(var(--foreground))] py-3">
|
||||||
|
<div class="mb-marquee">
|
||||||
|
<div class="mb-marquee-track">
|
||||||
|
{% for item in items %}<span class="mb-caps px-6 text-[hsl(var(--accent-foreground))]">{{ item.label }}</span><span aria-hidden="true" class="text-[hsl(var(--accent-foreground))]">/</span>{% endfor %}{% for item in items %}<span class="mb-caps px-6 text-[hsl(var(--accent-foreground))]">{{ item.label }}</span><span aria-hidden="true" class="text-[hsl(var(--accent-foreground))]">/</span>{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
21
blocks/credits_ticker.schema.json
Normal file
21
blocks/credits_ticker.schema.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Credits Ticker",
|
||||||
|
"description": "Marquee strip of credits scrolling across a fluoro accent band.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"items": {
|
||||||
|
"type": "array",
|
||||||
|
"title": "Credits",
|
||||||
|
"description": "The credit lines shown in the ticker.",
|
||||||
|
"default": [],
|
||||||
|
"x-editor": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"label": { "type": "string", "title": "Credit", "description": "One credit (e.g. \"Photography — Kit Sato\").", "x-editor": "text" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
blocks/drop_calendar.ninjatpl
Normal file
23
blocks/drop_calendar.ninjatpl
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{# drop_calendar (magazine-bold) — dated list of drops: mono datelines, condensed names, a status tag. Fields: heading, intro, drops[].date/.name/.detail/.status(live|soon|sold)/.link/.linkLabel. Static, no motion. #}
|
||||||
|
<section data-block="magazine-bold:drop_calendar" class="w-full py-12 md:py-16">
|
||||||
|
<div class="mx-auto max-w-4xl px-4 sm:px-6">
|
||||||
|
{% if heading %}<h2 class="mb-heading text-3xl md:text-5xl text-foreground">{{ heading }}</h2>{% endif %}
|
||||||
|
{% if intro %}<p class="mt-4 max-w-2xl text-deck text-muted-foreground">{{ intro }}</p>{% endif %}
|
||||||
|
{% if drops %}
|
||||||
|
<ul class="mt-10 border-t-2 border-[hsl(var(--foreground))]">
|
||||||
|
{% for drop in drops %}
|
||||||
|
<li class="flex flex-col gap-3 border-b-2 border-[hsl(var(--foreground))] py-5 sm:flex-row sm:items-center sm:gap-8">
|
||||||
|
<div class="mb-caps shrink-0 text-[hsl(var(--accent))] sm:w-40">{{ drop.date }}</div>
|
||||||
|
<div class="min-w-0 flex-1">
|
||||||
|
<h3 class="mb-heading text-xl md:text-2xl text-foreground">{% if drop.link %}<a href="{{ drop.link }}" class="transition-colors hover:text-[hsl(var(--accent))]">{{ drop.name }}</a>{% else %}{{ drop.name }}{% endif %}</h3>
|
||||||
|
{% if drop.detail %}<p class="mt-1 text-sm text-muted-foreground">{{ drop.detail }}</p>{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if drop.status == "live" %}<span class="mb-caps shrink-0 bg-[hsl(var(--accent))] px-3 py-1 text-[hsl(var(--accent-foreground))]">Live</span>
|
||||||
|
{% elif drop.status == "sold" %}<span class="mb-caps shrink-0 border-2 border-[hsl(var(--border))] px-3 py-1 text-muted-foreground">Sold out</span>
|
||||||
|
{% elif drop.status == "soon" %}<span class="mb-caps shrink-0 border-2 border-[hsl(var(--foreground))] px-3 py-1 text-foreground">Coming soon</span>{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% else %}<div class="mb-caption py-12">No drops scheduled.</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
45
blocks/drop_calendar.schema.json
Normal file
45
blocks/drop_calendar.schema.json
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Drop Calendar",
|
||||||
|
"description": "Dated list of upcoming and past drops with a status tag.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"heading": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Heading",
|
||||||
|
"description": "Section headline.",
|
||||||
|
"x-editor": "text"
|
||||||
|
},
|
||||||
|
"intro": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Intro",
|
||||||
|
"description": "Optional standfirst line.",
|
||||||
|
"x-editor": "textarea"
|
||||||
|
},
|
||||||
|
"drops": {
|
||||||
|
"type": "array",
|
||||||
|
"title": "Drops",
|
||||||
|
"description": "The scheduled drops.",
|
||||||
|
"default": [],
|
||||||
|
"x-editor": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"date": { "type": "string", "title": "Date", "description": "Dateline (e.g. \"14 MAR\").", "x-editor": "text" },
|
||||||
|
"name": { "type": "string", "title": "Name", "description": "Drop name.", "x-editor": "text" },
|
||||||
|
"detail": { "type": "string", "title": "Detail", "description": "Optional one-line detail.", "x-editor": "text" },
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Status",
|
||||||
|
"description": "Availability tag.",
|
||||||
|
"enum": ["live", "soon", "sold"],
|
||||||
|
"default": "soon",
|
||||||
|
"x-editor": "select"
|
||||||
|
},
|
||||||
|
"link": { "type": "string", "title": "Link", "description": "Optional link to the drop.", "x-editor": "link" },
|
||||||
|
"linkLabel": { "type": "string", "title": "Link Label", "description": "Link label.", "x-editor": "text", "default": "View drop" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +0,0 @@
|
|||||||
<nav data-block="magazine-bold:issue_archive" class="py-6">
|
|
||||||
<div class="font-mono text-kicker text-muted-foreground mb-6">Issue Archive</div>
|
|
||||||
{% set rows = items %}{% if not rows %}{% set rows = bucket_data %}{% endif %}{% set eff = limit|default:12|integer %}{% if rows %}<ol class="grid grid-cols-1 md:grid-cols-2 gap-x-12 gap-y-4">{% for item in rows %}{% if forloop.Counter0 < eff %}<li class="flex items-baseline gap-4 mb-hairline-bottom py-3"><span class="font-mono text-kicker text-muted-foreground w-10">{{ forloop.Counter|stringformat:"%02d" }}</span><span class="font-display flex-1 text-foreground">{% if item.title %}{{ item.title }}{% elif item.name %}{{ item.name }}{% else %}Untitled issue{% endif %}</span>{% if item.date %}<span class="font-mono mb-caption">{{ item.date }}</span>{% endif %}</li>{% endif %}{% endfor %}</ol>{% else %}<p class="font-mono mb-caption">No issues yet.</p>{% endif %}
|
|
||||||
</nav>
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
||||||
"title": "Issue Archive",
|
|
||||||
"description": "Numbered list of back-issues driven by a bucket query.",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"bucketSlug": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Issues Bucket",
|
|
||||||
"description": "Bucket that holds the issue rows.",
|
|
||||||
"x-editor": "bucket-picker"
|
|
||||||
},
|
|
||||||
"limit": {
|
|
||||||
"type": "integer",
|
|
||||||
"title": "Limit",
|
|
||||||
"description": "How many issues to render.",
|
|
||||||
"x-editor": "number",
|
|
||||||
"minimum": 1,
|
|
||||||
"maximum": 50,
|
|
||||||
"default": 12
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
24
blocks/lookbook.ninjatpl
Normal file
24
blocks/lookbook.ninjatpl
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{# lookbook (magazine-bold) — grid of duotone look frames with mono numbering, captions and optional shop links. Fields: heading, intro, looks[].image/.caption/.credit/.link/.linkLabel. Image via {% img %} (media filter not registered in block render path). MID motion: duotone hover + mb-reveal, reduced-motion safe. #}
|
||||||
|
<section data-block="magazine-bold:lookbook" class="w-full py-12 md:py-16">
|
||||||
|
<div class="mx-auto max-w-6xl px-4 sm:px-6">
|
||||||
|
{% if heading %}<h2 class="mb-heading text-3xl md:text-5xl text-foreground">{{ heading }}</h2>{% endif %}
|
||||||
|
{% if intro %}<p class="mt-4 max-w-2xl text-deck text-muted-foreground">{{ intro }}</p>{% endif %}
|
||||||
|
{% if looks %}
|
||||||
|
<div class="mt-10 grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{% for look in looks %}
|
||||||
|
<figure class="mb-reveal flex flex-col">
|
||||||
|
{% if look.image %}<div class="mb-duotone aspect-[3/4] w-full overflow-hidden">{% img look.image alt=look.caption|default:"" class="h-full w-full object-cover" %}</div>{% endif %}
|
||||||
|
<figcaption class="mt-3 flex items-baseline gap-3">
|
||||||
|
<span class="mb-caps text-[hsl(var(--accent))]">{{ forloop.Counter|stringformat:"%02d" }}</span>
|
||||||
|
<span class="min-w-0">
|
||||||
|
{% if look.caption %}<span class="block text-sm font-medium text-foreground">{{ look.caption }}</span>{% endif %}
|
||||||
|
{% if look.credit %}<span class="mb-caps block text-muted-foreground">{{ look.credit }}</span>{% endif %}
|
||||||
|
</span>
|
||||||
|
</figcaption>
|
||||||
|
{% if look.link %}<a href="{{ look.link }}" class="mb-link mt-2 inline-block text-sm">{{ look.linkLabel|default:"Shop the look" }}</a>{% endif %}
|
||||||
|
</figure>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% else %}<div class="mb-caption py-12">No looks yet.</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
37
blocks/lookbook.schema.json
Normal file
37
blocks/lookbook.schema.json
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Lookbook",
|
||||||
|
"description": "Grid of duotone look frames for a drop — numbered, captioned, optionally shoppable.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"heading": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Heading",
|
||||||
|
"description": "Section headline.",
|
||||||
|
"x-editor": "text"
|
||||||
|
},
|
||||||
|
"intro": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Intro",
|
||||||
|
"description": "Optional standfirst line.",
|
||||||
|
"x-editor": "textarea"
|
||||||
|
},
|
||||||
|
"looks": {
|
||||||
|
"type": "array",
|
||||||
|
"title": "Looks",
|
||||||
|
"description": "The lookbook frames.",
|
||||||
|
"default": [],
|
||||||
|
"x-editor": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"image": { "type": "string", "title": "Image", "description": "Look photo.", "x-editor": "media" },
|
||||||
|
"caption": { "type": "string", "title": "Caption", "description": "Look name / description.", "x-editor": "text" },
|
||||||
|
"credit": { "type": "string", "title": "Credit", "description": "Optional credit line (model, photographer).", "x-editor": "text" },
|
||||||
|
"link": { "type": "string", "title": "Shop Link", "description": "Optional link to the product.", "x-editor": "link" },
|
||||||
|
"linkLabel": { "type": "string", "title": "Link Label", "description": "Shop link label.", "x-editor": "text", "default": "Shop the look" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +0,0 @@
|
|||||||
<div data-block="magazine-bold:masthead" class="py-6 mb-hairline-bottom flex items-end justify-between gap-6">
|
|
||||||
<div class="flex items-baseline gap-4">
|
|
||||||
{% if issueNo %}<span class="font-mono text-kicker">No. {{ issueNo }}</span>{% endif %}
|
|
||||||
{% if tagline %}<span class="font-mono text-kicker text-muted-foreground">{{ tagline }}</span>{% endif %}
|
|
||||||
</div>
|
|
||||||
{% if date %}<span class="font-mono text-kicker text-muted-foreground">{{ date }}</span>{% endif %}
|
|
||||||
</div>
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
||||||
"title": "Issue Masthead",
|
|
||||||
"description": "Wordmark + folio bar across the top of the page.",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"issueNo": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Issue Number",
|
|
||||||
"description": "Folio number (e.g. \"01\").",
|
|
||||||
"x-editor": "text"
|
|
||||||
},
|
|
||||||
"tagline": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Tagline",
|
|
||||||
"description": "Issue volume / tagline line (e.g. \"Vol. 1 / Spring\").",
|
|
||||||
"x-editor": "text"
|
|
||||||
},
|
|
||||||
"date": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Date",
|
|
||||||
"description": "Issue date or month.",
|
|
||||||
"x-editor": "text"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
<section data-block="magazine-bold:photo_essay" class="w-full py-12">
|
|
||||||
<div class="max-w-6xl mx-auto px-6">
|
|
||||||
{% if title %}<h2 class="font-display text-deck text-foreground mb-8">{{ title }}</h2>{% endif %}
|
|
||||||
{% if frames %}<div class="mb-photo-grid">{% for frame in frames %}{% if frame.image %}<figure class="{% if frame.span == "full" %}mb-frame-full{% elif frame.span == "tall" %}mb-frame-tall{% else %}mb-frame-half{% endif %}"><img src="{{ frame.image }}" alt="{{ frame.caption }}" class="w-full h-auto block" loading="lazy"><figcaption class="mb-caption font-mono mt-2 flex items-baseline gap-3"><span class="mb-image-folio">{{ forloop.Counter|stringformat:"%02d" }}</span>{% if frame.caption %}<span>{{ frame.caption }}</span>{% endif %}</figcaption></figure>{% endif %}{% endfor %}</div>{% else %}<div class="font-mono mb-caption py-12">No frames yet.</div>{% endif %}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
||||||
"title": "Photo Essay",
|
|
||||||
"description": "Numbered photo-essay scaffolding with asymmetric frame spans.",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"title": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Essay Title",
|
|
||||||
"description": "Essay heading shown above the frames.",
|
|
||||||
"x-editor": "text"
|
|
||||||
},
|
|
||||||
"frames": {
|
|
||||||
"type": "array",
|
|
||||||
"title": "Frames",
|
|
||||||
"description": "Ordered list of photo frames in the essay.",
|
|
||||||
"default": [],
|
|
||||||
"x-editor": "collection",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"image": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Image",
|
|
||||||
"description": "Frame image.",
|
|
||||||
"x-editor": "media"
|
|
||||||
},
|
|
||||||
"caption": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Caption",
|
|
||||||
"description": "Mono-set caption.",
|
|
||||||
"x-editor": "text"
|
|
||||||
},
|
|
||||||
"span": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Span",
|
|
||||||
"description": "Frame layout span.",
|
|
||||||
"enum": ["half", "full", "tall"],
|
|
||||||
"default": "half",
|
|
||||||
"x-editor": "select"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["image"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
<aside data-block="magazine-bold:pull_quote" data-mb-accent="{% if accent == "blue" %}blue{% elif accent == "lime" %}lime{% else %}pink{% endif %}" class="my-8 grid grid-cols-12 gap-4 items-start">
|
|
||||||
<div class="mb-quote-bar col-span-1 h-full min-h-[3rem]"></div>
|
|
||||||
<blockquote class="col-span-11 mb-quote-text">
|
|
||||||
<p class="font-display text-deck">{% if quote %}{{ quote|safe }}{% endif %}</p>
|
|
||||||
{% if attribution %}<cite class="block mt-4 font-mono text-kicker text-muted-foreground not-italic">— {{ attribution }}</cite>{% endif %}
|
|
||||||
</blockquote>
|
|
||||||
</aside>
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
||||||
"title": "Pull Quote",
|
|
||||||
"description": "Color-block pull quote that sits in the gutter or main column.",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"quote": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Quote",
|
|
||||||
"description": "The quote body (rich text).",
|
|
||||||
"x-editor": "richtext"
|
|
||||||
},
|
|
||||||
"attribution": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Attribution",
|
|
||||||
"description": "Who said it.",
|
|
||||||
"x-editor": "text"
|
|
||||||
},
|
|
||||||
"accent": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Accent",
|
|
||||||
"description": "Which rotating accent to apply.",
|
|
||||||
"enum": ["pink", "blue", "lime"],
|
|
||||||
"default": "pink",
|
|
||||||
"x-editor": "select"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
27
fonts.json
27
fonts.json
@ -1 +1,26 @@
|
|||||||
[]
|
[
|
||||||
|
{
|
||||||
|
"name": "Archivo Black",
|
||||||
|
"family": "Archivo Black",
|
||||||
|
"variants": [
|
||||||
|
{ "weight": "400", "style": "normal", "file": "fonts/archivo-black-400.woff2" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Inter",
|
||||||
|
"family": "Inter",
|
||||||
|
"variants": [
|
||||||
|
{ "weight": "400", "style": "normal", "file": "fonts/inter-400.woff2" },
|
||||||
|
{ "weight": "500", "style": "normal", "file": "fonts/inter-500.woff2" },
|
||||||
|
{ "weight": "700", "style": "normal", "file": "fonts/inter-700.woff2" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "IBM Plex Mono",
|
||||||
|
"family": "IBM Plex Mono",
|
||||||
|
"variants": [
|
||||||
|
{ "weight": "400", "style": "normal", "file": "fonts/ibm-plex-mono-400.woff2" },
|
||||||
|
{ "weight": "500", "style": "normal", "file": "fonts/ibm-plex-mono-500.woff2" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|||||||
556
manifest.yaml
556
manifest.yaml
@ -1,5 +1,14 @@
|
|||||||
# Magazine Bold theme — codeless .bnp manifest (WO-WZ-021 Phase B1).
|
# Magazine Bold theme — codeless .bnp manifest (Theme Fleet Wave A, WO-TF-021).
|
||||||
# Synthesized into manifest.pb by `ninja plugin build` (no plugin.wasm).
|
# Synthesized into manifest.pb by `ninja plugin build` (no plugin.wasm).
|
||||||
|
#
|
||||||
|
# Persona: a streetwear label / fashion-drop publication. Print-cover energy
|
||||||
|
# translated to drops and lookbooks — massive condensed uppercase headlines
|
||||||
|
# (Archivo Black), duotone imagery, grid-breaking overlap, mono datelines and
|
||||||
|
# fluoro accent slabs. Newsprint-plus-voltage by day, ink-black-plus-fluoro by
|
||||||
|
# night. Colour ALWAYS via hsl(var(--token)); font families ALWAYS via
|
||||||
|
# var(--font-heading|body|mono, <fallback>). No literal colours, no hardcoded
|
||||||
|
# families. Motion tier MID: JS effects (marquee, scroll reveal, duotone hover),
|
||||||
|
# no canvas, prefers-reduced-motion honored with static fallbacks.
|
||||||
theme_presets: presets.json
|
theme_presets: presets.json
|
||||||
bundled_fonts: fonts.json
|
bundled_fonts: fonts.json
|
||||||
master_pages: master_pages.json
|
master_pages: master_pages.json
|
||||||
@ -7,212 +16,441 @@ master_pages: master_pages.json
|
|||||||
system_templates:
|
system_templates:
|
||||||
- key: magazine-bold
|
- key: magazine-bold
|
||||||
title: Magazine Bold
|
title: Magazine Bold
|
||||||
description: "Editorial-print theme with oversized display type, asymmetric grids and rotating color-block accents for fashion, streetwear and culture publications."
|
description: "A streetwear label's cover-story chrome: massive condensed uppercase headlines, duotone imagery, grid-breaking overlap and fluoro accent slabs. Built for fashion drops, lookbooks and culture publications."
|
||||||
|
|
||||||
page_templates:
|
page_templates:
|
||||||
- system: magazine-bold
|
- system: magazine-bold
|
||||||
key: default
|
key: default
|
||||||
title: Default
|
title: Default
|
||||||
description: "Standard editorial page with masthead, asymmetric main, footer colophon."
|
description: "Standard page: masthead, single reading column, colophon footer."
|
||||||
slots: [masthead, main, colophon]
|
slots: [header, main, footer]
|
||||||
- system: magazine-bold
|
|
||||||
key: landing
|
|
||||||
title: Issue Landing
|
|
||||||
description: "Cover-story hero plus issue navigation strip."
|
|
||||||
slots: [cover, secondary, main, colophon]
|
|
||||||
- system: magazine-bold
|
|
||||||
key: article
|
|
||||||
title: Article / Feature
|
|
||||||
description: "Long-form feature with deck, byline rail and pull-quote gutter."
|
|
||||||
slots: [masthead, deck, main, colophon]
|
|
||||||
- system: magazine-bold
|
- system: magazine-bold
|
||||||
key: full-width
|
key: full-width
|
||||||
title: Full Bleed
|
title: Full Bleed
|
||||||
description: "Edge-to-edge photo-essay layout."
|
description: "Edge-to-edge for lookbooks, cover images and photo essays."
|
||||||
slots: [masthead, main, colophon]
|
slots: [header, main, footer]
|
||||||
|
- system: magazine-bold
|
||||||
|
key: landing
|
||||||
|
title: Cover / Drop
|
||||||
|
description: "Cover-story stack: masthead, lead drop, river of stories, colophon."
|
||||||
|
slots: [masthead, lead, river, footer]
|
||||||
|
- system: magazine-bold
|
||||||
|
key: article
|
||||||
|
title: Feature
|
||||||
|
description: "Long-form feature: byline, drop-cap opener, reading column with a marginalia rail."
|
||||||
|
slots: [header, byline, main, marginalia, footer]
|
||||||
|
- system: magazine-bold
|
||||||
|
key: blog-index
|
||||||
|
title: Archive
|
||||||
|
description: "Drop archive: masthead, featured drop, and a river of stories."
|
||||||
|
slots: [header, featured, main, footer]
|
||||||
|
- system: magazine-bold
|
||||||
|
key: contact
|
||||||
|
title: Studio
|
||||||
|
description: "Contact layout with an optional aside for stockists, hours and location."
|
||||||
|
slots: [header, main, aside, footer]
|
||||||
|
- system: magazine-bold
|
||||||
|
key: auth
|
||||||
|
title: Members
|
||||||
|
description: "Centred members shell with wordmark and a fluoro rule."
|
||||||
|
slots: [main, footer]
|
||||||
|
|
||||||
template_overrides:
|
template_overrides:
|
||||||
|
# Chrome (4)
|
||||||
|
- { template: magazine-bold, block: navbar }
|
||||||
|
- { template: magazine-bold, block: footer }
|
||||||
|
- { template: magazine-bold, block: utility-bar }
|
||||||
|
- { template: magazine-bold, block: announcement-bar }
|
||||||
|
# Marketing (12)
|
||||||
|
- { template: magazine-bold, block: hero }
|
||||||
|
- { template: magazine-bold, block: feature-grid }
|
||||||
|
- { template: magazine-bold, block: rich-section }
|
||||||
|
- { template: magazine-bold, block: stats }
|
||||||
|
- { template: magazine-bold, block: testimonial }
|
||||||
|
- { template: magazine-bold, block: logo-strip }
|
||||||
|
- { template: magazine-bold, block: pricing }
|
||||||
|
- { template: magazine-bold, block: team }
|
||||||
|
- { template: magazine-bold, block: faq }
|
||||||
|
- { template: magazine-bold, block: timeline }
|
||||||
|
- { template: magazine-bold, block: cta }
|
||||||
|
- { template: magazine-bold, block: countdown }
|
||||||
|
# Primitives (11)
|
||||||
- { template: magazine-bold, block: heading }
|
- { template: magazine-bold, block: heading }
|
||||||
- { template: magazine-bold, block: text }
|
- { template: magazine-bold, block: text }
|
||||||
|
- { template: magazine-bold, block: rich-text }
|
||||||
- { template: magazine-bold, block: image }
|
- { template: magazine-bold, block: image }
|
||||||
|
- { template: magazine-bold, block: quote }
|
||||||
|
- { template: magazine-bold, block: divider }
|
||||||
- { template: magazine-bold, block: button }
|
- { template: magazine-bold, block: button }
|
||||||
|
- { template: magazine-bold, block: card }
|
||||||
|
- { template: magazine-bold, block: gallery }
|
||||||
|
- { template: magazine-bold, block: video-embed }
|
||||||
|
- { template: magazine-bold, block: audio-embed }
|
||||||
|
# Commerce / local (6)
|
||||||
|
- { template: magazine-bold, block: menu-list }
|
||||||
|
- { template: magazine-bold, block: event-list }
|
||||||
|
- { template: magazine-bold, block: contact-form }
|
||||||
|
- { template: magazine-bold, block: contact-card }
|
||||||
|
- { template: magazine-bold, block: hours-location }
|
||||||
|
- { template: magazine-bold, block: social-links }
|
||||||
|
# Blog family (12)
|
||||||
|
- { template: magazine-bold, block: blog-hero }
|
||||||
|
- { template: magazine-bold, block: featured-posts }
|
||||||
|
- { template: magazine-bold, block: related-posts }
|
||||||
|
- { template: magazine-bold, block: popular-posts }
|
||||||
|
- { template: magazine-bold, block: category-list }
|
||||||
|
- { template: magazine-bold, block: post-hero }
|
||||||
|
- { template: magazine-bold, block: post-metadata }
|
||||||
|
- { template: magazine-bold, block: author-bio }
|
||||||
|
- { template: magazine-bold, block: author-bio-hero }
|
||||||
|
- { template: magazine-bold, block: newsletter-signup }
|
||||||
|
- { template: magazine-bold, block: breadcrumbs }
|
||||||
|
- { template: magazine-bold, block: sidebar-nav }
|
||||||
|
# Auth surface (3)
|
||||||
|
- { template: magazine-bold, block: auth-form }
|
||||||
|
- { template: magazine-bold, block: password-reset-form }
|
||||||
|
- { template: magazine-bold, block: auth-status }
|
||||||
|
# System (1)
|
||||||
|
- { template: magazine-bold, block: page-suggestions }
|
||||||
|
|
||||||
email_wrappers:
|
email_wrappers:
|
||||||
- magazine-bold
|
- magazine-bold
|
||||||
|
|
||||||
css:
|
css:
|
||||||
input_css_append: |
|
input_css_append: |
|
||||||
/* ===========================================================
|
/* === Magazine Bold theme — utility layer ===
|
||||||
* Magazine Bold — theme utilities
|
*
|
||||||
* Appended to the Tailwind input via CSSManifest.InputCSSAppend.
|
* Streetwear / fashion-drop identity: massive condensed uppercase headlines,
|
||||||
* =========================================================== */
|
* heavy 2px rules, hard square edges, mono datelines and fluoro accent slabs.
|
||||||
|
* Colour is ALWAYS consumed via hsl(var(--token)); font families ALWAYS via
|
||||||
|
* var(--font-heading|body|mono, <fallback>). No literal colours, no hardcoded
|
||||||
|
* families.
|
||||||
|
*/
|
||||||
|
|
||||||
/* --- Font-slot classes (consume CMS-managed font variables) --- */
|
@layer utilities {
|
||||||
.font-display {
|
:root {
|
||||||
font-family: var(--font-heading, "Playfair Display", "Cormorant Garamond", Georgia, "Times New Roman", serif);
|
--mb-measure: 68ch;
|
||||||
font-feature-settings: "ss01", "ss02", "kern", "liga";
|
--mb-rule: 2px solid hsl(var(--foreground));
|
||||||
font-optical-sizing: auto;
|
|
||||||
}
|
|
||||||
.font-serif-sub {
|
|
||||||
font-family: var(--font-heading, "Fraunces", "Playfair Display", Georgia, "Times New Roman", serif);
|
|
||||||
}
|
|
||||||
.font-sans {
|
|
||||||
font-family: var(--font-body, "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);
|
|
||||||
}
|
|
||||||
.font-mono {
|
|
||||||
font-family: var(--font-mono, "JetBrains Mono", "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Display-size utilities ------------------------------- */
|
/* --- Font slots (consume CMS-managed font variables) --- */
|
||||||
/* text-folio: 220pt-equivalent oversized folio number / cover hit.
|
.font-display { font-family: var(--font-heading, "Archivo Black", "Arial Black", sans-serif); }
|
||||||
The clamp resolves to ~220px (~165pt) at 1440px, scaling down to 80px on mobile. */
|
.font-sans { font-family: var(--font-body, "Inter", system-ui, sans-serif); }
|
||||||
|
.font-mono { font-family: var(--font-mono, "IBM Plex Mono", ui-monospace, monospace); }
|
||||||
|
|
||||||
|
/* --- Page widths / reading measures --- */
|
||||||
|
.mb-column {
|
||||||
|
max-width: var(--mb-measure);
|
||||||
|
margin-left: auto; margin-right: auto;
|
||||||
|
padding-left: 1rem; padding-right: 1rem;
|
||||||
|
}
|
||||||
|
.mb-wide {
|
||||||
|
max-width: 80rem;
|
||||||
|
margin-left: auto; margin-right: auto;
|
||||||
|
padding-left: 1rem; padding-right: 1rem;
|
||||||
|
}
|
||||||
|
.mb-article-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 2rem;
|
||||||
|
max-width: 84rem;
|
||||||
|
margin-left: auto; margin-right: auto;
|
||||||
|
padding-left: 1rem; padding-right: 1rem;
|
||||||
|
}
|
||||||
|
@media (min-width: 1024px) {
|
||||||
|
.mb-article-grid { grid-template-columns: minmax(0, 68ch) 18rem; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Prose reading column --- */
|
||||||
|
.mb-prose {
|
||||||
|
max-width: var(--mb-measure);
|
||||||
|
margin-left: auto; margin-right: auto;
|
||||||
|
font-family: var(--font-body, "Inter", system-ui, sans-serif);
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
.mb-prose p { margin: 0 0 1.15em 0; }
|
||||||
|
.mb-prose h1, .mb-prose h2, .mb-prose h3, .mb-prose h4 {
|
||||||
|
font-family: var(--font-heading, "Archivo Black", "Arial Black", sans-serif);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
line-height: 1.0;
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
}
|
||||||
|
.mb-prose a {
|
||||||
|
color: hsl(var(--accent));
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-thickness: 2px;
|
||||||
|
text-underline-offset: 2px;
|
||||||
|
}
|
||||||
|
.mb-prose blockquote {
|
||||||
|
font-weight: 500;
|
||||||
|
border-left: 4px solid hsl(var(--accent));
|
||||||
|
padding-left: 1rem; margin-left: 0;
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Mono / tracked labels (kickers, eyebrows, meta) --- */
|
||||||
|
.mb-caps {
|
||||||
|
font-family: var(--font-mono, "IBM Plex Mono", ui-monospace, monospace);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.14em;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Display heading (Archivo Black, massive condensed caps) --- */
|
||||||
|
.mb-heading {
|
||||||
|
font-family: var(--font-heading, "Archivo Black", "Arial Black", sans-serif);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 0.95;
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
}
|
||||||
|
.mb-underline { position: relative; padding-bottom: 0.28em; }
|
||||||
|
.mb-underline::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute; left: 0; right: 0; bottom: 0;
|
||||||
|
height: 4px;
|
||||||
|
background: hsl(var(--accent));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Heavy rules (bars, not hairlines) --- */
|
||||||
|
hr.mb-rule, .mb-rule, .mb-hairline {
|
||||||
|
border: 0;
|
||||||
|
border-top: var(--mb-rule);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.mb-hairline-top { border-top: var(--mb-rule); }
|
||||||
|
.mb-hairline-bottom { border-bottom: var(--mb-rule); }
|
||||||
|
|
||||||
|
/* --- Body text override --- */
|
||||||
|
.mb-body {
|
||||||
|
font-family: var(--font-body, "Inter", system-ui, sans-serif);
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Inline accent link --- */
|
||||||
|
.mb-link {
|
||||||
|
color: hsl(var(--accent));
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-thickness: 2px;
|
||||||
|
text-underline-offset: 3px;
|
||||||
|
transition: color 120ms ease, text-decoration-color 120ms ease;
|
||||||
|
}
|
||||||
|
.mb-link:hover, .mb-link:focus-visible { color: hsl(var(--foreground)); }
|
||||||
|
|
||||||
|
/* --- Button — fluoro / ink slab, square, uppercase mono --- */
|
||||||
|
.mb-button, .mb-btn {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.7rem 1.4rem;
|
||||||
|
font-family: var(--font-mono, "IBM Plex Mono", ui-monospace, monospace);
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.14em;
|
||||||
|
background-color: hsl(var(--foreground));
|
||||||
|
color: hsl(var(--background));
|
||||||
|
border: 2px solid hsl(var(--foreground));
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
|
||||||
|
}
|
||||||
|
.mb-button:hover, .mb-button:focus, .mb-btn:hover, .mb-btn:focus {
|
||||||
|
background-color: hsl(var(--accent));
|
||||||
|
color: hsl(var(--accent-foreground));
|
||||||
|
border-color: hsl(var(--accent));
|
||||||
|
}
|
||||||
|
.mb-button:focus-visible, .mb-btn:focus-visible {
|
||||||
|
outline: 2px solid hsl(var(--ring));
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Panel / card — heavy 2px frame, hard edges, no shadow --- */
|
||||||
|
.mb-panel, .mb-card {
|
||||||
|
border: 2px solid hsl(var(--foreground));
|
||||||
|
background-color: hsl(var(--card));
|
||||||
|
transition: border-color 120ms ease, transform 120ms ease;
|
||||||
|
}
|
||||||
|
.mb-panel:hover, .mb-card:hover { border-color: hsl(var(--accent)); }
|
||||||
|
|
||||||
|
/* --- Form field — heavy border, square, accent focus --- */
|
||||||
|
.mb-input {
|
||||||
|
width: 100%;
|
||||||
|
border: 2px solid hsl(var(--input));
|
||||||
|
background-color: hsl(var(--background));
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
padding: 0.6rem 0.8rem;
|
||||||
|
font-family: var(--font-body, "Inter", system-ui, sans-serif);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
.mb-input::placeholder { color: hsl(var(--muted-foreground)); }
|
||||||
|
.mb-input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: hsl(var(--accent));
|
||||||
|
box-shadow: 0 0 0 2px hsl(var(--accent));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Figure / caption --- */
|
||||||
|
.mb-figure { margin: 1.5rem 0; }
|
||||||
|
.mb-figure img { display: block; width: 100%; height: auto; }
|
||||||
|
.mb-figure figcaption {
|
||||||
|
margin-top: 0.6rem;
|
||||||
|
font-family: var(--font-mono, "IBM Plex Mono", ui-monospace, monospace);
|
||||||
|
font-size: 0.72rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: hsl(var(--muted-foreground));
|
||||||
|
}
|
||||||
|
.mb-caption {
|
||||||
|
font-family: var(--font-mono, "IBM Plex Mono", ui-monospace, monospace);
|
||||||
|
font-size: 0.72rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
color: hsl(var(--muted-foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Drop cap — oversized Archivo Black accent first letter --- */
|
||||||
|
.mb-dropcap p:first-of-type::first-letter {
|
||||||
|
font-family: var(--font-heading, "Archivo Black", "Arial Black", sans-serif);
|
||||||
|
color: hsl(var(--accent));
|
||||||
|
float: left;
|
||||||
|
font-size: 4.6em;
|
||||||
|
line-height: 0.78;
|
||||||
|
padding-right: 0.1em;
|
||||||
|
padding-top: 0.02em;
|
||||||
|
margin-bottom: -0.08em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Marginalia rail (mono side notes, MID motion reveal) --- */
|
||||||
|
.mb-marginalia {
|
||||||
|
font-family: var(--font-mono, "IBM Plex Mono", ui-monospace, monospace);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: hsl(var(--muted-foreground));
|
||||||
|
}
|
||||||
|
.mb-marginalia aside {
|
||||||
|
margin: 0 0 1.5rem 0;
|
||||||
|
padding-left: 0.75rem;
|
||||||
|
border-left: var(--mb-rule);
|
||||||
|
opacity: 0;
|
||||||
|
animation: mb-fade-in 500ms ease forwards;
|
||||||
|
}
|
||||||
|
@keyframes mb-fade-in { to { opacity: 1; } }
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.mb-marginalia {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
border-top: var(--mb-rule);
|
||||||
|
padding-top: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Masthead wordmark --- */
|
||||||
|
.mb-masthead-wordmark {
|
||||||
|
font-family: var(--font-heading, "Archivo Black", "Arial Black", sans-serif);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: -0.03em;
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Display size utilities (folio / deck / kicker) --- */
|
||||||
.text-folio {
|
.text-folio {
|
||||||
font-family: var(--font-heading, "Playfair Display", Georgia, serif);
|
font-family: var(--font-heading, "Archivo Black", "Arial Black", sans-serif);
|
||||||
font-size: clamp(80px, 18vw, 240px);
|
font-size: clamp(72px, 16vw, 220px);
|
||||||
line-height: 0.85;
|
line-height: 0.82;
|
||||||
letter-spacing: -0.04em;
|
letter-spacing: -0.04em;
|
||||||
font-weight: 200;
|
text-transform: uppercase;
|
||||||
font-style: italic;
|
|
||||||
}
|
}
|
||||||
.text-deck {
|
.text-deck {
|
||||||
font-family: var(--font-body, "Inter", system-ui, sans-serif);
|
font-family: var(--font-body, "Inter", system-ui, sans-serif);
|
||||||
font-size: clamp(28px, 3.2vw, 48px);
|
font-size: clamp(24px, 3vw, 44px);
|
||||||
line-height: 1.15;
|
line-height: 1.12;
|
||||||
letter-spacing: -0.01em;
|
letter-spacing: -0.01em;
|
||||||
font-weight: 400;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
.text-kicker {
|
.text-kicker {
|
||||||
font-family: var(--font-mono, "JetBrains Mono", monospace);
|
font-family: var(--font-mono, "IBM Plex Mono", ui-monospace, monospace);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.18em;
|
letter-spacing: 0.18em;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Hairline rule motif ---------------------------------- */
|
/* --- Asymmetric editorial grids (persona blocks) --- */
|
||||||
.mb-hairline { border-color: hsl(var(--border)); border-width: 1px; }
|
.mb-grid-12 { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 1.5rem; }
|
||||||
.mb-hairline-top { border-top: 1px solid hsl(var(--border)); }
|
.mb-photo-grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 1.25rem; }
|
||||||
.mb-hairline-bottom { border-bottom: 1px solid hsl(var(--border)); }
|
|
||||||
|
|
||||||
/* --- Asymmetric editorial grid ---------------------------- */
|
|
||||||
.mb-grid-12 {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
||||||
gap: 1.5rem;
|
|
||||||
}
|
|
||||||
.mb-grid-asym-cover {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
||||||
gap: 2rem;
|
|
||||||
align-items: end;
|
|
||||||
}
|
|
||||||
.mb-col-folio { grid-column: 1 / span 2; }
|
|
||||||
.mb-col-headline { grid-column: 3 / span 7; }
|
|
||||||
.mb-col-deck { grid-column: 10 / span 3; }
|
|
||||||
.mb-col-image-wide { grid-column: 1 / -1; }
|
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
|
||||||
.mb-grid-asym-cover { grid-template-columns: 1fr; gap: 1.25rem; }
|
|
||||||
.mb-col-folio, .mb-col-headline, .mb-col-deck { grid-column: 1 / -1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Photo essay frame spans ------------------------------ */
|
|
||||||
.mb-photo-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
||||||
gap: 1.25rem;
|
|
||||||
}
|
|
||||||
.mb-frame-half { grid-column: span 6 / span 6; }
|
.mb-frame-half { grid-column: span 6 / span 6; }
|
||||||
.mb-frame-full { grid-column: 1 / -1; }
|
.mb-frame-full { grid-column: 1 / -1; }
|
||||||
.mb-frame-tall { grid-column: span 5 / span 5; grid-row: span 2; }
|
.mb-frame-tall { grid-column: span 5 / span 5; grid-row: span 2; }
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.mb-photo-grid { grid-template-columns: 1fr; }
|
.mb-photo-grid { grid-template-columns: 1fr; }
|
||||||
.mb-frame-half, .mb-frame-full, .mb-frame-tall { grid-column: 1 / -1; grid-row: auto; }
|
.mb-frame-half, .mb-frame-full, .mb-frame-tall { grid-column: 1 / -1; grid-row: auto; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Color-block accent panels ---------------------------- */
|
/* --- Color-block accent panel --- */
|
||||||
.colorblock-accent {
|
.colorblock-accent { background-color: hsl(var(--accent)); color: hsl(var(--accent-foreground)); }
|
||||||
background-color: hsl(var(--accent));
|
|
||||||
color: hsl(var(--accent-foreground));
|
/* --- Full-bleed image --- */
|
||||||
|
.mb-fullbleed { width: 100vw; max-width: 100vw; margin-left: 50%; transform: translateX(-50%); }
|
||||||
|
|
||||||
|
/* --- Duotone photo treatment (signature — persona blocks) --- */
|
||||||
|
.mb-duotone { position: relative; }
|
||||||
|
.mb-duotone img { filter: grayscale(1) contrast(1.15); display: block; width: 100%; height: 100%; object-fit: cover; }
|
||||||
|
.mb-duotone::after {
|
||||||
|
content: ""; position: absolute; inset: 0;
|
||||||
|
background: hsl(var(--accent));
|
||||||
|
mix-blend-mode: multiply;
|
||||||
|
opacity: 0.55;
|
||||||
|
transition: opacity 200ms ease;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.mb-duotone:hover::after { opacity: 0.2; }
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.mb-duotone:hover::after { opacity: 0.55; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Per-accent pull-quote variants ----------------------- *
|
/* --- Scroll reveal (MID motion; static fallback) --- */
|
||||||
* These let a pull_quote override the preset accent on a per-instance basis.
|
.mb-reveal { opacity: 0; transform: translateY(16px); transition: opacity 500ms ease, transform 500ms ease; }
|
||||||
* They use literal HSL triples that match the spec §4 accent values so a
|
.mb-reveal.mb-in { opacity: 1; transform: none; }
|
||||||
* "pink" pull-quote always reads pink even on the ink-blue preset.
|
@media (prefers-reduced-motion: reduce) {
|
||||||
*/
|
.mb-reveal { opacity: 1; transform: none; transition: none; }
|
||||||
|
}
|
||||||
|
.no-js .mb-reveal { opacity: 1; transform: none; }
|
||||||
|
|
||||||
|
/* --- Marquee ticker (MID motion; paused on hover; static fallback) --- */
|
||||||
|
.mb-marquee { overflow: hidden; white-space: nowrap; }
|
||||||
|
.mb-marquee-track {
|
||||||
|
display: inline-block;
|
||||||
|
will-change: transform;
|
||||||
|
animation: mb-marquee 22s linear infinite;
|
||||||
|
}
|
||||||
|
.mb-marquee:hover .mb-marquee-track { animation-play-state: paused; }
|
||||||
|
@keyframes mb-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.mb-marquee-track { animation: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Reading-progress bar (fluoro; scroll-timeline; reduced-motion guard) --- */
|
||||||
|
.mb-progress {
|
||||||
|
position: sticky; top: 0;
|
||||||
|
height: 4px;
|
||||||
|
background: hsl(var(--accent));
|
||||||
|
transform-origin: 0 50%;
|
||||||
|
animation: mb-progress linear both;
|
||||||
|
animation-timeline: scroll(root block);
|
||||||
|
z-index: 50;
|
||||||
|
}
|
||||||
|
@keyframes mb-progress { from { transform: scaleX(0); } to { transform: scaleX(1); } }
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.mb-progress { animation: none; transform: scaleX(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Per-instance pull-quote accent override (persona quote) --- */
|
||||||
[data-mb-accent="pink"] { --mb-accent-h: 330; --mb-accent-s: 95%; --mb-accent-l: 58%; }
|
[data-mb-accent="pink"] { --mb-accent-h: 330; --mb-accent-s: 95%; --mb-accent-l: 58%; }
|
||||||
[data-mb-accent="blue"] { --mb-accent-h: 220; --mb-accent-s: 100%; --mb-accent-l: 56%; }
|
[data-mb-accent="blue"] { --mb-accent-h: 220; --mb-accent-s: 100%; --mb-accent-l: 56%; }
|
||||||
[data-mb-accent="lime"] { --mb-accent-h: 78; --mb-accent-s: 90%; --mb-accent-l: 50%; }
|
[data-mb-accent="lime"] { --mb-accent-h: 78; --mb-accent-s: 90%; --mb-accent-l: 50%; }
|
||||||
[data-mb-accent] .mb-quote-bar {
|
[data-mb-accent] .mb-quote-bar { background-color: hsl(var(--mb-accent-h) var(--mb-accent-s) var(--mb-accent-l)); }
|
||||||
background-color: hsl(var(--mb-accent-h) var(--mb-accent-s) var(--mb-accent-l));
|
|
||||||
}
|
|
||||||
[data-mb-accent] .mb-quote-text { color: hsl(var(--foreground)); }
|
|
||||||
|
|
||||||
/* --- Drop-cap (heading override) -------------------------- */
|
|
||||||
.mb-dropcap::first-letter {
|
|
||||||
font-family: var(--font-heading, "Playfair Display", Georgia, serif);
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 200;
|
|
||||||
float: left;
|
|
||||||
font-size: 5.5em;
|
|
||||||
line-height: 0.85;
|
|
||||||
padding-right: 0.08em;
|
|
||||||
margin-top: 0.04em;
|
|
||||||
color: hsl(var(--foreground));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Body text override (Inter + margin folios) ----------- */
|
|
||||||
.mb-body {
|
|
||||||
font-family: var(--font-body, "Inter", system-ui, sans-serif);
|
|
||||||
font-size: clamp(16px, 1.05vw, 18px);
|
|
||||||
line-height: 1.55;
|
|
||||||
letter-spacing: 0.005em;
|
|
||||||
color: hsl(var(--foreground));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Squared ink button ----------------------------------- */
|
|
||||||
.mb-button {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
background-color: hsl(var(--primary));
|
|
||||||
color: hsl(var(--primary-foreground));
|
|
||||||
font-family: var(--font-mono, "JetBrains Mono", monospace);
|
|
||||||
font-size: 12px;
|
|
||||||
letter-spacing: 0.14em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
padding: 0.875rem 1.5rem;
|
|
||||||
border: 1px solid hsl(var(--primary));
|
|
||||||
border-radius: 0;
|
|
||||||
transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
|
|
||||||
}
|
|
||||||
.mb-button:hover {
|
|
||||||
background-color: hsl(var(--accent));
|
|
||||||
color: hsl(var(--accent-foreground));
|
|
||||||
border-color: hsl(var(--accent));
|
|
||||||
}
|
|
||||||
.mb-button:focus-visible {
|
|
||||||
outline: 2px solid hsl(var(--ring));
|
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Caption (mono, used by image override + photo essay) -- */
|
|
||||||
.mb-caption {
|
|
||||||
font-family: var(--font-mono, "JetBrains Mono", monospace);
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.45;
|
|
||||||
letter-spacing: 0.01em;
|
|
||||||
color: hsl(var(--muted-foreground));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* --- Full-bleed image with numbered folio ----------------- */
|
|
||||||
.mb-fullbleed { width: 100vw; max-width: 100vw; margin-left: 50%; transform: translateX(-50%); }
|
|
||||||
.mb-image-folio {
|
|
||||||
font-family: var(--font-mono, "JetBrains Mono", monospace);
|
|
||||||
font-size: 11px;
|
|
||||||
letter-spacing: 0.16em;
|
|
||||||
color: hsl(var(--muted-foreground));
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,25 +1,37 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"key": "magazine-bold:default-master",
|
"key": "magazine-bold:default-master",
|
||||||
"title": "Magazine Bold — Default",
|
"title": "Magazine Bold Default Master",
|
||||||
"page_templates": ["default", "article"],
|
"page_templates": ["default", "full-width", "blog-index", "contact"],
|
||||||
"blocks": [
|
"blocks": [
|
||||||
{ "block_key": "navbar", "title": "Masthead Nav", "content": { "menuName": "main" }, "slot": "masthead", "sort_order": 0 },
|
{ "block_key": "navbar", "title": "Masthead Navigation", "content": { "menuName": "main", "logoType": "text", "logoText": { "text": "OFFCUT" } }, "slot": "header", "sort_order": 0 },
|
||||||
{ "block_key": "magazine-bold:masthead", "title": "Issue Masthead", "content": { "issueNo": "01", "tagline": "Vol. 1 / Spring" }, "slot": "masthead", "sort_order": 1 },
|
{ "block_key": "slot", "title": "Featured", "content": { "slotName": "featured" }, "slot": "featured", "sort_order": 0 },
|
||||||
{ "block_key": "slot", "title": "Main Slot", "content": { "slotName": "main" }, "slot": "main", "sort_order": 0 },
|
{ "block_key": "slot", "title": "Main Content", "content": { "slotName": "main", "placeholder": "Page content" }, "slot": "main", "sort_order": 0 },
|
||||||
{ "block_key": "magazine-bold:colophon", "title": "Colophon", "content": { "showSocial": "true" }, "slot": "colophon", "sort_order": 0 }
|
{ "block_key": "slot", "title": "Aside", "content": { "slotName": "aside" }, "slot": "aside", "sort_order": 0 },
|
||||||
|
{ "block_key": "footer", "title": "Colophon", "content": { "layout": "columns", "companyName": "OFFCUT", "tagline": "A streetwear label and its cover stories.", "showNewsletter": true, "newsletterTitle": "Drop alerts", "newsletterDescription": "Get the next drop before it sells.", "copyright": "OFFCUT" }, "slot": "footer", "sort_order": 0 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "magazine-bold:landing-master",
|
"key": "magazine-bold:landing-master",
|
||||||
"title": "Magazine Bold — Issue Landing",
|
"title": "Magazine Bold Cover Master",
|
||||||
"page_templates": ["landing", "full-width"],
|
"page_templates": ["landing"],
|
||||||
"blocks": [
|
"blocks": [
|
||||||
{ "block_key": "navbar", "title": "Masthead Nav", "content": { "menuName": "main" }, "slot": "masthead", "sort_order": 0 },
|
{ "block_key": "navbar", "title": "Masthead Navigation", "content": { "menuName": "main", "logoType": "text", "logoText": { "text": "OFFCUT" } }, "slot": "masthead", "sort_order": 0 },
|
||||||
{ "block_key": "magazine-bold:cover_story", "title": "Cover Story", "content": { "issueNo": "01" }, "slot": "cover", "sort_order": 0 },
|
{ "block_key": "slot", "title": "Lead", "content": { "slotName": "lead", "placeholder": "Cover story" }, "slot": "lead", "sort_order": 0 },
|
||||||
{ "block_key": "magazine-bold:issue_archive", "title": "Issue Archive", "content": { "bucketSlug": "issues" }, "slot": "secondary", "sort_order": 0 },
|
{ "block_key": "slot", "title": "River", "content": { "slotName": "river", "placeholder": "Recent stories" }, "slot": "river", "sort_order": 0 },
|
||||||
{ "block_key": "slot", "title": "Main Slot", "content": { "slotName": "main" }, "slot": "main", "sort_order": 0 },
|
{ "block_key": "footer", "title": "Colophon", "content": { "layout": "columns", "companyName": "OFFCUT", "tagline": "A streetwear label and its cover stories.", "showNewsletter": true, "newsletterTitle": "Drop alerts", "newsletterDescription": "Get the next drop before it sells.", "copyright": "OFFCUT" }, "slot": "footer", "sort_order": 0 }
|
||||||
{ "block_key": "magazine-bold:colophon", "title": "Colophon", "content": { "showSocial": "true" }, "slot": "colophon", "sort_order": 0 }
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "magazine-bold:article-master",
|
||||||
|
"title": "Magazine Bold Feature Master",
|
||||||
|
"page_templates": ["article"],
|
||||||
|
"blocks": [
|
||||||
|
{ "block_key": "navbar", "title": "Masthead Navigation", "content": { "menuName": "main", "logoType": "text", "logoText": { "text": "OFFCUT" } }, "slot": "header", "sort_order": 0 },
|
||||||
|
{ "block_key": "slot", "title": "Byline", "content": { "slotName": "byline" }, "slot": "byline", "sort_order": 0 },
|
||||||
|
{ "block_key": "slot", "title": "Feature Body", "content": { "slotName": "main", "placeholder": "Feature body" }, "slot": "main", "sort_order": 0 },
|
||||||
|
{ "block_key": "slot", "title": "Marginalia Rail", "content": { "slotName": "marginalia" }, "slot": "marginalia", "sort_order": 0 },
|
||||||
|
{ "block_key": "footer", "title": "Colophon", "content": { "layout": "columns", "companyName": "OFFCUT", "tagline": "A streetwear label and its cover stories.", "copyright": "OFFCUT" }, "slot": "footer", "sort_order": 0 }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -3,7 +3,8 @@ name = "magazine-bold"
|
|||||||
display_name = "Magazine Bold"
|
display_name = "Magazine Bold"
|
||||||
scope = "@themes"
|
scope = "@themes"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
description = "Editorial-print theme with oversized display type, asymmetric grids and rotating color-block accents for fashion, streetwear and culture publications."
|
description = "A streetwear label's cover-story chrome: massive condensed uppercase headlines, duotone imagery, grid-breaking overlap and fluoro accent slabs. Newsprint plus voltage by day, ink-black plus fluoro by night. For fashion drops, lookbooks and culture publications."
|
||||||
kind = "theme"
|
kind = "theme"
|
||||||
categories = ["templates"]
|
categories = ["templates"]
|
||||||
tags = ["fashion", "lifestyle", "streetwear", "magazine", "bold", "display", "music", "editorial", "art"]
|
tags = ["fashion", "lifestyle", "streetwear", "magazine", "bold", "display", "editorial", "drops", "lookbook"]
|
||||||
|
required_icon_packs = ["lucide", "simple-icons"]
|
||||||
|
|||||||
176
presets.json
176
presets.json
@ -1,8 +1,8 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id": "paper-pink",
|
"id": "newsprint-voltage",
|
||||||
"name": "Paper & Hot Pink",
|
"name": "Newsprint & Voltage",
|
||||||
"description": "Warm paper-tone background with a hot-pink editorial accent (default).",
|
"description": "The default. Off-white newsprint ground and near-black ink by day, ink-black with a fluoro hot-magenta accent by night.",
|
||||||
"theme": {
|
"theme": {
|
||||||
"mode": "both",
|
"mode": "both",
|
||||||
"lightColors": {
|
"lightColors": {
|
||||||
@ -18,41 +18,41 @@
|
|||||||
"secondaryForeground": "0 0% 8%",
|
"secondaryForeground": "0 0% 8%",
|
||||||
"muted": "38 20% 92%",
|
"muted": "38 20% 92%",
|
||||||
"mutedForeground": "0 0% 40%",
|
"mutedForeground": "0 0% 40%",
|
||||||
"accent": "330 95% 58%",
|
"accent": "330 95% 55%",
|
||||||
"accentForeground": "0 0% 100%",
|
"accentForeground": "0 0% 100%",
|
||||||
"destructive": "0 84% 55%",
|
"destructive": "0 84% 55%",
|
||||||
"destructiveForeground": "0 0% 100%",
|
"destructiveForeground": "0 0% 100%",
|
||||||
"border": "0 0% 88%",
|
"border": "0 0% 86%",
|
||||||
"input": "0 0% 88%",
|
"input": "0 0% 86%",
|
||||||
"ring": "330 95% 58%"
|
"ring": "330 95% 55%"
|
||||||
},
|
},
|
||||||
"darkColors": {
|
"darkColors": {
|
||||||
"background": "0 0% 8%",
|
"background": "0 0% 7%",
|
||||||
"foreground": "38 30% 96%",
|
"foreground": "38 30% 96%",
|
||||||
"card": "0 0% 11%",
|
"card": "0 0% 11%",
|
||||||
"cardForeground": "38 30% 96%",
|
"cardForeground": "38 30% 96%",
|
||||||
"popover": "0 0% 11%",
|
"popover": "0 0% 11%",
|
||||||
"popoverForeground": "38 30% 96%",
|
"popoverForeground": "38 30% 96%",
|
||||||
"primary": "38 30% 96%",
|
"primary": "38 30% 96%",
|
||||||
"primaryForeground": "0 0% 8%",
|
"primaryForeground": "0 0% 7%",
|
||||||
"secondary": "0 0% 14%",
|
"secondary": "0 0% 14%",
|
||||||
"secondaryForeground": "38 30% 96%",
|
"secondaryForeground": "38 30% 96%",
|
||||||
"muted": "0 0% 14%",
|
"muted": "0 0% 14%",
|
||||||
"mutedForeground": "38 10% 60%",
|
"mutedForeground": "38 10% 62%",
|
||||||
"accent": "330 95% 62%",
|
"accent": "330 100% 64%",
|
||||||
"accentForeground": "0 0% 8%",
|
"accentForeground": "0 0% 7%",
|
||||||
"destructive": "0 84% 60%",
|
"destructive": "0 84% 60%",
|
||||||
"destructiveForeground": "0 0% 100%",
|
"destructiveForeground": "0 0% 100%",
|
||||||
"border": "0 0% 18%",
|
"border": "0 0% 20%",
|
||||||
"input": "0 0% 18%",
|
"input": "0 0% 20%",
|
||||||
"ring": "330 95% 62%"
|
"ring": "330 100% 64%"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "ink-blue",
|
"id": "ink-blue",
|
||||||
"name": "Ink & Electric Blue",
|
"name": "Ink & Electric Blue",
|
||||||
"description": "Same paper/ink neutrals with an electric-blue accent.",
|
"description": "The same newsprint/ink neutrals with an electric-blue accent — cooler, sportier drop energy.",
|
||||||
"theme": {
|
"theme": {
|
||||||
"mode": "both",
|
"mode": "both",
|
||||||
"lightColors": {
|
"lightColors": {
|
||||||
@ -68,41 +68,41 @@
|
|||||||
"secondaryForeground": "0 0% 8%",
|
"secondaryForeground": "0 0% 8%",
|
||||||
"muted": "38 20% 92%",
|
"muted": "38 20% 92%",
|
||||||
"mutedForeground": "0 0% 40%",
|
"mutedForeground": "0 0% 40%",
|
||||||
"accent": "220 100% 56%",
|
"accent": "220 100% 54%",
|
||||||
"accentForeground": "0 0% 100%",
|
"accentForeground": "0 0% 100%",
|
||||||
"destructive": "0 84% 55%",
|
"destructive": "0 84% 55%",
|
||||||
"destructiveForeground": "0 0% 100%",
|
"destructiveForeground": "0 0% 100%",
|
||||||
"border": "0 0% 88%",
|
"border": "0 0% 86%",
|
||||||
"input": "0 0% 88%",
|
"input": "0 0% 86%",
|
||||||
"ring": "220 100% 56%"
|
"ring": "220 100% 54%"
|
||||||
},
|
},
|
||||||
"darkColors": {
|
"darkColors": {
|
||||||
"background": "0 0% 8%",
|
"background": "0 0% 7%",
|
||||||
"foreground": "38 30% 96%",
|
"foreground": "38 30% 96%",
|
||||||
"card": "0 0% 11%",
|
"card": "0 0% 11%",
|
||||||
"cardForeground": "38 30% 96%",
|
"cardForeground": "38 30% 96%",
|
||||||
"popover": "0 0% 11%",
|
"popover": "0 0% 11%",
|
||||||
"popoverForeground": "38 30% 96%",
|
"popoverForeground": "38 30% 96%",
|
||||||
"primary": "38 30% 96%",
|
"primary": "38 30% 96%",
|
||||||
"primaryForeground": "0 0% 8%",
|
"primaryForeground": "0 0% 7%",
|
||||||
"secondary": "0 0% 14%",
|
"secondary": "0 0% 14%",
|
||||||
"secondaryForeground": "38 30% 96%",
|
"secondaryForeground": "38 30% 96%",
|
||||||
"muted": "0 0% 14%",
|
"muted": "0 0% 14%",
|
||||||
"mutedForeground": "38 10% 60%",
|
"mutedForeground": "38 10% 62%",
|
||||||
"accent": "220 100% 62%",
|
"accent": "216 100% 66%",
|
||||||
"accentForeground": "0 0% 8%",
|
"accentForeground": "0 0% 7%",
|
||||||
"destructive": "0 84% 60%",
|
"destructive": "0 84% 60%",
|
||||||
"destructiveForeground": "0 0% 100%",
|
"destructiveForeground": "0 0% 100%",
|
||||||
"border": "0 0% 18%",
|
"border": "0 0% 20%",
|
||||||
"input": "0 0% 18%",
|
"input": "0 0% 20%",
|
||||||
"ring": "220 100% 62%"
|
"ring": "216 100% 66%"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "chalk-lime",
|
"id": "chalk-lime",
|
||||||
"name": "Chalk & Lime",
|
"name": "Chalk & Lime",
|
||||||
"description": "Cooler chalk background with a lime accent — fresh, contemporary edge.",
|
"description": "Cooler chalk ground and an acid-lime accent — a fresh, contemporary edge. Deeper ink-black night desk.",
|
||||||
"theme": {
|
"theme": {
|
||||||
"mode": "both",
|
"mode": "both",
|
||||||
"lightColors": {
|
"lightColors": {
|
||||||
@ -118,13 +118,13 @@
|
|||||||
"secondaryForeground": "0 0% 8%",
|
"secondaryForeground": "0 0% 8%",
|
||||||
"muted": "60 12% 93%",
|
"muted": "60 12% 93%",
|
||||||
"mutedForeground": "0 0% 40%",
|
"mutedForeground": "0 0% 40%",
|
||||||
"accent": "78 90% 50%",
|
"accent": "78 90% 45%",
|
||||||
"accentForeground": "0 0% 8%",
|
"accentForeground": "0 0% 8%",
|
||||||
"destructive": "0 84% 55%",
|
"destructive": "0 84% 55%",
|
||||||
"destructiveForeground": "0 0% 100%",
|
"destructiveForeground": "0 0% 100%",
|
||||||
"border": "0 0% 88%",
|
"border": "0 0% 86%",
|
||||||
"input": "0 0% 88%",
|
"input": "0 0% 86%",
|
||||||
"ring": "78 90% 50%"
|
"ring": "78 90% 45%"
|
||||||
},
|
},
|
||||||
"darkColors": {
|
"darkColors": {
|
||||||
"background": "0 0% 6%",
|
"background": "0 0% 6%",
|
||||||
@ -138,14 +138,114 @@
|
|||||||
"secondary": "0 0% 13%",
|
"secondary": "0 0% 13%",
|
||||||
"secondaryForeground": "60 15% 95%",
|
"secondaryForeground": "60 15% 95%",
|
||||||
"muted": "0 0% 13%",
|
"muted": "0 0% 13%",
|
||||||
"mutedForeground": "60 8% 60%",
|
"mutedForeground": "60 8% 62%",
|
||||||
"accent": "78 90% 55%",
|
"accent": "76 92% 56%",
|
||||||
"accentForeground": "0 0% 8%",
|
"accentForeground": "0 0% 8%",
|
||||||
"destructive": "0 84% 60%",
|
"destructive": "0 84% 60%",
|
||||||
"destructiveForeground": "0 0% 100%",
|
"destructiveForeground": "0 0% 100%",
|
||||||
"border": "0 0% 17%",
|
"border": "0 0% 18%",
|
||||||
"input": "0 0% 17%",
|
"input": "0 0% 18%",
|
||||||
"ring": "78 90% 55%"
|
"ring": "76 92% 56%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flux-green",
|
||||||
|
"name": "Flux Green",
|
||||||
|
"description": "Newsprint ground with a fluorescent green accent — rave-flyer energy. Ink-black night with a glowing lime-green pop.",
|
||||||
|
"theme": {
|
||||||
|
"mode": "both",
|
||||||
|
"lightColors": {
|
||||||
|
"background": "38 28% 95%",
|
||||||
|
"foreground": "0 0% 8%",
|
||||||
|
"card": "0 0% 100%",
|
||||||
|
"cardForeground": "0 0% 8%",
|
||||||
|
"popover": "0 0% 100%",
|
||||||
|
"popoverForeground": "0 0% 8%",
|
||||||
|
"primary": "0 0% 8%",
|
||||||
|
"primaryForeground": "38 28% 95%",
|
||||||
|
"secondary": "38 18% 90%",
|
||||||
|
"secondaryForeground": "0 0% 8%",
|
||||||
|
"muted": "38 18% 92%",
|
||||||
|
"mutedForeground": "0 0% 40%",
|
||||||
|
"accent": "150 82% 38%",
|
||||||
|
"accentForeground": "0 0% 100%",
|
||||||
|
"destructive": "0 84% 55%",
|
||||||
|
"destructiveForeground": "0 0% 100%",
|
||||||
|
"border": "0 0% 86%",
|
||||||
|
"input": "0 0% 86%",
|
||||||
|
"ring": "150 82% 38%"
|
||||||
|
},
|
||||||
|
"darkColors": {
|
||||||
|
"background": "0 0% 7%",
|
||||||
|
"foreground": "38 28% 95%",
|
||||||
|
"card": "0 0% 11%",
|
||||||
|
"cardForeground": "38 28% 95%",
|
||||||
|
"popover": "0 0% 11%",
|
||||||
|
"popoverForeground": "38 28% 95%",
|
||||||
|
"primary": "38 28% 95%",
|
||||||
|
"primaryForeground": "0 0% 7%",
|
||||||
|
"secondary": "0 0% 14%",
|
||||||
|
"secondaryForeground": "38 28% 95%",
|
||||||
|
"muted": "0 0% 14%",
|
||||||
|
"mutedForeground": "38 10% 62%",
|
||||||
|
"accent": "146 90% 52%",
|
||||||
|
"accentForeground": "0 0% 7%",
|
||||||
|
"destructive": "0 84% 60%",
|
||||||
|
"destructiveForeground": "0 0% 100%",
|
||||||
|
"border": "0 0% 20%",
|
||||||
|
"input": "0 0% 20%",
|
||||||
|
"ring": "146 90% 52%"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "signal-orange",
|
||||||
|
"name": "Signal Orange",
|
||||||
|
"description": "Warm newsprint and a hazard-orange accent — hi-vis workwear energy. Ink-black night with a glowing tangerine pop.",
|
||||||
|
"theme": {
|
||||||
|
"mode": "both",
|
||||||
|
"lightColors": {
|
||||||
|
"background": "38 32% 95%",
|
||||||
|
"foreground": "0 0% 8%",
|
||||||
|
"card": "0 0% 100%",
|
||||||
|
"cardForeground": "0 0% 8%",
|
||||||
|
"popover": "0 0% 100%",
|
||||||
|
"popoverForeground": "0 0% 8%",
|
||||||
|
"primary": "0 0% 8%",
|
||||||
|
"primaryForeground": "38 32% 95%",
|
||||||
|
"secondary": "38 20% 90%",
|
||||||
|
"secondaryForeground": "0 0% 8%",
|
||||||
|
"muted": "38 20% 92%",
|
||||||
|
"mutedForeground": "0 0% 40%",
|
||||||
|
"accent": "18 100% 50%",
|
||||||
|
"accentForeground": "0 0% 100%",
|
||||||
|
"destructive": "0 84% 55%",
|
||||||
|
"destructiveForeground": "0 0% 100%",
|
||||||
|
"border": "0 0% 86%",
|
||||||
|
"input": "0 0% 86%",
|
||||||
|
"ring": "18 100% 50%"
|
||||||
|
},
|
||||||
|
"darkColors": {
|
||||||
|
"background": "0 0% 7%",
|
||||||
|
"foreground": "38 32% 95%",
|
||||||
|
"card": "0 0% 11%",
|
||||||
|
"cardForeground": "38 32% 95%",
|
||||||
|
"popover": "0 0% 11%",
|
||||||
|
"popoverForeground": "38 32% 95%",
|
||||||
|
"primary": "38 32% 95%",
|
||||||
|
"primaryForeground": "0 0% 7%",
|
||||||
|
"secondary": "0 0% 14%",
|
||||||
|
"secondaryForeground": "38 32% 95%",
|
||||||
|
"muted": "0 0% 14%",
|
||||||
|
"mutedForeground": "38 10% 62%",
|
||||||
|
"accent": "22 100% 58%",
|
||||||
|
"accentForeground": "0 0% 7%",
|
||||||
|
"destructive": "0 84% 60%",
|
||||||
|
"destructiveForeground": "0 0% 100%",
|
||||||
|
"border": "0 0% 20%",
|
||||||
|
"input": "0 0% 20%",
|
||||||
|
"ring": "22 100% 58%"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
229
seed/seed.json
Normal file
229
seed/seed.json
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
{
|
||||||
|
"demo": true,
|
||||||
|
"settings": {
|
||||||
|
"merge": {
|
||||||
|
"companyName": "OFFCUT",
|
||||||
|
"tagline": "A streetwear label and its cover stories."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data_tables": [
|
||||||
|
{
|
||||||
|
"key": "contact_submissions",
|
||||||
|
"name": "Studio Inbox",
|
||||||
|
"description": "Seeded contact-form submissions for the OFFCUT demo. A submission becomes a row here and emails the site admins.",
|
||||||
|
"columns": [
|
||||||
|
{ "key": "name", "label": "Name", "type": "text", "required": true },
|
||||||
|
{ "key": "email", "label": "Email", "type": "email", "required": true },
|
||||||
|
{ "key": "subject", "label": "Subject", "type": "text", "required": false },
|
||||||
|
{ "key": "message", "label": "Message", "type": "text", "required": true }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"slug": "/",
|
||||||
|
"title": "OFFCUT",
|
||||||
|
"template_key": "landing",
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"block_key": "magazine-bold:cover_story",
|
||||||
|
"title": "Cover",
|
||||||
|
"slot": "lead",
|
||||||
|
"sort_order": 1,
|
||||||
|
"content": {
|
||||||
|
"kicker": "Drop 04 / Spring",
|
||||||
|
"headline": "Cut from the offcuts",
|
||||||
|
"deck": "One drop. Made from last season's leftover cloth. Nothing reordered.",
|
||||||
|
"link": "/drops",
|
||||||
|
"linkLabel": "See the drop"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"block_key": "magazine-bold:credits_ticker",
|
||||||
|
"title": "Credits",
|
||||||
|
"slot": "lead",
|
||||||
|
"sort_order": 2,
|
||||||
|
"content": { "items": [ { "label": "Photography Kit Sato" }, { "label": "Styling Remi Vaughn" }, { "label": "Cast Ari, Dune, Lux" }, { "label": "Set The Old Print Works" } ] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"block_key": "heading",
|
||||||
|
"title": "River heading",
|
||||||
|
"slot": "river",
|
||||||
|
"sort_order": 1,
|
||||||
|
"content": { "text": "From the studio", "level": 2 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"block_key": "card",
|
||||||
|
"title": "Story: Offcut method",
|
||||||
|
"slot": "river",
|
||||||
|
"sort_order": 2,
|
||||||
|
"content": { "layout": "vertical", "title": "The offcut method", "text": "<p>How we build a full drop from cloth other people threw away.</p>", "link": "/blog/the-offcut-method", "linkLabel": "Read the story" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"block_key": "card",
|
||||||
|
"title": "Story: Casting the block",
|
||||||
|
"slot": "river",
|
||||||
|
"sort_order": 3,
|
||||||
|
"content": { "layout": "vertical", "title": "Casting off the block", "text": "<p>We cast the drop on the street it was cut on. Here is who showed up.</p>", "link": "/blog/casting-off-the-block", "linkLabel": "Read the story" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"block_key": "card",
|
||||||
|
"title": "Story: One colour",
|
||||||
|
"slot": "river",
|
||||||
|
"sort_order": 4,
|
||||||
|
"content": { "layout": "vertical", "title": "One colour, all season", "text": "<p>Why the whole drop runs a single fluoro accent, and nothing else.</p>", "link": "/blog/one-colour-all-season", "linkLabel": "Read the story" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"block_key": "magazine-bold:drop_calendar",
|
||||||
|
"title": "Drops",
|
||||||
|
"slot": "river",
|
||||||
|
"sort_order": 5,
|
||||||
|
"content": {
|
||||||
|
"heading": "Drop calendar",
|
||||||
|
"intro": "Every drop is one run. When it is gone it is gone.",
|
||||||
|
"drops": [
|
||||||
|
{ "date": "14 MAR", "name": "Offcut 04", "detail": "Spring drop. Reworked shell jackets and cargo sets.", "status": "live" },
|
||||||
|
{ "date": "02 MAY", "name": "Offcut 05", "detail": "Workwear capsule. Signed up members first.", "status": "soon" },
|
||||||
|
{ "date": "09 FEB", "name": "Offcut 03", "detail": "Winter drop. Fleece and heavy cotton.", "status": "sold" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"block_key": "newsletter-signup",
|
||||||
|
"title": "Drop alerts",
|
||||||
|
"slot": "river",
|
||||||
|
"sort_order": 6,
|
||||||
|
"content": { "title": "Drop alerts", "description": "One email per drop. We tell you when, and nothing else.", "listSlug": "main", "source": "home", "submitText": "Get alerts", "successMessage": "You are on the list. Watch your inbox before the next drop.", "placeholder": "you@example.com", "layout": "card" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "/about",
|
||||||
|
"title": "About",
|
||||||
|
"template_key": "default",
|
||||||
|
"blocks": [
|
||||||
|
{ "block_key": "heading", "title": "Heading", "slot": "main", "sort_order": 1, "content": { "text": "About OFFCUT", "level": 1 } },
|
||||||
|
{ "block_key": "rich-text", "title": "Bio", "slot": "main", "sort_order": 2, "content": { "html": "<p>OFFCUT is a streetwear label built on leftover cloth. We buy end-of-run rolls that mills cannot sell and cut them into short, numbered drops.</p><p>Every drop is one run. We do not reorder. When a size is gone it stays gone. That keeps the work honest and keeps the waste down.</p><p>The studio is in an old print works. The photography, the lookbook and the label all come out of the same room.</p>" } },
|
||||||
|
{
|
||||||
|
"block_key": "magazine-bold:lookbook",
|
||||||
|
"title": "Lookbook",
|
||||||
|
"slot": "main",
|
||||||
|
"sort_order": 3,
|
||||||
|
"content": {
|
||||||
|
"heading": "Drop 04 lookbook",
|
||||||
|
"intro": "Shot on the block, styled in one colour.",
|
||||||
|
"looks": [
|
||||||
|
{ "caption": "Shell jacket, offcut nylon", "credit": "On Ari" },
|
||||||
|
{ "caption": "Cargo set, reworked cotton", "credit": "On Dune" },
|
||||||
|
{ "caption": "Fleece half-zip", "credit": "On Lux" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ "block_key": "quote", "title": "Quote", "slot": "main", "sort_order": 4, "content": { "style": "block", "align": "center", "quote": "We only make what we can cut from what is already there.", "attribution": "OFFCUT studio" } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "/blog",
|
||||||
|
"title": "Stories",
|
||||||
|
"template_key": "blog-index",
|
||||||
|
"blocks": [
|
||||||
|
{ "block_key": "card", "title": "Featured: Offcut method", "slot": "featured", "sort_order": 1, "content": { "layout": "horizontal", "title": "The offcut method", "text": "<p>How we build a full drop from cloth other people threw away. The lead story this season.</p>", "link": "/blog/the-offcut-method", "linkLabel": "Read the story" } },
|
||||||
|
{ "block_key": "card", "title": "Post: Casting the block", "slot": "main", "sort_order": 1, "content": { "layout": "vertical", "title": "Casting off the block", "text": "<p>We cast the drop on the street it was cut on.</p>", "link": "/blog/casting-off-the-block", "linkLabel": "Read on" } },
|
||||||
|
{ "block_key": "card", "title": "Post: One colour", "slot": "main", "sort_order": 2, "content": { "layout": "vertical", "title": "One colour, all season", "text": "<p>Why the whole drop runs a single fluoro accent.</p>", "link": "/blog/one-colour-all-season", "linkLabel": "Read on" } },
|
||||||
|
{ "block_key": "card", "title": "Post: The offcut method again", "slot": "main", "sort_order": 3, "content": { "layout": "vertical", "title": "Numbering the run", "text": "<p>Every piece is numbered by hand. Here is why that matters.</p>", "link": "/blog/the-offcut-method", "linkLabel": "Read on" } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "/blog/the-offcut-method",
|
||||||
|
"title": "The offcut method",
|
||||||
|
"template_key": "article",
|
||||||
|
"parent_slug": "/blog",
|
||||||
|
"blocks": [
|
||||||
|
{ "block_key": "text", "title": "Byline", "slot": "byline", "sort_order": 1, "content": { "text": "<p>By the OFFCUT studio. Drop 04.</p>" } },
|
||||||
|
{ "block_key": "text", "title": "Lead", "slot": "main", "sort_order": 1, "content": { "text": "<p>Most labels start with a colour card and a factory minimum. We start with a phone call about a roll of cloth that nobody wants. That is the whole method, and it shapes everything after it.</p>" } },
|
||||||
|
{ "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "<p>Mills end every season with rolls they cannot sell. Wrong shade, short length, last year's spec. We buy those rolls cheap and we buy them whole. Then we cut a drop around what we actually have.</p><p>That means the run is short. A roll makes forty jackets, not four hundred. When it is cut, the pattern retires. The next drop uses a different cloth and looks like a different label, because it is a different cloth.</p><p>People ask us to reorder. We cannot, and we would not. The limit is the point.</p>" } },
|
||||||
|
{ "block_key": "quote", "title": "Gutter quote", "slot": "marginalia", "sort_order": 1, "content": { "style": "pull", "align": "left", "quote": "A roll makes forty jackets, not four hundred.", "attribution": "Drop 04" } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "/blog/casting-off-the-block",
|
||||||
|
"title": "Casting off the block",
|
||||||
|
"template_key": "article",
|
||||||
|
"parent_slug": "/blog",
|
||||||
|
"blocks": [
|
||||||
|
{ "block_key": "text", "title": "Byline", "slot": "byline", "sort_order": 1, "content": { "text": "<p>By the OFFCUT studio. Drop 04.</p>" } },
|
||||||
|
{ "block_key": "text", "title": "Lead", "slot": "main", "sort_order": 1, "content": { "text": "<p>We do not book models through an agency. We cast the drop on the street it was cut on, and we shoot the people who show up. Here is how the casting went.</p>" } },
|
||||||
|
{ "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "<p>We put up one poster outside the print works. It gave a date, a time, and nothing else. On the day, thirty people came by. Some knew the label. Most did not.</p><p>We shot everyone in the same set, in the same light, in one colour. No hair, no retouching past the basics. The clothes had to carry the frame, so we let them.</p><p>The cast in the lookbook are the people who came. That is the only rule we kept.</p>" } },
|
||||||
|
{ "block_key": "quote", "title": "Gutter quote", "slot": "marginalia", "sort_order": 1, "content": { "style": "pull", "align": "left", "quote": "We shoot the people who show up.", "attribution": "Casting" } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "/blog/one-colour-all-season",
|
||||||
|
"title": "One colour, all season",
|
||||||
|
"template_key": "article",
|
||||||
|
"parent_slug": "/blog",
|
||||||
|
"blocks": [
|
||||||
|
{ "block_key": "text", "title": "Byline", "slot": "byline", "sort_order": 1, "content": { "text": "<p>By the OFFCUT studio. Drop 04.</p>" } },
|
||||||
|
{ "block_key": "text", "title": "Lead", "slot": "main", "sort_order": 1, "content": { "text": "<p>The whole drop runs one accent colour. It is on the labels, the site, the poster and the tape on the boxes. There is nothing else, and that is on purpose.</p>" } },
|
||||||
|
{ "block_key": "rich-text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "html": "<p>A single colour makes a small label look sure of itself. It also makes the work easy to spot in a feed full of noise. One fluoro hit, black ink, newsprint paper, and you know it is us before you read a word.</p><p>Next drop the colour changes. The system stays. One accent, held all the way through, then swapped clean for the next run.</p>" } },
|
||||||
|
{ "block_key": "quote", "title": "Gutter quote", "slot": "marginalia", "sort_order": 1, "content": { "style": "pull", "align": "left", "quote": "One fluoro hit, black ink, newsprint paper.", "attribution": "House style" } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "/contact",
|
||||||
|
"title": "Studio",
|
||||||
|
"template_key": "contact",
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"block_key": "contact-form",
|
||||||
|
"title": "Write to the studio",
|
||||||
|
"slot": "main",
|
||||||
|
"sort_order": 1,
|
||||||
|
"content": {
|
||||||
|
"heading": "Write to the studio",
|
||||||
|
"description": "Stockist requests, press, or a question about a drop. We read everything.",
|
||||||
|
"submitLabel": "Send",
|
||||||
|
"successMessage": "Thank you. Your note reached the studio. We will write back.",
|
||||||
|
"fields": [
|
||||||
|
{ "name": "name", "label": "Your name", "type": "text", "required": true, "width": "half" },
|
||||||
|
{ "name": "email", "label": "Email", "type": "email", "required": true, "width": "half" },
|
||||||
|
{ "name": "subject", "label": "Subject", "type": "text", "required": false, "width": "full" },
|
||||||
|
{ "name": "message", "label": "Your note", "type": "textarea", "required": true, "width": "full" }
|
||||||
|
],
|
||||||
|
"formConfig": { "enabled": true, "captchaEnabled": false, "targetTable": "contact_submissions" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"block_key": "contact-card",
|
||||||
|
"title": "Other ways to reach us",
|
||||||
|
"slot": "aside",
|
||||||
|
"sort_order": 1,
|
||||||
|
"content": {
|
||||||
|
"heading": "Elsewhere",
|
||||||
|
"description": "The studio inbox is best. These work too.",
|
||||||
|
"columns": 1,
|
||||||
|
"channels": [
|
||||||
|
{ "icon": "lucide:mail", "label": "Email", "value": "studio@offcut.example", "link": "mailto:studio@offcut.example" },
|
||||||
|
{ "icon": "lucide:map-pin", "label": "Studio", "value": "The Old Print Works", "link": "" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "/login",
|
||||||
|
"title": "Members",
|
||||||
|
"template_key": "auth",
|
||||||
|
"blocks": [
|
||||||
|
{ "block_key": "auth-form", "title": "Sign in", "slot": "main", "sort_order": 1, "content": { "default_tab": "login", "show_social": false } },
|
||||||
|
{ "block_key": "text", "title": "Foot", "slot": "footer", "sort_order": 1, "content": { "text": "<p>Members get the next drop before it goes public.</p>" } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"menu_items": [
|
||||||
|
{ "menu": "main", "label": "Home", "page_slug": "/", "sort_order": 1 },
|
||||||
|
{ "menu": "main", "label": "Stories", "page_slug": "/blog", "sort_order": 2 },
|
||||||
|
{ "menu": "main", "label": "About", "page_slug": "/about", "sort_order": 3 },
|
||||||
|
{ "menu": "main", "label": "Studio", "page_slug": "/contact", "sort_order": 4 }
|
||||||
|
]
|
||||||
|
}
|
||||||
19
seed/workflows.json
Normal file
19
seed/workflows.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"workflows": [
|
||||||
|
{
|
||||||
|
"key": "contact_notify_admins",
|
||||||
|
"name": "Studio note to admins",
|
||||||
|
"description": "On a contact-form submission, email the site admins.",
|
||||||
|
"enabled": true,
|
||||||
|
"trigger": { "type": "row_inserted", "table": "contact_submissions" },
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"type": "email",
|
||||||
|
"name": "Email admins",
|
||||||
|
"template": "Form Submission Notification",
|
||||||
|
"recipients": { "mode": "admins", "scope": "all" }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -5,44 +5,59 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="x-apple-disable-message-reformatting">
|
<meta name="x-apple-disable-message-reformatting">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<!--[if mso]>
|
||||||
|
<noscript>
|
||||||
|
<xml>
|
||||||
|
<o:OfficeDocumentSettings>
|
||||||
|
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||||
|
</o:OfficeDocumentSettings>
|
||||||
|
</xml>
|
||||||
|
</noscript>
|
||||||
|
<![endif]-->
|
||||||
<title>{{ site_name }}</title>
|
<title>{{ site_name }}</title>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body, table, td, p, a, li, blockquote { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
|
body, table, td, p, a, li, blockquote {
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-ms-text-size-adjust: 100%;
|
||||||
|
}
|
||||||
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
|
table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
|
||||||
img { -ms-interpolation-mode: bicubic; border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; }
|
img { -ms-interpolation-mode: bicubic; border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; }
|
||||||
body { margin: 0 !important; padding: 0 !important; width: 100% !important; }
|
body { margin: 0 !important; padding: 0 !important; width: 100% !important; }
|
||||||
|
a[x-apple-data-detectors] { color: inherit !important; text-decoration: none !important; }
|
||||||
@media only screen and (max-width: 620px) {
|
@media only screen and (max-width: 620px) {
|
||||||
.mb-email-container { width: 100% !important; max-width: 100% !important; }
|
.email-container { width: 100% !important; max-width: 100% !important; }
|
||||||
.mb-email-pad { padding-left: 24px !important; padding-right: 24px !important; }
|
.content-padding { padding-left: 24px !important; padding-right: 24px !important; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body style="background-color: {{ colors.background|default:"#F7F2EB" }}; margin: 0; padding: 0; font-family: &#39;Inter&#39;, -apple-system, BlinkMacSystemFont, &#39;Segoe UI&#39;, Roboto, sans-serif;">
|
<body style="background-color: {{ colors.background|default:"#f4f1ea" }}; margin: 0; padding: 0; font-family: 'Inter', 'Helvetica Neue', Arial, sans-serif;">
|
||||||
{% if preview_text %}<div style="display: none; max-height: 0; overflow: hidden; mso-hide: all;">{{ preview_text }}</div>{% endif %}
|
{% if preview_text %}<div style="display: none; max-height: 0; overflow: hidden; mso-hide: all;">{{ preview_text }}</div>{% endif %}
|
||||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" style="padding: 48px 10px; background-color: {{ colors.background|default:"#F7F2EB" }};">
|
<td align="center" style="padding: 40px 10px; background-color: {{ colors.background|default:"#f4f1ea" }};">
|
||||||
<table role="presentation" class="mb-email-container" width="600" cellspacing="0" cellpadding="0" border="0" style="max-width: 600px; background-color: {{ colors.card|default:"#FFFFFF" }}; border: 1px solid {{ colors.border|default:"#E0E0E0" }};">
|
<table role="presentation" class="email-container" width="600" cellspacing="0" cellpadding="0" border="0" style="max-width: 600px; background-color: {{ colors.card|default:"#ffffff" }}; border: 2px solid {{ colors.foreground|default:"#111111" }};">
|
||||||
<!-- Wordmark / masthead -->
|
<!-- Masthead wordmark -->
|
||||||
<tr>
|
<tr>
|
||||||
<td align="left" class="mb-email-pad" style="padding: 32px 40px; background-color: {{ colors.card|default:"#FFFFFF" }}; border-bottom: 1px solid {{ colors.border|default:"#E0E0E0" }};">
|
<td align="left" style="padding: 28px 40px 20px 40px; border-bottom: 2px solid {{ colors.foreground|default:"#111111" }};">
|
||||||
{% if logo_url %}<img src="{{ logo_url }}" alt="{{ site_name }}" style="max-height:48px;width:auto;display:block;">{% elif site_name %}<h1 style="margin: 0; font-family: &#39;PP Editorial New&#39;, Georgia, &#39;Times New Roman&#39;, serif; font-style: italic; font-weight: 200; font-size: 42px; line-height: 1; letter-spacing: -0.02em; color: {{ colors.foreground|default:"#141414" }};">{{ site_name }}</h1>{% endif %}
|
{% if logo_url %}<img src="{{ logo_url }}" alt="{{ site_name }}" style="max-height: 40px; width: auto; display: block;">{% elif site_name %}<h1 style="margin: 0; font-family: 'Archivo Black', 'Arial Black', sans-serif; text-transform: uppercase; font-size: 30px; line-height: 1; color: {{ colors.foreground|default:"#111111" }}; letter-spacing: -0.02em;">{{ site_name }}</h1>{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- Ink hairline -->
|
|
||||||
<tr>
|
|
||||||
<td style="height: 1px; line-height: 1px; background-color: {{ colors.foreground|default:"#141414" }};"> </td>
|
|
||||||
</tr>
|
|
||||||
<!-- Body -->
|
<!-- Body -->
|
||||||
<tr>
|
<tr>
|
||||||
<td class="mb-email-pad" style="padding: 40px 48px; background-color: {{ colors.card|default:"#FFFFFF" }}; color: {{ colors.foreground|default:"#141414" }}; font-family: &#39;Inter&#39;, -apple-system, BlinkMacSystemFont, &#39;Segoe UI&#39;, Roboto, sans-serif; font-size: 16px; line-height: 24px;">{{ body|safe }}</td>
|
<td class="content-padding" style="padding: 32px 44px 36px 44px; color: {{ colors.foreground|default:"#111111" }}; font-size: 16px; line-height: 1.6; font-family: 'Inter', 'Helvetica Neue', Arial, sans-serif;">{{ body|safe }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- Mono colophon -->
|
<!-- Footer -->
|
||||||
<tr>
|
<tr>
|
||||||
<td class="mb-email-pad" style="padding: 24px 48px; background-color: {{ colors.muted|default:"#EEE9E0" }}; border-top: 1px solid {{ colors.border|default:"#E0E0E0" }}; font-family: &#39;JetBrains Mono&#39;, &#39;Courier New&#39;, Courier, monospace; font-size: 12px; line-height: 18px; color: {{ colors.mutedForeground|default:"#666666" }}; letter-spacing: 0.08em; text-transform: uppercase;">
|
<td style="padding: 20px 44px 32px 44px; border-top: 2px solid {{ colors.foreground|default:"#111111" }};">
|
||||||
<div>{{ site_name }}</div>
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0">
|
||||||
{% if site_url %}<div style="margin-top:8px;"><a href="{{ site_url }}" style="color: {{ colors.mutedForeground|default:"#666666" }}; text-decoration: underline;">{{ site_url }}</a></div>{% endif %}
|
<tr>
|
||||||
{% if unsubscribe_url %}<div style="margin-top:8px;"><a href="{{ unsubscribe_url }}" style="color: {{ colors.mutedForeground|default:"#666666" }}; text-decoration: underline;">Unsubscribe</a></div>{% endif %}
|
<td align="left" style="font-family: 'IBM Plex Mono', 'Courier New', monospace; font-size: 12px; color: {{ colors.mutedForeground|default:"#6b6b6b" }}; text-transform: uppercase; letter-spacing: 0.1em;">
|
||||||
|
<p style="margin: 0 0 8px;">{{ site_name }}</p>
|
||||||
|
{% if site_url %}<p style="margin: 0 0 8px;"><a href="{{ site_url }}" style="color: {{ colors.accent|default:"#ec0f6e" }}; text-decoration: none; border-bottom: 2px solid {{ colors.accent|default:"#ec0f6e" }};">{{ site_url }}</a></p>{% endif %}
|
||||||
|
{% if unsubscribe_url %}<p style="margin: 12px 0 0; font-size: 11px;"><a href="{{ unsubscribe_url }}" style="color: {{ colors.mutedForeground|default:"#6b6b6b" }}; text-decoration: underline;">Unsubscribe</a></p>{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@ -1,20 +1,16 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
|
||||||
{{ head_html|safe }}
|
{{ head_html|safe }}
|
||||||
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col font-sans">
|
<body class="antialiased min-h-screen flex flex-col bg-background text-foreground" style="font-family: var(--font-body, "Inter", system-ui, sans-serif);">
|
||||||
|
<div class="mb-progress" aria-hidden="true"></div>
|
||||||
{{ admin_banner_html|safe }}
|
{{ admin_banner_html|safe }}
|
||||||
<header class="w-full mb-hairline-bottom">
|
{% if slots.header %}<header class="w-full">{{ slots.header|safe }}</header>{% endif %}
|
||||||
<div class="max-w-6xl mx-auto px-6">{{ slots.masthead|safe }}</div>
|
{% if slots.byline %}<div class="mb-column w-full pt-6 pb-2">{{ slots.byline|safe }}</div>{% endif %}
|
||||||
</header>
|
<div class="mb-article-grid flex-grow py-8">
|
||||||
<section class="w-full">
|
<main class="mb-dropcap mb-prose">{% if slots.main %}{{ slots.main|safe }}{% else %}<p style="color: hsl(var(--muted-foreground));">This essay has no content yet.</p>{% endif %}</main>
|
||||||
<div class="max-w-6xl mx-auto px-6 py-10">{{ slots.deck|safe }}</div>
|
<aside class="mb-marginalia">{{ slots.marginalia|safe }}</aside>
|
||||||
</section>
|
</div>
|
||||||
<main class="flex-grow w-full">
|
{% if slots.footer %}<footer class="w-full mt-auto">{{ slots.footer|safe }}</footer>{% endif %}
|
||||||
<div class="max-w-4xl mx-auto px-6 py-12">{% if slots.main %}<article class="mb-body">{{ slots.main|safe }}</article>{% else %}<div class="py-20 text-center font-mono mb-caption">No content blocks assigned to this page.</div>{% endif %}</div>
|
|
||||||
</main>
|
|
||||||
<footer class="w-full mt-auto mb-hairline-top">
|
|
||||||
<div class="max-w-6xl mx-auto px-6">{{ slots.colophon|safe }}</div>
|
|
||||||
</footer>
|
|
||||||
{{ body_end_html|safe }}
|
{{ body_end_html|safe }}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
18
templates/magazine-bold/auth.ninjatpl
Normal file
18
templates/magazine-bold/auth.ninjatpl
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
|
||||||
|
{{ head_html|safe }}
|
||||||
|
<body class="antialiased min-h-screen flex flex-col bg-background text-foreground" style="font-family: var(--font-body, "Inter", system-ui, sans-serif);">
|
||||||
|
{{ admin_banner_html|safe }}
|
||||||
|
<main class="flex-grow w-full flex flex-col items-center justify-center px-4 py-12">
|
||||||
|
<div class="w-full max-w-md">
|
||||||
|
<a href="/" class="block text-center" aria-label="{{ site_settings.Title }} home">
|
||||||
|
{% if site_settings.Logo %}<img src="{{ site_settings.Logo }}" alt="{{ site_settings.LogoAlt }}" class="mx-auto h-12 w-auto">{% else %}<span class="mb-masthead-wordmark text-3xl">{{ site_settings.Title|default:"Magazine Bold" }}</span>{% endif %}
|
||||||
|
</a>
|
||||||
|
<hr class="mb-rule mx-auto mt-4 mb-8" style="max-width:10rem;">
|
||||||
|
<div>{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="py-20 text-center"><p class="italic" style="color:hsl(var(--muted-foreground));">No form assigned to this page.</p></div>{% endif %}</div>
|
||||||
|
{% if slots.footer %}<div class="mt-6 text-center text-sm" style="color:hsl(var(--muted-foreground));">{{ slots.footer|safe }}</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
{{ body_end_html|safe }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
23
templates/magazine-bold/blog-index.ninjatpl
Normal file
23
templates/magazine-bold/blog-index.ninjatpl
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
|
||||||
|
{{ head_html|safe }}
|
||||||
|
<body class="antialiased min-h-screen flex flex-col bg-background text-foreground" style="font-family: var(--font-body, "Inter", system-ui, sans-serif);">
|
||||||
|
{{ admin_banner_html|safe }}
|
||||||
|
{% if slots.header %}<header class="w-full">{{ slots.header|safe }}</header>{% endif %}
|
||||||
|
<main class="flex-grow w-full">
|
||||||
|
<div class="mb-wide py-10 md:py-14">
|
||||||
|
{% if title %}
|
||||||
|
<div class="text-center">
|
||||||
|
<h1 class="mb-heading text-4xl md:text-5xl">{{ title }}</h1>
|
||||||
|
{% if page_meta.MetaDescription %}<p class="mt-3 italic" style="color:hsl(var(--muted-foreground));">{{ page_meta.MetaDescription }}</p>{% endif %}
|
||||||
|
<hr class="mb-rule mx-auto mt-6" style="max-width:8rem;">
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if slots.featured %}<section class="mt-12">{{ slots.featured|safe }}</section>{% endif %}
|
||||||
|
{% if slots.main %}<section class="mt-12">{{ slots.main|safe }}</section>{% else %}<div class="py-20 text-center"><p class="italic" style="color:hsl(var(--muted-foreground));">No essays published yet.</p></div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
{% if slots.footer %}<footer class="w-full mt-auto">{{ slots.footer|safe }}</footer>{% endif %}
|
||||||
|
{{ body_end_html|safe }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
22
templates/magazine-bold/contact.ninjatpl
Normal file
22
templates/magazine-bold/contact.ninjatpl
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
|
||||||
|
{{ head_html|safe }}
|
||||||
|
<body class="antialiased min-h-screen flex flex-col bg-background text-foreground" style="font-family: var(--font-body, "Inter", system-ui, sans-serif);">
|
||||||
|
{{ admin_banner_html|safe }}
|
||||||
|
{% if slots.header %}<header class="w-full">{{ slots.header|safe }}</header>{% endif %}
|
||||||
|
<main class="flex-grow w-full">
|
||||||
|
<div class="mb-wide py-10 md:py-14">
|
||||||
|
{% if slots.aside %}
|
||||||
|
<div class="grid gap-12 md:grid-cols-[2fr_1fr]">
|
||||||
|
<div>{% if slots.main %}{{ slots.main|safe }}{% endif %}</div>
|
||||||
|
<aside class="w-full md:border-l md:border-[hsl(var(--border))] md:pl-10">{{ slots.aside|safe }}</aside>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="mx-auto max-w-2xl">{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="py-20 text-center"><p class="italic" style="color:hsl(var(--muted-foreground));">No content blocks assigned to this page.</p></div>{% endif %}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
{% if slots.footer %}<footer class="w-full mt-auto">{{ slots.footer|safe }}</footer>{% endif %}
|
||||||
|
{{ body_end_html|safe }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -1,17 +1,13 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
|
||||||
{{ head_html|safe }}
|
{{ head_html|safe }}
|
||||||
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col font-sans">
|
<body class="antialiased min-h-screen flex flex-col bg-background text-foreground" style="font-family: var(--font-body, "Inter", system-ui, sans-serif);">
|
||||||
{{ admin_banner_html|safe }}
|
{{ admin_banner_html|safe }}
|
||||||
<header class="w-full mb-hairline-bottom">
|
{% if slots.header %}<header class="w-full">{{ slots.header|safe }}</header>{% endif %}
|
||||||
<div class="max-w-6xl mx-auto px-6">{{ slots.masthead|safe }}</div>
|
<main class="flex-grow w-full py-10 md:py-14">
|
||||||
</header>
|
{% if slots.main %}<div class="mb-column mb-prose">{{ slots.main|safe }}</div>{% else %}<div class="mb-column py-20 text-center"><p style="color: hsl(var(--muted-foreground));">No content blocks assigned to this page.</p></div>{% endif %}
|
||||||
<main class="flex-grow w-full">
|
|
||||||
<div class="max-w-6xl mx-auto px-6 py-12">{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="py-20 text-center font-mono mb-caption">No content blocks assigned to this page.</div>{% endif %}</div>
|
|
||||||
</main>
|
</main>
|
||||||
<footer class="w-full mt-auto mb-hairline-top">
|
{% if slots.footer %}<footer class="w-full mt-auto">{{ slots.footer|safe }}</footer>{% endif %}
|
||||||
<div class="max-w-6xl mx-auto px-6">{{ slots.colophon|safe }}</div>
|
|
||||||
</footer>
|
|
||||||
{{ body_end_html|safe }}
|
{{ body_end_html|safe }}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -1,17 +1,11 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
|
||||||
{{ head_html|safe }}
|
{{ head_html|safe }}
|
||||||
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col font-sans">
|
<body class="antialiased min-h-screen flex flex-col bg-background text-foreground" style="font-family: var(--font-body, "Inter", system-ui, sans-serif);">
|
||||||
{{ admin_banner_html|safe }}
|
{{ admin_banner_html|safe }}
|
||||||
<header class="w-full mb-hairline-bottom">
|
{% if slots.header %}<header class="w-full">{{ slots.header|safe }}</header>{% endif %}
|
||||||
<div class="max-w-6xl mx-auto px-6">{{ slots.masthead|safe }}</div>
|
<main class="flex-grow w-full">{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="mb-wide py-20 text-center"><p style="color: hsl(var(--muted-foreground));">No content blocks assigned to this page.</p></div>{% endif %}</main>
|
||||||
</header>
|
{% if slots.footer %}<footer class="w-full mt-auto">{{ slots.footer|safe }}</footer>{% endif %}
|
||||||
<main class="flex-grow w-full">
|
|
||||||
{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="max-w-4xl mx-auto py-20 px-6 text-center font-mono mb-caption">No content blocks assigned to this page.</div>{% endif %}
|
|
||||||
</main>
|
|
||||||
<footer class="w-full mt-auto mb-hairline-top">
|
|
||||||
<div class="max-w-6xl mx-auto px-6">{{ slots.colophon|safe }}</div>
|
|
||||||
</footer>
|
|
||||||
{{ body_end_html|safe }}
|
{{ body_end_html|safe }}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -1,18 +1,14 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en" class="{% if theme_mode == "dark" %}dark{% endif %}">
|
||||||
{{ head_html|safe }}
|
{{ head_html|safe }}
|
||||||
<body class="bg-background text-foreground antialiased min-h-screen flex flex-col font-sans">
|
<body class="antialiased min-h-screen flex flex-col bg-background text-foreground" style="font-family: var(--font-body, "Inter", system-ui, sans-serif);">
|
||||||
{{ admin_banner_html|safe }}
|
{{ admin_banner_html|safe }}
|
||||||
<section class="w-full">{{ slots.cover|safe }}</section>
|
{% if slots.masthead %}<header class="w-full">{{ slots.masthead|safe }}</header>{% endif %}
|
||||||
<section class="w-full border-t border-b mb-hairline">
|
|
||||||
<div class="max-w-6xl mx-auto px-6 py-10">{{ slots.secondary|safe }}</div>
|
|
||||||
</section>
|
|
||||||
<main class="flex-grow w-full">
|
<main class="flex-grow w-full">
|
||||||
<div class="max-w-6xl mx-auto px-6 py-12">{% if slots.main %}{{ slots.main|safe }}{% else %}<div class="py-20 text-center font-mono mb-caption">No content blocks assigned to this page.</div>{% endif %}</div>
|
{% if slots.lead %}<section class="mb-wide py-10 md:py-14">{{ slots.lead|safe }}</section>{% endif %}
|
||||||
|
{% if slots.river %}<section class="mb-wide pb-10 md:pb-16"><hr class="mb-rule mb-10">{{ slots.river|safe }}</section>{% endif %}
|
||||||
</main>
|
</main>
|
||||||
<footer class="w-full mt-auto mb-hairline-top">
|
{% if slots.footer %}<footer class="w-full mt-auto">{{ slots.footer|safe }}</footer>{% endif %}
|
||||||
<div class="max-w-6xl mx-auto px-6">{{ slots.colophon|safe }}</div>
|
|
||||||
</footer>
|
|
||||||
{{ body_end_html|safe }}
|
{{ body_end_html|safe }}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
21
templates/overrides/magazine-bold/announcement-bar.ninjatpl
Normal file
21
templates/overrides/magazine-bold/announcement-bar.ninjatpl
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{# announcement-bar override — quiet notice on a hairline panel, small-caps message. Dismiss <script>, data-bn-annkey and localStorage keying preserved verbatim. Fields mirror builtin: variant, message, linkText, linkUrl, dismissable. #}
|
||||||
|
{% set anncls = "mb-panel" %}
|
||||||
|
{% if variant == "promo" %}{% set anncls = "mb-panel" %}{% elif variant == "alert" %}{% set anncls = "bg-destructive text-destructive-foreground" %}{% endif %}
|
||||||
|
<div data-block-override="magazine-bold:announcement-bar" data-bn-announcement data-bn-annkey="{{ variant|default:'info' }}:{{ message|slugify }}" class="bn-announcement w-full border-b border-[hsl(var(--border))] {{ anncls }} {{ class }}">
|
||||||
|
<div class="max-w-7xl mx-auto px-10 py-2 relative flex items-center justify-center gap-2 text-sm text-center">
|
||||||
|
<p class="mb-caps text-xs">{{ message }}{% if linkText and linkUrl %} <a href="{{ linkUrl }}" class="underline underline-offset-2 hover:opacity-80">{{ linkText }}</a>{% endif %}</p>
|
||||||
|
{% if dismissable %}<button type="button" data-bn-ann-dismiss aria-label="Dismiss announcement" class="absolute right-3 top-1/2 -translate-y-1/2 inline-flex items-center justify-center p-1 rounded hover:bg-black/10 transition-colors"><svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg></button>{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if dismissable %}<script>
|
||||||
|
(function(){
|
||||||
|
var bars=document.querySelectorAll('[data-bn-announcement]:not([data-bn-ann-wired])');
|
||||||
|
bars.forEach(function(bar){
|
||||||
|
bar.setAttribute('data-bn-ann-wired','1');
|
||||||
|
var key='bnann:'+bar.getAttribute('data-bn-annkey');
|
||||||
|
try{if(localStorage.getItem(key)==='1'){bar.style.display='none';return;}}catch(e){}
|
||||||
|
var btn=bar.querySelector('[data-bn-ann-dismiss]');
|
||||||
|
if(btn)btn.addEventListener('click',function(){bar.style.display='none';try{localStorage.setItem(key,'1');}catch(e){}});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>{% endif %}
|
||||||
16
templates/overrides/magazine-bold/audio-embed.ninjatpl
Normal file
16
templates/overrides/magazine-bold/audio-embed.ninjatpl
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{# audio-embed override — magazine-bold hairline player card. Fields + facade JS mirror builtin verbatim: url, media, artwork, title, artist, download. #}
|
||||||
|
<figure data-block-override="magazine-bold:audio-embed" class="bn-audio mb-panel my-8 overflow-hidden bg-card text-card-foreground{% if class %} {{ class }}{% endif %}" data-bn-audio data-audio-url="{{ url|default:"" }}" data-audio-media="{% if media %}{{ media|mediaURL }}{% endif %}"><div class="flex items-center gap-4 p-4">{% if artwork %}<div class="h-16 w-16 shrink-0 overflow-hidden bg-muted">{% img artwork alt=title|default:"" class="h-full w-full object-cover" %}</div>{% endif %}<div class="min-w-0 flex-1">{% if title %}<p class="mb-caps truncate text-sm" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--foreground));">{{ title }}</p>{% endif %}{% if artist %}<p class="truncate text-sm italic" style="color:hsl(var(--muted-foreground));">{{ artist }}</p>{% endif %}<div class="mt-2 flex items-center gap-3" data-bn-audio-slot><button type="button" data-bn-audio-play class="mb-button inline-flex items-center gap-2 text-sm" aria-label="Play{% if title %}: {{ title }}{% endif %}">::lucide:play:sm:: Play</button>{% if download %}<a href="{% if media %}{{ media|mediaURL }}{% else %}{{ url }}{% endif %}" download class="mb-link inline-flex items-center gap-1 text-sm">::lucide:download:sm:: Download</a>{% endif %}</div></div></div></figure><script>
|
||||||
|
(function(){
|
||||||
|
if(window.__bnAudioFacade)return;window.__bnAudioFacade=true;
|
||||||
|
document.addEventListener('click',function(e){
|
||||||
|
var btn=e.target.closest('[data-bn-audio-play]');if(!btn)return;
|
||||||
|
var fig=btn.closest('[data-bn-audio]');if(!fig)return;
|
||||||
|
e.preventDefault();
|
||||||
|
var src=fig.getAttribute('data-audio-media')||fig.getAttribute('data-audio-url')||'';
|
||||||
|
if(!src)return;
|
||||||
|
var slot=btn.closest('[data-bn-audio-slot]');
|
||||||
|
var a=document.createElement('audio');a.src=src;a.controls=true;a.autoplay=true;a.preload='none';a.style.cssText='width:100%;min-width:12rem;height:2.25rem;';
|
||||||
|
btn.remove();slot.insertBefore(a,slot.firstChild);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
33
templates/overrides/magazine-bold/auth-form.ninjatpl
Normal file
33
templates/overrides/magazine-bold/auth-form.ninjatpl
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{# auth-form (magazine-bold) — typographic login/register in an mb-panel with small-caps labels, hairline tab rules and fluoro active tab. Wiring UNTOUCHABLE: single shared #auth-message, /api/auth/login (email,password) and /api/auth/register (username,email,password), hx-target #auth-message, captcha injected from _captcha_widget. #}
|
||||||
|
{% if context.isPublicLoggedIn %}
|
||||||
|
<div data-block-override="magazine-bold:auth-form" class="auth-form-logged-in mx-auto max-w-md text-center py-8">
|
||||||
|
<p class="italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Inter', system-ui, sans-serif);">You are signed in as <strong style="color:hsl(var(--foreground));">{{ context.publicUsername }}</strong></p>
|
||||||
|
<a href="/account" class="mb-link mt-3 inline-block">My account</a>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div data-block-override="magazine-bold:auth-form" class="auth-form mb-panel mx-auto max-w-md bg-card p-8">
|
||||||
|
<div id="auth-message"></div>
|
||||||
|
<div class="mb-6 flex border-b border-[hsl(var(--border))]">
|
||||||
|
<button type="button" onclick="document.getElementById('login-panel').classList.remove('hidden');document.getElementById('register-panel').classList.add('hidden');this.setAttribute('data-active','1');this.nextElementSibling.removeAttribute('data-active');" data-auth-tab {% if default_tab != "register" %}data-active="1"{% endif %} class="mb-caps flex-1 py-2 text-center text-xs border-b-2 border-transparent data-[active]:border-[hsl(var(--accent))] data-[active]:text-[hsl(var(--accent))]">Sign in</button>
|
||||||
|
<button type="button" onclick="document.getElementById('register-panel').classList.remove('hidden');document.getElementById('login-panel').classList.add('hidden');this.setAttribute('data-active','1');this.previousElementSibling.removeAttribute('data-active');" data-auth-tab {% if default_tab == "register" %}data-active="1"{% endif %} class="mb-caps flex-1 py-2 text-center text-xs border-b-2 border-transparent data-[active]:border-[hsl(var(--accent))] data-[active]:text-[hsl(var(--accent))]">Register</button>
|
||||||
|
</div>
|
||||||
|
<div id="login-panel" class="{% if default_tab == "register" %}hidden{% endif %}">
|
||||||
|
<form hx-post="/api/auth/login" hx-target="#auth-message" hx-swap="innerHTML">
|
||||||
|
<div class="mb-3"><label class="mb-caps text-muted-foreground block text-xs mb-1">Email</label><input type="email" name="email" required class="w-full mb-input" /></div>
|
||||||
|
<div class="mb-4"><label class="mb-caps text-muted-foreground block text-xs mb-1">Password</label><input type="password" name="password" required class="w-full mb-input" /></div>
|
||||||
|
{% if _captcha_widget %}{{ _captcha_widget|safe }}{% endif %}
|
||||||
|
<button type="submit" class="mb-button w-full" hx-disabled-elt="this">Sign in</button>
|
||||||
|
<p class="text-center mt-3 text-sm"><a href="/forgot-password" class="mb-link">Forgot password?</a></p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div id="register-panel" class="{% if default_tab != "register" %}hidden{% endif %}">
|
||||||
|
<form hx-post="/api/auth/register" hx-target="#auth-message" hx-swap="innerHTML">
|
||||||
|
<div class="mb-3"><label class="mb-caps text-muted-foreground block text-xs mb-1">Username</label><input type="text" name="username" required pattern="[a-zA-Z0-9_-]{3,30}" class="w-full mb-input" /></div>
|
||||||
|
<div class="mb-3"><label class="mb-caps text-muted-foreground block text-xs mb-1">Email</label><input type="email" name="email" required class="w-full mb-input" /></div>
|
||||||
|
<div class="mb-4"><label class="mb-caps text-muted-foreground block text-xs mb-1">Password</label><input type="password" name="password" required minlength="8" class="w-full mb-input" /></div>
|
||||||
|
{% if _captcha_widget %}{{ _captcha_widget|safe }}{% endif %}
|
||||||
|
<button type="submit" class="mb-button w-full" hx-disabled-elt="this">Create account</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
31
templates/overrides/magazine-bold/auth-status.ninjatpl
Normal file
31
templates/overrides/magazine-bold/auth-status.ninjatpl
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{# auth-status (magazine-bold) — header login state; small-caps links, hairline monogram and quiet mb-panel dropdown. Driven by context.isPublicLoggedIn / context.publicDisplayName / context.publicUsername; logout posts /api/auth/logout. #}
|
||||||
|
{% if context.isPublicLoggedIn %}
|
||||||
|
<div data-block-override="magazine-bold:auth-status" class="auth-status relative">
|
||||||
|
<button type="button" onclick="this.nextElementSibling.classList.toggle('hidden')" class="mb-caps flex items-center gap-2 text-xs opacity-90 hover:opacity-100 px-3 py-1.5 transition-colors">
|
||||||
|
<span class="inline-flex h-7 w-7 items-center justify-center rounded-full text-[0.7rem] font-bold" style="border:1px solid hsl(var(--border));color:hsl(var(--accent));">{{ context.publicDisplayName|default:context.publicUsername|first|upper }}</span>
|
||||||
|
<span>{{ context.publicDisplayName|default:context.publicUsername }}</span>
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
|
||||||
|
</button>
|
||||||
|
<div id="auth-dropdown-menu" class="hidden absolute right-0 top-full mt-1 w-48 mb-panel bg-card py-1 z-50">
|
||||||
|
<a href="/account" class="block px-4 py-2 text-sm text-foreground hover:bg-accent">My profile</a>
|
||||||
|
<a href="/account/reviews" class="block px-4 py-2 text-sm text-foreground hover:bg-accent">My reviews</a>
|
||||||
|
<hr class="my-1 mb-rule"/>
|
||||||
|
<button hx-post="/api/auth/logout" hx-swap="none" class="block w-full text-left px-4 py-2 text-sm text-destructive hover:bg-accent">Sign out</button>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
document.addEventListener('click', function(e) {
|
||||||
|
var menu = document.getElementById('auth-dropdown-menu');
|
||||||
|
var btn = document.querySelector('.auth-status > button');
|
||||||
|
if (!menu || !btn) return;
|
||||||
|
if (!btn.contains(e.target) && !menu.contains(e.target)) { menu.classList.add('hidden'); }
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div data-block-override="magazine-bold:auth-status" class="auth-status flex items-center gap-2">
|
||||||
|
<a href="/login" class="mb-caps text-xs px-3 py-1.5 opacity-80 hover:opacity-100 hover:text-[hsl(var(--accent))] transition-colors">Sign in</a>
|
||||||
|
<a href="/login?tab=register" class="mb-button text-xs">Reserve</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
22
templates/overrides/magazine-bold/author-bio-hero.ninjatpl
Normal file
22
templates/overrides/magazine-bold/author-bio-hero.ninjatpl
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{# author-bio-hero (magazine-bold) — author-page header framed by hairline rules over generous whitespace. #}
|
||||||
|
{% if author %}
|
||||||
|
<section data-block-override="magazine-bold:author-bio-hero" class="mb-rule bg-background py-12 text-foreground md:py-16{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto flex max-w-3xl flex-col items-center px-4 text-center">
|
||||||
|
{% if showAvatar and author.avatar_url %}<div class="mb-6 h-24 w-24 overflow-hidden rounded-full bg-muted ring-1 ring-[hsl(var(--border))]">{% img author.avatar_url alt=author.display_name class="h-full w-full object-cover" %}</div>{% endif %}
|
||||||
|
<div class="mb-4 flex items-center justify-center gap-4"><span class="mb-rule w-12"></span><span class="mb-rule w-12"></span></div>
|
||||||
|
<h1 class="mb-caps text-3xl md:text-4xl text-foreground" style="font-family:var(--font-heading, "Archivo Black", 'Arial Black', sans-serif)">{{ author.display_name }}</h1>
|
||||||
|
{% if author.job_title or author.company %}<p class="mb-caps mt-2 text-sm text-muted-foreground">{{ author.job_title }}{% if author.job_title and author.company %} · {% endif %}{{ author.company }}</p>{% endif %}
|
||||||
|
{% if author.bio %}<p class="mt-5 max-w-2xl leading-relaxed text-muted-foreground" style="font-family:var(--font-body, "Inter", system-ui, sans-serif)">{{ author.bio }}</p>{% endif %}
|
||||||
|
<div class="mt-6 flex flex-wrap items-center justify-center gap-4 text-sm text-muted-foreground">
|
||||||
|
{% if showPostCount %}<span>{{ author.post_count|default:0 }} post{% if author.post_count != 1 %}s{% endif %}</span>{% endif %}
|
||||||
|
{% if author.location %}<span>{{ author.location }}</span>{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if showSocial %}<div class="mt-6 flex items-center justify-center gap-4">
|
||||||
|
{% if author.website_url %}<a href="{{ author.website_url }}" target="_blank" rel="noopener noreferrer" class="text-muted-foreground transition-colors hover:text-[hsl(var(--accent))]" title="Website"><svg class="h-5 w-5" aria-hidden="true"><use href="/icons/lucide.svg#globe"></use></svg></a>{% endif %}
|
||||||
|
{% if author.twitter_handle %}<a href="https://twitter.com/{{ author.twitter_handle|cut:"@" }}" target="_blank" rel="noopener noreferrer" class="text-muted-foreground transition-colors hover:text-[hsl(var(--accent))]" title="Twitter"><svg class="h-5 w-5" aria-hidden="true"><use href="/icons/simple-icons.svg#x"></use></svg></a>{% endif %}
|
||||||
|
{% if author.linkedin_url %}<a href="{{ author.linkedin_url }}" target="_blank" rel="noopener noreferrer" class="text-muted-foreground transition-colors hover:text-[hsl(var(--accent))]" title="LinkedIn"><svg class="h-5 w-5" aria-hidden="true"><use href="/icons/simple-icons.svg#linkedin"></use></svg></a>{% endif %}
|
||||||
|
{% if author.github_url %}<a href="{{ author.github_url }}" target="_blank" rel="noopener noreferrer" class="text-muted-foreground transition-colors hover:text-[hsl(var(--accent))]" title="GitHub"><svg class="h-5 w-5" aria-hidden="true"><use href="/icons/simple-icons.svg#github"></use></svg></a>{% endif %}
|
||||||
|
</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
14
templates/overrides/magazine-bold/author-bio.ninjatpl
Normal file
14
templates/overrides/magazine-bold/author-bio.ninjatpl
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{# author-bio (magazine-bold) — byline card in an mb-panel with hairline rule and small-caps label. #}
|
||||||
|
{% if post.author_name %}
|
||||||
|
<aside data-block-override="magazine-bold:author-bio" class="mb-panel bg-card p-6 text-card-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
{% if heading %}<p class="mb-caps mb-4 text-xs text-muted-foreground">{{ heading }}</p>{% endif %}
|
||||||
|
<div class="flex items-start gap-4">
|
||||||
|
{% if showAvatar and post.author_avatar %}<div class="h-14 w-14 flex-shrink-0 overflow-hidden rounded-full bg-muted ring-1 ring-[hsl(var(--border))]">{% img post.author_avatar alt=post.author_name class="h-full w-full object-cover" %}</div>{% endif %}
|
||||||
|
<div class="min-w-0">
|
||||||
|
<h3 class="mb-caps text-base text-card-foreground" style="font-family:var(--font-heading, "Archivo Black", 'Arial Black', sans-serif)">{% if post.author_slug %}<a href="/author/{{ post.author_slug }}" class="transition-colors hover:text-[hsl(var(--accent))]">{{ post.author_name }}</a>{% else %}{{ post.author_name }}{% endif %}</h3>
|
||||||
|
<span class="mb-rule mt-2 block w-10"></span>
|
||||||
|
{% if post.author_bio %}<p class="mt-2 text-sm text-muted-foreground" style="font-family:var(--font-body, "Inter", system-ui, sans-serif)">{{ post.author_bio }}</p>{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
{% endif %}
|
||||||
24
templates/overrides/magazine-bold/blog-hero.ninjatpl
Normal file
24
templates/overrides/magazine-bold/blog-hero.ninjatpl
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{# blog-hero (magazine-bold) — blog-index header + featured posts from the `posts` provider; typographic, hairline rules. #}
|
||||||
|
<section data-block-override="magazine-bold:blog-hero" class="bg-background py-12 text-foreground md:py-16{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-6xl px-4">
|
||||||
|
<div class="mx-auto max-w-2xl text-center">
|
||||||
|
{% if title %}<h1 class="mb-caps mb-underline text-4xl md:text-5xl text-foreground" style="font-family:var(--font-heading, "Archivo Black", 'Arial Black', sans-serif)">{{ title }}</h1>{% endif %}
|
||||||
|
{% if subtitle %}<p class="mt-4 text-lg text-muted-foreground" style="font-family:var(--font-body, "Inter", system-ui, sans-serif)">{{ subtitle }}</p>{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if posts %}
|
||||||
|
<div class="mt-12 grid grid-cols-1 gap-8 md:grid-cols-3">
|
||||||
|
{% for post in posts %}
|
||||||
|
<article class="group flex flex-col{% if forloop.First %} md:col-span-3 md:flex-row md:items-stretch md:gap-8{% endif %}">
|
||||||
|
{% if post.featured_image_url %}<a href="{{ post.url }}" class="mb-4 block overflow-hidden bg-muted{% if forloop.First %} md:mb-0 md:w-1/2{% endif %}">{% img post.featured_image_url alt=post.title class="aspect-video h-full w-full object-cover transition-transform duration-500 group-hover:scale-105" %}</a>{% endif %}
|
||||||
|
<div class="flex flex-col justify-center{% if forloop.First %} md:w-1/2{% endif %}">
|
||||||
|
<span class="mb-rule mb-4 block w-12"></span>
|
||||||
|
<h2 class="mb-caps {% if forloop.First %}text-2xl{% else %}text-lg{% endif %} leading-snug text-foreground" style="font-family:var(--font-heading, "Archivo Black", 'Arial Black', sans-serif)"><a href="{{ post.url }}" class="transition-colors hover:text-[hsl(var(--accent))]">{{ post.title }}</a></h2>
|
||||||
|
{% if showExcerpt and post.excerpt %}<p class="mt-3 text-sm text-muted-foreground" style="font-family:var(--font-body, "Inter", system-ui, sans-serif)">{{ post.excerpt }}</p>{% endif %}
|
||||||
|
<div class="mt-4 flex flex-wrap items-center gap-x-3 text-xs text-muted-foreground">{% if post.author_name %}<span class="mb-caps text-[hsl(var(--accent))]">{{ post.author_name }}</span>{% endif %}{% if post.published_at %}<span>{{ post.published_at|date:"M j, Y" }}</span>{% endif %}</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
13
templates/overrides/magazine-bold/breadcrumbs.ninjatpl
Normal file
13
templates/overrides/magazine-bold/breadcrumbs.ninjatpl
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{# breadcrumbs (magazine-bold) — small-caps trail with hairline fluoro separators; current item plain. #}
|
||||||
|
{% set arrows = template == "arrows" %}
|
||||||
|
<nav aria-label="Breadcrumb" data-block-override="magazine-bold:breadcrumbs" class="py-3{% if class %} {{ class }}{% endif %}">
|
||||||
|
<ol class="flex flex-wrap items-center gap-1 text-xs">
|
||||||
|
{% if showHome %}<li class="flex items-center"><a href="/" class="mb-caps text-muted-foreground transition-colors hover:text-[hsl(var(--accent))]">{{ homeLabel|default:"Home" }}</a></li>{% endif %}
|
||||||
|
{% for item in breadcrumbs %}
|
||||||
|
<li class="flex items-center">
|
||||||
|
{% if showHome or not forloop.First %}{% if arrows %}<svg class="mx-2 h-3.5 w-3.5 text-[hsl(var(--accent))]" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>{% else %}<span class="mx-2 text-[hsl(var(--accent))]">{{ separator|default:"/" }}</span>{% endif %}{% endif %}
|
||||||
|
{% if item.is_current %}{% if showCurrent %}<span class="mb-caps text-foreground" aria-current="page">{{ item.label }}</span>{% endif %}{% else %}<a href="{{ item.url }}" class="mb-caps text-muted-foreground transition-colors hover:text-[hsl(var(--accent))]">{{ item.label }}</a>{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
@ -1 +1,10 @@
|
|||||||
{% set _label = label|default:text %}{% set _url = url|default:href %}{% if _label %}<a href="{{ _url }}" class="mb-button mb-button-{% if variant == "secondary" %}secondary{% elif variant == "destructive" %}destructive{% else %}primary{% endif %}" data-variant="{% if variant == "secondary" %}secondary{% elif variant == "destructive" %}destructive{% else %}primary{% endif %}">{{ _label }}</a>{% endif %}
|
{# button override — hairline uppercase button; ghost/link/secondary styles render as an inline fluoro underline link. Fields: link, style, label, size, icon, iconPosition, newTab, align. #}
|
||||||
|
{% with align=align|default:"left" %}
|
||||||
|
<div data-block-override="magazine-bold:button" class="bn-button flex {% if align == "center" %}justify-center{% elif align == "right" %}justify-end{% else %}justify-start{% endif %}{% if class %} {{ class }}{% endif %}">
|
||||||
|
{% if style == "ghost" or style == "link" or style == "secondary" %}
|
||||||
|
<a href="{{ link|default:'#' }}"{% if newTab %} target="_blank" rel="noopener"{% endif %} class="mb-link inline-flex items-center gap-2 text-sm">{% if icon and iconPosition != "right" %}::{{ icon }}:sm::{% endif %}{{ label|default:"Learn more" }}{% if icon and iconPosition == "right" %}::{{ icon }}:sm::{% endif %}</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ link|default:'#' }}"{% if newTab %} target="_blank" rel="noopener"{% endif %} class="mb-button inline-flex items-center gap-2">{% if icon and iconPosition != "right" %}::{{ icon }}:sm::{% endif %}{{ label|default:"Learn more" }}{% if icon and iconPosition == "right" %}::{{ icon }}:sm::{% endif %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endwith %}
|
||||||
|
|||||||
4
templates/overrides/magazine-bold/card.ninjatpl
Normal file
4
templates/overrides/magazine-bold/card.ninjatpl
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{# card override — magazine-bold hairline panel; accent border on hover, fluoro link. Fields mirror builtin: layout, link, media, title, text, linkLabel. Media via {% img %}. #}
|
||||||
|
{% with layout=layout|default:"vertical" %}
|
||||||
|
<article data-block-override="magazine-bold:card" class="bn-card mb-panel overflow-hidden bg-card text-card-foreground transition-colors hover:border-[hsl(var(--accent))] {% if layout == "horizontal" %}flex flex-col sm:flex-row{% endif %}{% if class %} {{ class }}{% endif %}">{% if link %}<a href="{{ link }}" class="contents">{% endif %}{% if media %}<div class="overflow-hidden bg-muted {% if layout == "horizontal" %}sm:w-2/5 sm:shrink-0{% endif %}">{% if layout == "horizontal" %}{% img media alt=title|default:"" class="h-full w-full object-cover" %}{% else %}{% img media alt=title|default:"" class="aspect-video h-full w-full object-cover" %}{% endif %}</div>{% endif %}<div class="flex flex-col gap-2 p-5 md:p-6 {% if layout == "horizontal" %}justify-center{% endif %}">{% if title %}<h3 class="mb-caps text-lg" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--foreground));">{{ title }}</h3>{% endif %}{% if text %}<div class="text-sm leading-relaxed" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Inter', system-ui, sans-serif);">{{ text|safe }}</div>{% endif %}{% if linkLabel and link %}<span class="mt-2 inline-flex items-center gap-1 text-sm mb-link">{{ linkLabel }}::lucide:arrow-right:sm::</span>{% endif %}</div>{% if link %}</a>{% endif %}</article>
|
||||||
|
{% endwith %}
|
||||||
18
templates/overrides/magazine-bold/category-list.ninjatpl
Normal file
18
templates/overrides/magazine-bold/category-list.ninjatpl
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{# category-list (magazine-bold) — from the `categories` provider. hairline list or bordered chips. #}
|
||||||
|
{% set pills = style == "pills" %}
|
||||||
|
<section data-block-override="magazine-bold:category-list" class="bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
{% if heading %}<h2 class="mb-caps mb-5 text-xs text-muted-foreground">{{ heading }}</h2>{% endif %}
|
||||||
|
{% if pills %}
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
{% for cat in categories %}
|
||||||
|
<a href="{{ cat.url }}" class="mb-caps inline-flex items-center gap-1.5 rounded-none border border-[hsl(var(--border))] bg-card px-4 py-1.5 text-xs text-card-foreground transition-colors hover:border-[hsl(var(--accent))] hover:text-[hsl(var(--accent))]">{{ cat.name }}{% if showCount %}<span class="text-xs text-muted-foreground">{{ cat.post_count }}</span>{% endif %}</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<ul class="space-y-1">
|
||||||
|
{% for cat in categories %}
|
||||||
|
<li><a href="{{ cat.url }}" class="mb-rule flex items-center justify-between px-3 py-2.5 text-sm text-muted-foreground transition-colors hover:text-[hsl(var(--accent))]"><span class="mb-caps">{{ cat.name }}</span>{% if showCount %}<span class="rounded-none border border-[hsl(var(--border))] px-2 py-0.5 text-xs text-muted-foreground">{{ cat.post_count }}</span>{% endif %}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</section>
|
||||||
17
templates/overrides/magazine-bold/contact-card.ninjatpl
Normal file
17
templates/overrides/magazine-bold/contact-card.ninjatpl
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<section data-block-override="magazine-bold:contact-card" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-4xl px-4 text-center">
|
||||||
|
{% if heading %}<h2 class="mb-caps mb-underline text-3xl md:text-4xl text-foreground" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ heading }}</h2>{% endif %}
|
||||||
|
{% if description %}<p class="mt-4 mx-auto max-w-2xl text-lg text-muted-foreground" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ description }}</p>{% endif %}
|
||||||
|
<div class="mt-12 grid grid-cols-1 gap-6 text-left {% if columns == 3 %}sm:grid-cols-2 lg:grid-cols-3{% elif columns == 1 %}{% else %}sm:grid-cols-2{% endif %}">
|
||||||
|
{% for channel in channels %}
|
||||||
|
<div class="mb-panel flex items-start gap-4 rounded-none bg-card p-6 text-card-foreground">
|
||||||
|
{% if channel.icon %}<span class="inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-none border border-[hsl(var(--border))] text-[hsl(var(--accent))]"><svg class="h-5 w-5" aria-hidden="true"><use href="/icons/{{ channel.icon|split:":"|first }}.svg#{{ channel.icon|split:":"|last }}"></use></svg></span>{% endif %}
|
||||||
|
<div class="min-w-0">
|
||||||
|
{% if channel.label %}<div class="mb-caps text-xs tracking-[0.16em] text-muted-foreground">{{ channel.label }}</div>{% endif %}
|
||||||
|
{% if channel.value %}<div class="mt-1 break-words text-lg text-foreground" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{% if channel.link %}<a href="{{ channel.link }}" class="mb-link transition-colors hover:text-[hsl(var(--accent))]">{{ channel.value }}</a>{% else %}{{ channel.value }}{% endif %}</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
43
templates/overrides/magazine-bold/contact-form.ninjatpl
Normal file
43
templates/overrides/magazine-bold/contact-form.ninjatpl
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{# contact-form override — magazine-bold enquiry form. ALL form wiring preserved verbatim from builtin: bid, {% form %} action/hx attrs, honeypot, field loop, form.submitted/errors, submit target. Only inputs restyled to hairline magazine-bold fields. #}
|
||||||
|
{% with bid=_block_id|default:context.blockId %}
|
||||||
|
<section data-block-override="magazine-bold:contact-form" id="contact-form-{{ bid }}" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-2xl px-4">
|
||||||
|
{% if form.submitted %}
|
||||||
|
<div class="mb-panel bg-card p-10 text-center">
|
||||||
|
<div class="mx-auto mb-4 inline-flex h-12 w-12 items-center justify-center rounded-full text-[hsl(var(--accent))]" style="box-shadow:inset 0 0 0 1px hsl(var(--accent));">::lucide:check:lg::</div>
|
||||||
|
<p class="text-lg italic" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif);">{{ form.message|default:successMessage|default:"Thank you. Your table request has been received." }}</p>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="text-center">
|
||||||
|
{% if heading %}<h2 class="mb-caps text-3xl md:text-4xl" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--foreground));">{{ heading }}</h2>{% endif %}
|
||||||
|
{% if description %}<p class="mt-3 text-lg italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Inter', system-ui, sans-serif);">{{ description }}</p>{% endif %}
|
||||||
|
<span class="mb-rule w-16 mx-auto mt-5 mb-2 block" aria-hidden="true"></span>
|
||||||
|
</div>
|
||||||
|
{% if form.errors._form %}<div class="mt-6 border border-destructive/50 bg-destructive/10 px-4 py-3 text-sm text-destructive">{{ form.errors._form }}</div>{% endif %}
|
||||||
|
{% with formAction="/api/blocks/"|add:bid|add:"/submit" formTarget="#contact-form-"|add:bid %}
|
||||||
|
{% form action=formAction hx_target=formTarget hx_swap="outerHTML" class="mt-8 grid grid-cols-1 gap-5 sm:grid-cols-2" %}
|
||||||
|
{% for field in fields %}
|
||||||
|
<div class="{% if field.width == "half" %}sm:col-span-1{% else %}sm:col-span-2{% endif %}">
|
||||||
|
{% if field.label %}<label for="cf-{{ bid }}-{{ field.name }}" class="mb-caps text-muted-foreground mb-1.5 block text-xs">{{ field.label }}{% if field.required %} <span class="text-destructive">*</span>{% endif %}</label>{% endif %}
|
||||||
|
{% if field.type == "textarea" %}
|
||||||
|
<textarea id="cf-{{ bid }}-{{ field.name }}" name="{{ field.name }}" rows="5"{% if field.required %} required{% endif %}{% if field.placeholder %} placeholder="{{ field.placeholder }}"{% endif %} class="mb-input"></textarea>
|
||||||
|
{% elif field.type == "select" %}
|
||||||
|
<select id="cf-{{ bid }}-{{ field.name }}" name="{{ field.name }}"{% if field.required %} required{% endif %} class="mb-input">
|
||||||
|
{% if field.placeholder %}<option value="">{{ field.placeholder }}</option>{% endif %}
|
||||||
|
{% for opt in field.options %}<option value="{{ opt.value }}">{{ opt.label|default:opt.value }}</option>{% endfor %}
|
||||||
|
</select>
|
||||||
|
{% else %}
|
||||||
|
<input id="cf-{{ bid }}-{{ field.name }}" name="{{ field.name }}" type="{{ field.type|default:"text" }}"{% if field.required %} required{% endif %}{% if field.placeholder %} placeholder="{{ field.placeholder }}"{% endif %} class="mb-input">
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<div class="hidden" aria-hidden="true"><label>Leave this field empty<input type="text" name="website" tabindex="-1" autocomplete="off"></label></div>
|
||||||
|
<div class="sm:col-span-2 flex justify-center">
|
||||||
|
<button type="submit" class="mb-button inline-flex items-center justify-center">{{ submitLabel|default:"Request a table" }}</button>
|
||||||
|
</div>
|
||||||
|
{% endform %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endwith %}
|
||||||
48
templates/overrides/magazine-bold/countdown.ninjatpl
Normal file
48
templates/overrides/magazine-bold/countdown.ninjatpl
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<section data-block-override="magazine-bold:countdown" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-4xl px-4 text-center">
|
||||||
|
{% if heading %}<h2 class="mb-caps text-3xl tracking-tight md:text-4xl" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ heading }}</h2>{% endif %}
|
||||||
|
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-lg text-muted-foreground" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ intro }}</p>{% endif %}
|
||||||
|
<div class="mt-10" data-countdown>
|
||||||
|
<div class="flex items-start justify-center gap-3 sm:gap-6" data-countdown-timer data-target="{{ target }}" role="timer" aria-label="Time remaining">
|
||||||
|
<div class="flex flex-col items-center {% if variant == "boxes" %}mb-panel min-w-[4.5rem] bg-card px-3 py-4 text-card-foreground{% endif %}"><span class="text-4xl font-bold tabular-nums text-[hsl(var(--accent))] sm:text-5xl" style="font-family:var(--font-mono)" data-unit="days">--</span><span class="mb-caps mt-1 text-xs text-muted-foreground">{{ labelDays|default:"Days" }}</span></div>
|
||||||
|
<div class="flex flex-col items-center {% if variant == "boxes" %}mb-panel min-w-[4.5rem] bg-card px-3 py-4 text-card-foreground{% endif %}"><span class="text-4xl font-bold tabular-nums text-[hsl(var(--accent))] sm:text-5xl" style="font-family:var(--font-mono)" data-unit="hours">--</span><span class="mb-caps mt-1 text-xs text-muted-foreground">{{ labelHours|default:"Hours" }}</span></div>
|
||||||
|
<div class="flex flex-col items-center {% if variant == "boxes" %}mb-panel min-w-[4.5rem] bg-card px-3 py-4 text-card-foreground{% endif %}"><span class="text-4xl font-bold tabular-nums text-[hsl(var(--accent))] sm:text-5xl" style="font-family:var(--font-mono)" data-unit="minutes">--</span><span class="mb-caps mt-1 text-xs text-muted-foreground">{{ labelMinutes|default:"Minutes" }}</span></div>
|
||||||
|
<div class="flex flex-col items-center {% if variant == "boxes" %}mb-panel min-w-[4.5rem] bg-card px-3 py-4 text-card-foreground{% endif %}"><span class="text-4xl font-bold tabular-nums text-[hsl(var(--accent))] sm:text-5xl" style="font-family:var(--font-mono)" data-unit="seconds">--</span><span class="mb-caps mt-1 text-xs text-muted-foreground">{{ labelSeconds|default:"Seconds" }}</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-8 hidden" data-countdown-expired>
|
||||||
|
<p class="mb-caps text-xl" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ expiredMessage|default:"This has started." }}</p>
|
||||||
|
{% if expiredUrl and expiredLabel %}<a href="{{ expiredUrl }}" class="mb-button mt-4 inline-flex items-center gap-2">{{ expiredLabel }}<svg class="h-4 w-4" aria-hidden="true"><use href="/icons/lucide.svg#arrow-right"></use></svg></a>{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<noscript><p class="mt-6 text-muted-foreground">Counting down to <time datetime="{{ target }}">{{ target }}</time>.</p></noscript>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
var timers = document.querySelectorAll('[data-countdown-timer]');
|
||||||
|
timers.forEach(function(el){
|
||||||
|
var target = new Date(el.getAttribute('data-target')).getTime();
|
||||||
|
if (isNaN(target)) return;
|
||||||
|
var wrap = el.closest('[data-countdown]');
|
||||||
|
var expired = wrap ? wrap.querySelector('[data-countdown-expired]') : null;
|
||||||
|
function pad(n){ return (n < 10 ? '0' : '') + n; }
|
||||||
|
function set(unit, value){ var node = el.querySelector('[data-unit="' + unit + '"]'); if (node) node.textContent = value; }
|
||||||
|
var interval;
|
||||||
|
function tick(){
|
||||||
|
var diff = target - Date.now();
|
||||||
|
if (diff <= 0){
|
||||||
|
el.style.display = 'none';
|
||||||
|
if (expired) expired.classList.remove('hidden');
|
||||||
|
if (interval) clearInterval(interval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
set('days', Math.floor(diff / 86400000));
|
||||||
|
set('hours', pad(Math.floor(diff % 86400000 / 3600000)));
|
||||||
|
set('minutes', pad(Math.floor(diff % 3600000 / 60000)));
|
||||||
|
set('seconds', pad(Math.floor(diff % 60000 / 1000)));
|
||||||
|
}
|
||||||
|
tick();
|
||||||
|
interval = setInterval(tick, 1000);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
14
templates/overrides/magazine-bold/cta.ninjatpl
Normal file
14
templates/overrides/magazine-bold/cta.ninjatpl
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<section data-block-override="magazine-bold:cta" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-6xl px-4">
|
||||||
|
<div class="{% if background == "band" %}mb-panel px-6 py-14 text-center text-card-foreground md:px-12{% elif background == "card" %}mb-panel bg-card px-6 py-14 text-center text-card-foreground md:px-12{% else %}mb-panel flex flex-col items-center gap-8 bg-muted px-6 py-12 text-foreground md:flex-row md:justify-between md:px-12 md:text-left{% endif %}">
|
||||||
|
<div class="{% if background != "split" %}mx-auto max-w-2xl{% endif %}">
|
||||||
|
{% if heading %}<h2 class="mb-caps text-3xl tracking-tight md:text-4xl" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ heading }}</h2>{% endif %}
|
||||||
|
{% if text %}<p class="mt-3 text-lg {% if background == "band" %}text-muted-foreground{% else %}text-muted-foreground{% endif %}" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ text }}</p>{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap items-center justify-center gap-3 {% if background != "split" %}mt-8{% else %}shrink-0{% endif %}">
|
||||||
|
{% if primaryUrl and primaryLabel %}<a href="{{ primaryUrl }}" class="mb-button inline-flex items-center gap-2">{{ primaryLabel }}<svg class="h-4 w-4" aria-hidden="true"><use href="/icons/lucide.svg#arrow-right"></use></svg></a>{% endif %}
|
||||||
|
{% if secondaryUrl and secondaryLabel %}<a href="{{ secondaryUrl }}" class="mb-link inline-flex items-center">{{ secondaryLabel }}</a>{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
12
templates/overrides/magazine-bold/divider.ninjatpl
Normal file
12
templates/overrides/magazine-bold/divider.ninjatpl
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{# divider override — magazine-bold hairline rules. Fields mirror builtin: variant (line|ornament|spacer-rule), weight, width, icon. ornament renders an icon flanked by hairline rules; spacer-rule a short centred hairline; line a full hairline rule. #}
|
||||||
|
{% with variant=variant|default:"line" weight=weight|default:"thin" width=width|default:"full" %}
|
||||||
|
{% if variant == "ornament" %}
|
||||||
|
<div data-block-override="magazine-bold:divider" class="bn-divider mx-auto my-10 flex flex-col items-center gap-2 {% if width == "narrow" %}max-w-[200px]{% elif width == "wide" %}max-w-3xl{% else %}w-full{% endif %}{% if class %} {{ class }}{% endif %}" role="separator">
|
||||||
|
<div class="flex w-full items-center gap-4"><span class="mb-rule flex-1"></span><span style="color:hsl(var(--accent));">::{{ icon|default:"lucide:diamond" }}:sm::</span><span class="mb-rule flex-1"></span></div>
|
||||||
|
</div>
|
||||||
|
{% elif variant == "spacer-rule" %}
|
||||||
|
<div data-block-override="magazine-bold:divider" class="bn-divider mx-auto py-10 flex justify-center {% if width == "narrow" %}max-w-[200px]{% elif width == "wide" %}max-w-3xl{% else %}w-full{% endif %}{% if class %} {{ class }}{% endif %}" role="separator"><span class="{% if weight == "thick" %}mb-rule{% else %}mb-rule{% endif %} w-16"></span></div>
|
||||||
|
{% else %}
|
||||||
|
<div data-block-override="magazine-bold:divider" class="bn-divider mx-auto my-8 flex justify-center {% if width == "narrow" %}max-w-[200px]{% elif width == "wide" %}max-w-3xl{% else %}w-full{% endif %}{% if class %} {{ class }}{% endif %}" role="separator"><span class="{% if weight == "thick" %}mb-rule{% else %}mb-rule{% endif %} w-full"></span></div>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
41
templates/overrides/magazine-bold/event-list.ninjatpl
Normal file
41
templates/overrides/magazine-bold/event-list.ninjatpl
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{# event-list override — magazine-bold listings: hairline panels/rules, fluoro dates, Archivo Black titles. Fields mirror builtin: heading, intro, layout, hidePast, events[].* . #}
|
||||||
|
<section data-block-override="magazine-bold:event-list" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-6xl px-4 text-center">
|
||||||
|
{% if heading %}<h2 class="mb-caps mb-underline text-3xl md:text-4xl text-foreground" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ heading }}</h2>{% endif %}
|
||||||
|
{% if intro %}<p class="mt-4 mx-auto max-w-2xl text-lg text-muted-foreground" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ intro }}</p>{% endif %}
|
||||||
|
{% if layout == "cards" %}
|
||||||
|
<div class="mt-12 grid grid-cols-1 gap-8 text-left sm:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{% for event in events %}{% if not (hidePast and event.past) %}
|
||||||
|
<article class="mb-panel flex flex-col overflow-hidden rounded-none bg-card text-card-foreground{% if event.past %} opacity-60{% endif %}">
|
||||||
|
{% if event.media %}<div class="aspect-video overflow-hidden bg-muted">{% img event.media alt=event.title class="h-full w-full object-cover" %}</div>{% endif %}
|
||||||
|
<div class="flex flex-1 flex-col gap-2 p-6">
|
||||||
|
<div class="mb-caps text-xs tracking-[0.18em] text-[hsl(var(--accent))]"><time{% if event.datetime %} datetime="{{ event.datetime }}"{% endif %}>{{ event.start }}{% if event.end %} – {{ event.end }}{% endif %}</time>{% if event.timezone %} <span class="text-muted-foreground">{{ event.timezone }}</span>{% endif %}</div>
|
||||||
|
<h3 class="text-xl text-foreground" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ event.title }}</h3>
|
||||||
|
{% if event.venue %}<p class="mb-caps text-xs tracking-[0.14em] text-muted-foreground">{{ event.venue }}</p>{% endif %}
|
||||||
|
{% if event.description %}<p class="text-sm text-muted-foreground" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ event.description }}</p>{% endif %}
|
||||||
|
{% if event.link %}<a href="{{ event.link }}" class="mb-link mt-auto inline-flex items-center gap-2 pt-3 text-sm text-[hsl(var(--accent))]">{{ event.linkLabel|default:"Details" }}<svg class="h-4 w-4" aria-hidden="true"><use href="/icons/lucide.svg#arrow-right"></use></svg></a>{% endif %}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{% endif %}{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<ul class="mt-12 divide-y divide-[hsl(var(--border))] border-y-2 border-[hsl(var(--border))] text-left">
|
||||||
|
{% for event in events %}{% if not (hidePast and event.past) %}
|
||||||
|
<li class="flex flex-col gap-3 py-6 sm:flex-row sm:items-center sm:gap-8{% if event.past %} opacity-60{% endif %}">
|
||||||
|
<div class="shrink-0 sm:w-52">
|
||||||
|
<div class="mb-caps text-sm tracking-[0.16em] text-[hsl(var(--accent))]"><time{% if event.datetime %} datetime="{{ event.datetime }}"{% endif %}>{{ event.start }}</time></div>
|
||||||
|
{% if event.end %}<div class="text-xs text-muted-foreground">to {{ event.end }}</div>{% endif %}
|
||||||
|
{% if event.timezone %}<div class="text-xs text-muted-foreground">{{ event.timezone }}</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="min-w-0 flex-1">
|
||||||
|
<h3 class="text-lg text-foreground" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ event.title }}</h3>
|
||||||
|
{% if event.venue %}<p class="mb-caps text-xs tracking-[0.14em] text-muted-foreground">{{ event.venue }}</p>{% endif %}
|
||||||
|
{% if event.description %}<p class="mt-1 text-sm text-muted-foreground" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ event.description }}</p>{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if event.link %}<a href="{{ event.link }}" class="mb-button inline-flex shrink-0 items-center gap-2">{{ event.linkLabel|default:"Details" }}</a>{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endif %}{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
17
templates/overrides/magazine-bold/faq.ninjatpl
Normal file
17
templates/overrides/magazine-bold/faq.ninjatpl
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<section data-block-override="magazine-bold:faq" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-3xl px-4">
|
||||||
|
{% if heading %}<h2 class="mb-caps text-center text-3xl text-foreground md:text-4xl" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ heading }}</h2>{% endif %}
|
||||||
|
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ intro }}</p>{% endif %}
|
||||||
|
<div class="mt-10 divide-y divide-[hsl(var(--border))] border-y border-[hsl(var(--border))]">
|
||||||
|
{% for item in items %}
|
||||||
|
<details class="group py-2"{% if variant == "open-list" %} open{% endif %}>
|
||||||
|
<summary class="flex cursor-pointer list-none items-center justify-between gap-4 py-3 text-lg font-medium marker:content-none [&::-webkit-details-marker]:hidden" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">
|
||||||
|
<span>{{ item.question }}</span>
|
||||||
|
<svg class="h-5 w-5 shrink-0 text-[hsl(var(--accent))] transition-transform duration-200 group-open:rotate-180" aria-hidden="true"><use href="/icons/lucide.svg#chevron-down"></use></svg>
|
||||||
|
</summary>
|
||||||
|
<div class="pb-4 text-muted-foreground [&_a]:text-[hsl(var(--accent))] [&_a]:underline" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ item.answer|safe }}</div>
|
||||||
|
</details>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
17
templates/overrides/magazine-bold/feature-grid.ninjatpl
Normal file
17
templates/overrides/magazine-bold/feature-grid.ninjatpl
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<section data-block-override="magazine-bold:feature-grid" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-6xl px-4">
|
||||||
|
{% if eyebrow %}<div class="mb-3 flex items-center justify-center gap-4"><span class="mb-rule w-12"></span><p class="mb-caps text-xs text-muted-foreground">{{ eyebrow }}</p><span class="mb-rule w-12"></span></div>{% endif %}
|
||||||
|
{% if heading %}<h2 class="mb-caps mt-2 text-center text-3xl text-foreground md:text-4xl" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ heading }}</h2>{% endif %}
|
||||||
|
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ intro }}</p>{% endif %}
|
||||||
|
<div class="mt-12 grid grid-cols-1 gap-6 sm:grid-cols-2 {% if columns == 2 %}lg:grid-cols-2{% elif columns == 4 %}lg:grid-cols-4{% else %}lg:grid-cols-3{% endif %}">
|
||||||
|
{% for item in items %}
|
||||||
|
<div class="{% if layout == "card" %}mb-panel bg-card p-6 text-card-foreground hover:border-[hsl(var(--accent))] transition-shadow{% elif layout == "centered" %}text-center{% endif %}">
|
||||||
|
{% if item.icon %}<div class="mb-4 inline-flex h-11 w-11 items-center justify-center border border-[hsl(var(--border))] bg-muted text-[hsl(var(--accent))]{% if layout == "centered" %} mx-auto{% endif %}"><svg class="h-6 w-6" aria-hidden="true"><use href="/icons/{{ item.icon|split:":"|first }}.svg#{{ item.icon|split:":"|last }}"></use></svg></div>{% endif %}
|
||||||
|
<h3 class="mb-caps text-lg text-foreground" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{% if item.linkUrl %}<a href="{{ item.linkUrl }}" class="transition-colors hover:text-[hsl(var(--accent))]">{{ item.title }}</a>{% else %}{{ item.title }}{% endif %}</h3>
|
||||||
|
{% if item.text %}<p class="mt-2 text-muted-foreground" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ item.text }}</p>{% endif %}
|
||||||
|
{% if item.linkUrl and item.linkLabel %}<a href="{{ item.linkUrl }}" class="mb-link mt-3 inline-flex items-center gap-1 text-sm">{{ item.linkLabel }}<svg class="h-4 w-4" aria-hidden="true"><use href="/icons/lucide.svg#arrow-right"></use></svg></a>{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
24
templates/overrides/magazine-bold/featured-posts.ninjatpl
Normal file
24
templates/overrides/magazine-bold/featured-posts.ninjatpl
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{# featured-posts (magazine-bold) — grid of hairline-framed cards from the `posts` provider #}
|
||||||
|
{# (params: limit, featured=true). Card: image, title, excerpt, meta. #}
|
||||||
|
<section data-block-override="magazine-bold:featured-posts" class="py-12 md:py-16 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-6xl px-4">
|
||||||
|
{% if heading %}<h2 class="mb-caps mb-underline mb-10 text-center text-2xl md:text-3xl text-foreground" style="font-family:var(--font-heading, "Archivo Black", 'Arial Black', sans-serif)">{{ heading }}</h2>{% endif %}
|
||||||
|
<div class="grid grid-cols-1 gap-8 sm:grid-cols-2 {% if columns == 2 %}lg:grid-cols-2{% elif columns == 4 %}lg:grid-cols-4{% else %}lg:grid-cols-3{% endif %}">
|
||||||
|
{% for post in posts %}
|
||||||
|
<article class="mb-panel group flex flex-col overflow-hidden bg-card text-card-foreground transition-shadow">
|
||||||
|
{% if showFeaturedImage and post.featured_image_url %}<a href="{{ post.url }}" class="block aspect-video overflow-hidden bg-muted">{% img post.featured_image_url alt=post.title class="h-full w-full object-cover transition-transform duration-500 group-hover:scale-105" %}</a>{% endif %}
|
||||||
|
<div class="flex flex-1 flex-col p-6 text-center">
|
||||||
|
<h3 class="mb-caps text-lg leading-snug text-card-foreground" style="font-family:var(--font-heading, "Archivo Black", 'Arial Black', sans-serif)"><a href="{{ post.url }}" class="transition-colors hover:text-[hsl(var(--accent))]">{{ post.title }}</a></h3>
|
||||||
|
<span class="mb-rule mx-auto mt-3 block w-10"></span>
|
||||||
|
{% if showExcerpt and post.excerpt %}<p class="mt-3 flex-1 text-sm text-muted-foreground" style="font-family:var(--font-body, "Inter", system-ui, sans-serif)">{{ post.excerpt }}</p>{% endif %}
|
||||||
|
<div class="mt-4 flex flex-wrap items-center justify-center gap-x-3 gap-y-1 text-xs text-muted-foreground">
|
||||||
|
{% if showAuthor and post.author_name %}<span class="mb-caps text-[hsl(var(--accent))]">{{ post.author_name }}</span>{% endif %}
|
||||||
|
{% if showDate and post.published_at %}<span>{{ post.published_at|date:"M j, Y" }}</span>{% endif %}
|
||||||
|
{% if post.reading_time %}<span>{{ post.reading_time }} min read</span>{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
40
templates/overrides/magazine-bold/footer.ninjatpl
Normal file
40
templates/overrides/magazine-bold/footer.ninjatpl
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{# footer override — quiet magazine-bold colophon: hairline rules, small-caps labels, address, reservations, social on a card panel. Structure/fields/signup_form preserved from builtin; reskinned to magazine-bold. #}
|
||||||
|
{% set fl = layout %}
|
||||||
|
{% if not fl %}{% if template == "centered" %}{% set fl = "centered" %}{% else %}{% set fl = "columns" %}{% endif %}{% endif %}
|
||||||
|
{% set fbg = backgroundColor|default:"bg-card text-card-foreground" %}
|
||||||
|
{% macro sociallinks(cls) %}{% if social_items %}<div class="flex items-center gap-4 {{ cls }}">{% for s in social_items %}<a href="{{ s.url }}" target="_blank" rel="noopener" title="{{ s.label }}" class="text-muted-foreground hover:text-[hsl(var(--accent))] transition-colors"><span class="sr-only">{{ s.label }}</span>{% if s.icon %}::{{ s.icon }}::{% else %}{{ s.label }}{% endif %}</a>{% endfor %}</div>{% endif %}{% endmacro %}
|
||||||
|
{% macro newsletter() %}{% if showNewsletter %}<div class="max-w-sm"><h3 class="mb-caps text-sm text-muted-foreground">{{ newsletterTitle|default:"Join the guest list" }}</h3>{% if newsletterDescription %}<p class="mt-1 text-sm italic text-muted-foreground">{{ newsletterDescription }}</p>{% endif %}<div class="mt-3">{% signup_form list=newsletterListSlug|default:"main" source="footer" submit=newsletterButtonText|default:"Subscribe" %}</div></div>{% endif %}{% endmacro %}
|
||||||
|
{% macro legalbar() %}<div class="mt-10 pt-6 border-t border-[hsl(var(--border))] flex flex-col sm:flex-row items-center justify-between gap-3 text-sm text-muted-foreground"><p>{% if copyright %}© {{ copyright }}{% else %}© {{ companyName }}{% endif %}</p>{% if legal_items %}<nav class="flex flex-wrap items-center gap-x-4 gap-y-1">{% for l in legal_items %}<a href="{{ l.url|default:'#' }}"{% if l.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="hover:text-[hsl(var(--accent))] transition-colors">{{ l.label }}</a>{% endfor %}</nav>{% endif %}</div>{% endmacro %}
|
||||||
|
<footer data-block-override="magazine-bold:footer" class="w-full border-t border-[hsl(var(--border))] {{ fbg }} {{ class }}">
|
||||||
|
<div class="{% if fullWidth %}w-full px-4 sm:px-6 lg:px-8{% else %}max-w-7xl mx-auto px-4 sm:px-6 lg:px-8{% endif %} py-16">
|
||||||
|
{% if fl == "minimal" %}
|
||||||
|
<div class="flex flex-col sm:flex-row items-center justify-between gap-6">
|
||||||
|
<div class="flex items-center gap-3">{% if logo %}<img src="{{ logo }}" alt="{{ companyName }}" class="h-8 w-auto"/>{% elif companyName %}<span class="mb-caps text-lg" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);">{{ companyName }}</span>{% endif %}{% if tagline %}<span class="text-sm italic text-muted-foreground hidden sm:inline">{{ tagline }}</span>{% endif %}</div>
|
||||||
|
{{ sociallinks("") }}
|
||||||
|
</div>
|
||||||
|
{{ legalbar() }}
|
||||||
|
{% elif fl == "centered" %}
|
||||||
|
<div class="flex flex-col items-center text-center gap-5">
|
||||||
|
{% if logo %}<img src="{{ logo }}" alt="{{ companyName }}" class="h-10 w-auto"/>{% elif companyName %}<span class="mb-caps text-2xl text-[hsl(var(--accent))]" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);">{{ companyName }}</span>{% endif %}
|
||||||
|
<span class="mb-rule w-12"></span>
|
||||||
|
{% if tagline %}<p class="max-w-md text-sm italic text-muted-foreground">{{ tagline }}</p>{% endif %}
|
||||||
|
{% if columns %}<nav class="flex flex-wrap items-center justify-center gap-x-6 gap-y-2">{% for col in columns %}{% for item in col.items %}<a href="{{ item.url|default:'#' }}"{% if item.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="mb-caps text-xs text-muted-foreground hover:text-[hsl(var(--accent))] transition-colors">{{ item.label }}</a>{% endfor %}{% endfor %}</nav>{% endif %}
|
||||||
|
{{ newsletter() }}
|
||||||
|
{{ sociallinks("justify-center") }}
|
||||||
|
</div>
|
||||||
|
{{ legalbar() }}
|
||||||
|
{% else %}
|
||||||
|
<div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-8">
|
||||||
|
<div class="col-span-2 lg:col-span-2">
|
||||||
|
{% if logo %}<img src="{{ logo }}" alt="{{ companyName }}" class="h-9 w-auto"/>{% elif companyName %}<span class="mb-caps text-xl text-[hsl(var(--accent))]" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);">{{ companyName }}</span>{% endif %}
|
||||||
|
{% if tagline %}<p class="mt-3 max-w-xs text-sm italic text-muted-foreground">{{ tagline }}</p>{% endif %}
|
||||||
|
{% if address or email or phone %}<address class="mt-4 not-italic text-sm text-muted-foreground space-y-1">{% if address %}<div>{{ address }}</div>{% endif %}{% if email %}<div><a href="mailto:{{ email }}" class="hover:text-[hsl(var(--accent))] transition-colors">{{ email }}</a></div>{% endif %}{% if phone %}<div><a href="tel:{{ phone }}" class="hover:text-[hsl(var(--accent))] transition-colors">{{ phone }}</a></div>{% endif %}</address>{% endif %}
|
||||||
|
{{ sociallinks("mt-5") }}
|
||||||
|
</div>
|
||||||
|
{% for col in columns %}<div><h3 class="mb-caps text-xs text-muted-foreground">{{ col.header }}</h3><ul class="mt-3 space-y-2">{% for item in col.items %}<li><a href="{{ item.url|default:'#' }}"{% if item.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="text-sm text-muted-foreground hover:text-[hsl(var(--accent))] transition-colors">{{ item.label }}</a></li>{% endfor %}</ul></div>{% endfor %}
|
||||||
|
{% if showNewsletter %}<div class="col-span-2 lg:col-span-1">{{ newsletter() }}</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
{{ legalbar() }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
52
templates/overrides/magazine-bold/gallery.ninjatpl
Normal file
52
templates/overrides/magazine-bold/gallery.ninjatpl
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{# gallery override — magazine-bold grid: plain hairline framing, generous whitespace, unchanged lightbox. Fields + lightbox JS mirror builtin verbatim: layout, gap, columns, lightbox, images[].src/.alt/.caption. #}
|
||||||
|
{% with layout=layout|default:"grid" gap=gap|default:"md" cols=columns|default:3 %}<div data-block-override="magazine-bold:gallery" class="bn-gallery my-8{% if class %} {{ class }}{% endif %}" data-bn-gallery data-lightbox="{% if lightbox %}true{% else %}false{% endif %}"><div class="{% if layout == "carousel" %}flex snap-x snap-mandatory overflow-x-auto pb-2 {% if gap == "sm" %}gap-2{% elif gap == "lg" %}gap-6{% else %}gap-4{% endif %}{% elif layout == "masonry" %}columns-2 {% if cols == 4 %}md:columns-4{% elif cols == 2 %}md:columns-2{% else %}md:columns-3{% endif %} {% if gap == "sm" %}gap-2{% elif gap == "lg" %}gap-6{% else %}gap-4{% endif %}{% else %}grid grid-cols-2 {% if cols == 4 %}md:grid-cols-4{% elif cols == 2 %}md:grid-cols-2{% else %}md:grid-cols-3{% endif %} {% if gap == "sm" %}gap-2{% elif gap == "lg" %}gap-6{% else %}gap-4{% endif %}{% endif %}">{% for image in images %}<a href="{{ image.src|mediaURL }}" data-bn-gallery-item data-caption="{{ image.caption|default:"" }}" class="group relative block overflow-hidden bg-muted focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 {% if layout == "carousel" %}w-64 shrink-0 snap-start sm:w-80{% elif layout == "masonry" %}mb-4 w-full break-inside-avoid{% endif %}">{% if layout == "grid" %}{% img image.src alt=image.alt|default:"" class="aspect-square w-full object-cover" %}{% else %}{% img image.src alt=image.alt|default:"" class="h-auto w-full" %}{% endif %}{% if image.caption %}<span class="sr-only">{{ image.caption }}</span>{% endif %}</a>{% endfor %}</div></div>{% if lightbox %}<script>
|
||||||
|
(function(){
|
||||||
|
if(window.__bnLightbox)return;window.__bnLightbox=true;
|
||||||
|
var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
|
var ov,imgEl,capEl,items=[],idx=0,lastFocus=null;
|
||||||
|
function build(){
|
||||||
|
ov=document.createElement('div');
|
||||||
|
ov.setAttribute('role','dialog');ov.setAttribute('aria-modal','true');ov.setAttribute('aria-label','Image viewer');ov.tabIndex=-1;
|
||||||
|
ov.style.cssText='position:fixed;inset:0;z-index:9999;display:none;align-items:center;justify-content:center;background:rgba(0,0,0,0.9);padding:2rem;';
|
||||||
|
var btn='position:absolute;width:2.75rem;height:2.75rem;display:inline-flex;align-items:center;justify-content:center;border-radius:9999px;border:none;background:hsl(var(--background));color:hsl(var(--foreground));cursor:pointer;';
|
||||||
|
ov.innerHTML=
|
||||||
|
'<button type="button" data-lb-close aria-label="Close" style="'+btn+'top:1rem;right:1rem;"><svg style="width:1.25rem;height:1.25rem" aria-hidden="true"><use href="/icons/lucide.svg#x"/></svg></button>'+
|
||||||
|
'<button type="button" data-lb-prev aria-label="Previous image" style="'+btn+'left:1rem;top:50%;transform:translateY(-50%);"><svg style="width:1.25rem;height:1.25rem" aria-hidden="true"><use href="/icons/lucide.svg#chevron-left"/></svg></button>'+
|
||||||
|
'<figure style="margin:0;max-width:92vw;max-height:92vh;display:flex;flex-direction:column;align-items:center;gap:0.75rem;"><img data-lb-img alt="" style="max-width:92vw;max-height:82vh;object-fit:contain;border-radius:0.5rem;'+(reduce?'':'transition:opacity .2s ease;')+'"><figcaption data-lb-cap style="color:hsl(var(--background));opacity:.85;font-size:.875rem;text-align:center;max-width:60ch;"></figcaption></figure>'+
|
||||||
|
'<button type="button" data-lb-next aria-label="Next image" style="'+btn+'right:1rem;top:50%;transform:translateY(-50%);"><svg style="width:1.25rem;height:1.25rem" aria-hidden="true"><use href="/icons/lucide.svg#chevron-right"/></svg></button>';
|
||||||
|
document.body.appendChild(ov);
|
||||||
|
imgEl=ov.querySelector('[data-lb-img]');capEl=ov.querySelector('[data-lb-cap]');
|
||||||
|
ov.addEventListener('click',function(e){if(e.target===ov||e.target.closest('[data-lb-close]'))close();});
|
||||||
|
ov.querySelector('[data-lb-prev]').addEventListener('click',function(e){e.stopPropagation();show(idx-1);});
|
||||||
|
ov.querySelector('[data-lb-next]').addEventListener('click',function(e){e.stopPropagation();show(idx+1);});
|
||||||
|
}
|
||||||
|
function show(i){
|
||||||
|
if(!items.length)return;
|
||||||
|
idx=(i+items.length)%items.length;
|
||||||
|
var a=items[idx];var im=a.querySelector('img');
|
||||||
|
imgEl.src=(im&&(im.currentSrc||im.src))||a.getAttribute('href');
|
||||||
|
imgEl.alt=im?im.alt:'';
|
||||||
|
var cap=a.getAttribute('data-caption')||'';capEl.textContent=cap;capEl.style.display=cap?'block':'none';
|
||||||
|
}
|
||||||
|
function open(g,link){
|
||||||
|
if(!ov)build();
|
||||||
|
items=Array.prototype.slice.call(g.querySelectorAll('[data-bn-gallery-item]'));
|
||||||
|
lastFocus=document.activeElement;ov.style.display='flex';
|
||||||
|
var multi=items.length>1;
|
||||||
|
ov.querySelector('[data-lb-prev]').style.display=multi?'inline-flex':'none';
|
||||||
|
ov.querySelector('[data-lb-next]').style.display=multi?'inline-flex':'none';
|
||||||
|
show(items.indexOf(link));ov.focus();
|
||||||
|
}
|
||||||
|
function close(){if(ov)ov.style.display='none';if(lastFocus&&lastFocus.focus)lastFocus.focus();}
|
||||||
|
document.addEventListener('click',function(e){
|
||||||
|
var link=e.target.closest('[data-bn-gallery][data-lightbox="true"] [data-bn-gallery-item]');
|
||||||
|
if(!link)return;e.preventDefault();open(link.closest('[data-bn-gallery]'),link);
|
||||||
|
});
|
||||||
|
document.addEventListener('keydown',function(e){
|
||||||
|
if(!ov||ov.style.display==='none')return;
|
||||||
|
if(e.key==='Escape'){e.preventDefault();close();}
|
||||||
|
else if(e.key==='ArrowLeft')show(idx-1);
|
||||||
|
else if(e.key==='ArrowRight')show(idx+1);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>{% endif %}{% endwith %}
|
||||||
@ -1 +1,7 @@
|
|||||||
{% if level|integer == 1 %}<h1 class="font-display text-folio text-foreground mb-dropcap {{ textClass }}">{{ text }}</h1>{% elif level|integer == 3 %}<h3 class="font-display text-3xl text-foreground {{ textClass }}">{{ text }}</h3>{% elif level|integer == 4 %}<h4 class="font-serif-sub text-2xl text-foreground {{ textClass }}">{{ text }}</h4>{% elif level|integer == 5 %}<h5 class="font-serif-sub text-xl text-foreground {{ textClass }}">{{ text }}</h5>{% elif level|integer == 6 %}<h6 class="font-mono text-kicker text-muted-foreground {{ textClass }}">{{ text }}</h6>{% else %}<h2 class="font-display text-deck text-foreground {{ textClass }}">{{ text }}</h2>{% endif %}
|
{# heading override — magazine-bold display serif with a hairline underline. Fields: text, level, textClass (mirrors builtin). #}
|
||||||
|
{% if level == 1 %}<h1 data-block-override="magazine-bold:heading" class="mb-heading mb-underline text-4xl md:text-5xl flex-1 {{ textClass }}" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--foreground));">{{ text }}</h1>
|
||||||
|
{% elif level == 3 %}<h3 data-block-override="magazine-bold:heading" class="mb-heading mb-underline text-2xl md:text-3xl flex-1 {{ textClass }}" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--foreground));">{{ text }}</h3>
|
||||||
|
{% elif level == 4 %}<h4 data-block-override="magazine-bold:heading" class="mb-heading mb-underline text-xl md:text-2xl flex-1 {{ textClass }}" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--foreground));">{{ text }}</h4>
|
||||||
|
{% elif level == 5 %}<h5 data-block-override="magazine-bold:heading" class="mb-heading mb-underline text-lg md:text-xl flex-1 {{ textClass }}" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--foreground));">{{ text }}</h5>
|
||||||
|
{% elif level == 6 %}<h6 data-block-override="magazine-bold:heading" class="mb-heading mb-underline text-base md:text-lg flex-1 {{ textClass }}" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--foreground));">{{ text }}</h6>
|
||||||
|
{% else %}<h2 data-block-override="magazine-bold:heading" class="mb-heading mb-underline text-3xl md:text-4xl flex-1 {{ textClass }}" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--foreground));">{{ text }}</h2>{% endif %}
|
||||||
|
|||||||
39
templates/overrides/magazine-bold/hero.ninjatpl
Normal file
39
templates/overrides/magazine-bold/hero.ninjatpl
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{# hero override — magazine-bold masthead: hairline eyebrow rules, small-caps Archivo Black headline, roomy reading measure. Structure/fields/{% img %} calls preserved from builtin. Styled-text fields accept {text,color} or a bare string. #}
|
||||||
|
{% set eyebrowText = eyebrow.text|default:eyebrow %}
|
||||||
|
{% set headlineText = headline.text|default:headline %}
|
||||||
|
{% set subText = subheadline.text|default:subheadline %}
|
||||||
|
{% set descText = description.text|default:description %}
|
||||||
|
{% set pText = ctaPrimary.text %}
|
||||||
|
{% set pUrl = ctaPrimary.url %}
|
||||||
|
{% set sText = ctaSecondary.text %}
|
||||||
|
{% set sUrl = ctaSecondary.url %}
|
||||||
|
{% set variant = template|default:"centered" %}
|
||||||
|
{% set hasImage = backgroundImage %}
|
||||||
|
{% set showOverlay = hasImage and overlayColor != "none" and overlayColor != "" %}
|
||||||
|
{% macro herocontent(align) %}
|
||||||
|
{% if eyebrowText %}<div class="mb-5 flex items-center gap-4 {% if align == "center" %}justify-center{% endif %}"><span class="mb-rule w-10"></span><span class="mb-caps text-xs text-muted-foreground">{{ eyebrowText }}</span><span class="mb-rule w-10"></span></div>{% endif %}
|
||||||
|
{% if headlineText %}<h1 class="mb-caps text-4xl sm:text-5xl lg:text-6xl leading-tight" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--foreground));">{{ headlineText }}</h1>{% endif %}
|
||||||
|
{% if subText %}<h2 class="mt-5 text-xl sm:text-2xl italic max-w-2xl{% if align == "center" %} mx-auto{% endif %}" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Inter', system-ui, sans-serif);">{{ subText }}</h2>{% endif %}
|
||||||
|
{% if descText %}<p class="mt-4 max-w-xl leading-relaxed{% if align == "center" %} mx-auto{% endif %}" style="color:hsl(var(--muted-foreground));">{{ descText }}</p>{% endif %}
|
||||||
|
{% if pText or sText %}<div class="mt-8 flex flex-col sm:flex-row gap-4{% if align == "center" %} justify-center{% endif %}">
|
||||||
|
{% if pText and pUrl %}<a href="{{ pUrl }}" class="mb-button inline-flex items-center justify-center gap-2">{{ pText }}</a>{% endif %}
|
||||||
|
{% if sText and sUrl %}<a href="{{ sUrl }}" class="mb-link inline-flex items-center justify-center">{{ sText }}</a>{% endif %}
|
||||||
|
</div>{% endif %}
|
||||||
|
{% if trustLogos %}<div class="mt-10">
|
||||||
|
<p class="mb-caps text-xs text-muted-foreground mb-3">Acclaim</p>
|
||||||
|
<div class="flex flex-wrap items-center gap-6{% if align == "center" %} justify-center{% endif %}">{% for logo in trustLogos %}{% if logo.url %}<a href="{{ logo.url }}" target="_blank" rel="noopener noreferrer" class="opacity-60 transition-opacity hover:opacity-100">{% img logo.image alt=logo.alt class="h-8 w-auto" %}</a>{% else %}{% img logo.image alt=logo.alt class="h-8 w-auto opacity-60" %}{% endif %}{% endfor %}</div>
|
||||||
|
</div>{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% if variant == "split" %}
|
||||||
|
<section data-block-override="magazine-bold:hero" class="hero grid grid-cols-1 lg:grid-cols-2 overflow-hidden bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="relative min-h-[320px] bg-muted {% if mediaPosition == "left" %}order-1 lg:order-1{% else %}order-1 lg:order-2{% endif %}">{% if hasImage %}{% img backgroundImage alt=headlineText class="absolute inset-0 h-full w-full object-cover" layout="hero" %}{% endif %}</div>
|
||||||
|
<div class="flex flex-col justify-center px-6 py-16 lg:px-12 {% if mediaPosition == "left" %}order-2 lg:order-2{% else %}order-2 lg:order-1{% endif %}"><div class="max-w-lg text-left">{{ herocontent("left") }}</div></div>
|
||||||
|
</section>
|
||||||
|
{% else %}
|
||||||
|
{% set align = "center" %}
|
||||||
|
{% if variant == "left-aligned" %}{% set align = "left" %}{% endif %}
|
||||||
|
<section data-block-override="magazine-bold:hero" class="hero relative flex flex-col overflow-hidden bg-background text-foreground {% if minHeight == "screen" %}min-h-screen{% elif minHeight == "50vh" %}min-h-[50vh]{% elif minHeight == "auto" %}min-h-[400px]{% else %}min-h-[75vh]{% endif %} {% if verticalAlign == "top" %}justify-start pt-20{% elif verticalAlign == "bottom" %}justify-end pb-20{% else %}justify-center{% endif %}{% if class %} {{ class }}{% endif %}">
|
||||||
|
{% if hasImage %}<div class="absolute inset-0 z-0">{% img backgroundImage alt=headlineText class="h-full w-full object-cover" layout="hero" %}{% if showOverlay %}<div class="absolute inset-0 {% if overlayColor == "light" %}bg-background/60{% elif overlayColor == "primary" %}bg-primary/50{% elif overlayOpacity <= 30 %}bg-black/30{% elif overlayOpacity <= 50 %}bg-black/50{% elif overlayOpacity <= 70 %}bg-black/70{% else %}bg-black/80{% endif %}"></div>{% endif %}</div>{% endif %}
|
||||||
|
<div class="relative z-10 mx-auto w-full max-w-4xl px-4 sm:px-6 lg:px-8 {% if variant == "left-aligned" %}text-left{% else %}text-center{% endif %}"><div class="mb-panel px-6 py-10 sm:px-10 sm:py-14">{{ herocontent(align) }}</div></div>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
31
templates/overrides/magazine-bold/hours-location.ninjatpl
Normal file
31
templates/overrides/magazine-bold/hours-location.ninjatpl
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{# hours-location override (magazine-bold) — typographic hours ledger + hairline-framed map. Map facade <button onclick> preserved verbatim. Fields mirror builtin: heading, hours[].day/.hours/.closed, specialHoursNote, address, phone, directionsUrl, mapEmbedUrl, mapButtonLabel. #}
|
||||||
|
<section data-block-override="magazine-bold:hours-location" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-5xl px-4">
|
||||||
|
{% if heading %}<h2 class="mb-caps text-3xl md:text-4xl text-center" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--foreground));">{{ heading }}</h2>{% endif %}
|
||||||
|
<div class="mt-10 grid grid-cols-1 gap-10 md:grid-cols-2">
|
||||||
|
<div class="mb-panel bg-card p-6 md:p-8">
|
||||||
|
{% if hours %}
|
||||||
|
<table class="w-full text-sm">
|
||||||
|
<tbody class="divide-y divide-[hsl(var(--border))]">
|
||||||
|
{% for row in hours %}
|
||||||
|
<tr>
|
||||||
|
<th scope="row" class="mb-caps py-2 pr-4 text-left text-xs font-medium" style="color:hsl(var(--foreground));">{{ row.day }}</th>
|
||||||
|
<td class="py-2 text-right tabular-nums {% if row.closed %}italic{% endif %}" style="{% if row.closed %}color:hsl(var(--muted-foreground));{% else %}color:hsl(var(--accent));{% endif %}">{% if row.closed %}Closed{% else %}{{ row.hours }}{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
{% if specialHoursNote %}<p class="mt-4 border-t border-[hsl(var(--border))] pt-3 text-sm italic" style="color:hsl(var(--muted-foreground));">{{ specialHoursNote }}</p>{% endif %}
|
||||||
|
{% if address %}<div class="mt-6"><div class="mb-caps text-xs" style="color:hsl(var(--muted-foreground));">Address</div><address class="mt-1 whitespace-pre-line not-italic" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif);">{{ address }}</address></div>{% endif %}
|
||||||
|
{% if phone %}<div class="mt-4"><div class="mb-caps text-xs" style="color:hsl(var(--muted-foreground));">Reservations</div><a href="tel:{{ phone }}" class="mb-link mt-1 inline-block">{{ phone }}</a></div>{% endif %}
|
||||||
|
{% if directionsUrl %}<a href="{{ directionsUrl }}" target="_blank" rel="noopener noreferrer" class="mb-button mt-6 inline-flex items-center gap-2">::lucide:navigation:sm:: Get directions</a>{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if mapEmbedUrl %}
|
||||||
|
<div class="aspect-square w-full overflow-hidden bg-muted md:aspect-auto">
|
||||||
|
<button type="button" data-map-src="{{ mapEmbedUrl }}" onclick="var f=document.createElement('iframe');f.src=this.getAttribute('data-map-src');f.setAttribute('class','h-full w-full border-0');f.setAttribute('loading','lazy');f.setAttribute('referrerpolicy','no-referrer-when-downgrade');f.setAttribute('title','Map');this.parentNode.replaceChild(f,this);" class="mb-caps flex h-full min-h-[16rem] w-full items-center justify-center gap-2 text-sm transition-colors hover:bg-accent hover:text-accent-foreground" style="color:hsl(var(--muted-foreground));">::lucide:map-pin:md:: {{ mapButtonLabel|default:"Load map" }}</button>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
@ -1 +1,11 @@
|
|||||||
{% if src %}<figure class="my-8{% if fullBleed == true or fullBleed == "true" %} mb-fullbleed{% endif %}"><img src="{{ src }}" alt="{{ alt }}" class="w-full h-auto block" loading="lazy">{% if caption %}<figcaption class="mb-caption font-mono mt-2 px-6">{{ caption }}</figcaption>{% endif %}</figure>{% endif %}
|
{# image override — plain magazine-bold plate, no ornament frame. Fields mirror builtin: src, alt, caption, size, align, aspectRatio, objectFit. Media resolved via {% img %} (media filter is not registered in the block render path). #}
|
||||||
|
{% with size=size|default:"full" align=align|default:"center" objectFit=objectFit|default:"cover" %}
|
||||||
|
<figure data-block-override="magazine-bold:image" class="my-8 flex-1 {% if align == "left" %}flex justify-start{% elif align == "right" %}flex justify-end{% else %}flex justify-center{% endif %}{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="{% if size == "xs" %}max-w-[200px] w-full{% elif size == "sm" %}max-w-[400px] w-full{% elif size == "md" %}max-w-[600px] w-full{% elif size == "lg" %}max-w-[800px] w-full{% else %}w-full{% endif %}">
|
||||||
|
<div class="relative overflow-hidden bg-muted {% if aspectRatio == "square" %}aspect-square{% elif aspectRatio == "video" %}aspect-video{% elif aspectRatio == "portrait" %}aspect-[3/4]{% elif aspectRatio == "wide" %}aspect-[21/9]{% endif %}">
|
||||||
|
{% if objectFit == "contain" %}{% img src alt=alt class="w-full h-full object-contain" layout=size %}{% elif objectFit == "fill" %}{% img src alt=alt class="w-full h-full object-fill" layout=size %}{% elif objectFit == "none" %}{% img src alt=alt class="w-full h-full object-none" layout=size %}{% else %}{% img src alt=alt class="w-full h-full object-cover" layout=size %}{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if caption %}<figcaption class="mt-3 text-center text-sm italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Inter', system-ui, sans-serif);">{{ caption }}</figcaption>{% endif %}
|
||||||
|
</figure>
|
||||||
|
{% endwith %}
|
||||||
|
|||||||
15
templates/overrides/magazine-bold/logo-strip.ninjatpl
Normal file
15
templates/overrides/magazine-bold/logo-strip.ninjatpl
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<section data-block-override="magazine-bold:logo-strip" class="py-12 md:py-16 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-6xl px-4">
|
||||||
|
{% if heading %}<div class="mb-8 flex items-center justify-center gap-4"><span class="mb-rule w-12"></span><p class="mb-caps text-xs text-muted-foreground">{{ heading }}</p><span class="mb-rule w-12"></span></div>{% endif %}
|
||||||
|
<div class="mt-8 {% if marquee %}group relative overflow-hidden [mask-image:linear-gradient(to_right,transparent,black_8%,black_92%,transparent)]{% endif %}">
|
||||||
|
<div class="flex flex-wrap items-center justify-center gap-x-10 gap-y-6 {% if marquee %}flex-nowrap [animation:logo-marquee_30s_linear_infinite] motion-reduce:[animation:none] group-hover:[animation-play-state:paused]{% endif %}">
|
||||||
|
{% for logo in logos %}
|
||||||
|
{% if logo.url %}<a href="{{ logo.url }}" class="block shrink-0 opacity-80 transition hover:opacity-100">{% endif %}
|
||||||
|
<span class="inline-flex h-8 items-center{% if grayscale %} grayscale transition-[filter] hover:grayscale-0{% endif %}">{% img logo.image alt=logo.alt class="h-8 w-auto object-contain" %}</span>
|
||||||
|
{% if logo.url %}</a>{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% if marquee %}<style>@keyframes logo-marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}</style>{% endif %}
|
||||||
30
templates/overrides/magazine-bold/menu-list.ninjatpl
Normal file
30
templates/overrides/magazine-bold/menu-list.ninjatpl
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{# menu-list override — magazine-bold menu: Archivo Black section titles, hairline leader rules, generous columns. Fields mirror builtin: heading, intro, columns, sections[].title/.note/.items[].name/.badges/.description/.price, currency, footnote. #}
|
||||||
|
<section data-block-override="magazine-bold:menu-list" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-5xl px-4 text-center">
|
||||||
|
{% if heading %}<h2 class="mb-caps text-3xl md:text-4xl" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--foreground));">{{ heading }}</h2>{% endif %}
|
||||||
|
{% if intro %}<p class="mx-auto mt-3 max-w-2xl text-lg italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Inter', system-ui, sans-serif);">{{ intro }}</p>{% endif %}
|
||||||
|
<span class="mb-rule w-16 mx-auto mt-6 mb-4 block" aria-hidden="true"></span>
|
||||||
|
<div class="mt-6 grid grid-cols-1 gap-x-14 gap-y-12 text-left {% if columns == 2 %}md:grid-cols-2{% endif %}">
|
||||||
|
{% for section in sections %}
|
||||||
|
<div class="menu-section">
|
||||||
|
{% if section.title %}<h3 class="mb-caps mb-underline pb-2 text-center text-xl" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--accent));">{{ section.title }}</h3>{% endif %}
|
||||||
|
{% if section.note %}<p class="mt-2 text-center text-sm italic" style="color:hsl(var(--muted-foreground));">{{ section.note }}</p>{% endif %}
|
||||||
|
<ul class="mt-5 space-y-5">
|
||||||
|
{% for item in section.items %}
|
||||||
|
<li>
|
||||||
|
<div class="flex items-baseline gap-3">
|
||||||
|
<span class="shrink-0 font-medium" style="color:hsl(var(--foreground));">{{ item.name }}</span>
|
||||||
|
<span class="min-w-6 flex-1 translate-y-[-2px] border-b border-dotted border-[hsl(var(--border))]" aria-hidden="true"></span>
|
||||||
|
{% if item.price %}<span class="shrink-0 tabular-nums" style="color:hsl(var(--accent));font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);">{% if currency %}{{ currency }}{% endif %}{{ item.price }}</span>{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if item.badges %}<div class="mt-1 flex flex-wrap gap-1.5">{% for badge in item.badges %}{% if badge.label %}<span class="mb-caps inline-block border border-[hsl(var(--border))] px-2 py-0.5 text-[0.65rem]" style="color:hsl(var(--muted-foreground));">{{ badge.label }}</span>{% endif %}{% endfor %}</div>{% endif %}
|
||||||
|
{% if item.description %}<p class="mt-1 text-sm italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Inter', system-ui, sans-serif);">{{ item.description }}</p>{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% if footnote %}<p class="mt-12 text-xs" style="color:hsl(var(--muted-foreground));">{{ footnote }}</p>{% endif %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
104
templates/overrides/magazine-bold/navbar.ninjatpl
Normal file
104
templates/overrides/magazine-bold/navbar.ninjatpl
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
{# navbar override — magazine-bold masthead. Structure, fixed drawer ids, focus-trap JS and all data-* preserved verbatim from the builtin; presentation reskinned to magazine-bold (hairline rules, small-caps labels). Fields identical: variant/stickyBehavior/megaMenu/logoType/logoText/logo/companyName/cta/menu.items/... #}
|
||||||
|
{% set nv = variant %}
|
||||||
|
{% if not nv %}{% if template == "centered" %}{% set nv = "centered-logo" %}{% elif template == "transparent" %}{% set nv = "transparent-over-hero" %}{% else %}{% set nv = "standard" %}{% endif %}{% endif %}
|
||||||
|
{% set sb = stickyBehavior %}
|
||||||
|
{% if not sb %}{% if sticky %}{% set sb = "sticky" %}{% else %}{% set sb = "none" %}{% endif %}{% endif %}
|
||||||
|
{% set use_mega = megaMenu or template == "mega" %}
|
||||||
|
{% set is_transparent = nv == "transparent-over-hero" %}
|
||||||
|
{% set cta_text = cta.text|default:ctaLabel %}
|
||||||
|
{% set cta_url = cta.url|default:ctaUrl %}
|
||||||
|
{% set bg = backgroundColor|default:"bg-background" %}
|
||||||
|
{% macro navlink(item) %}<a href="{{ item.url|default:'#' }}"{% if item.open_in_new_tab %} target="_blank" rel="noopener"{% endif %}{% if item.title %} title="{{ item.title }}"{% endif %} class="mb-caps px-3 py-2 text-xs text-foreground/80 hover:text-[hsl(var(--accent))] transition-colors {{ item.css_class }}">{{ item.label }}</a>{% endmacro %}
|
||||||
|
{% macro dropdown(item) %}<div class="relative group">
|
||||||
|
<button type="button" class="mb-caps px-3 py-2 text-xs text-foreground/80 hover:text-[hsl(var(--accent))] transition-colors inline-flex items-center gap-1">{{ item.label }}<svg class="h-4 w-4 transition-transform group-hover:rotate-180" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg></button>
|
||||||
|
{% if use_mega %}<div class="absolute left-1/2 -translate-x-1/2 mt-1 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50"><div class="mb-panel p-6 bg-card grid gap-8" style="grid-template-columns:repeat({{ item.children|length|default:1 }},minmax(10rem,1fr));min-width:min(90vw,42rem)">{% for child in item.children %}<div class="space-y-1">{% if child.item_type == "header" %}<div class="mb-caps px-2 py-1 text-[0.65rem] text-muted-foreground border-b border-[hsl(var(--border))] mb-2">{{ child.label }}</div>{% else %}<a href="{{ child.url|default:'#' }}"{% if child.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="block px-2 py-2 hover:bg-accent transition-colors {{ child.css_class }}">{% if child.icon %}<span class="mr-1.5 text-[hsl(var(--accent))]">::{{ child.icon }}::</span>{% endif %}<span class="text-sm font-medium text-foreground">{{ child.label }}</span>{% if child.description %}<span class="block text-xs text-muted-foreground mt-0.5">{{ child.description }}</span>{% endif %}</a>{% endif %}</div>{% endfor %}</div></div>
|
||||||
|
{% else %}<div class="absolute left-0 mt-1 w-56 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50"><div class="mb-panel py-2 bg-card">{% for child in item.children %}{% if child.item_type == "header" %}<div class="mb-caps px-4 py-2 text-[0.65rem] text-muted-foreground">{{ child.label }}</div>{% else %}<a href="{{ child.url|default:'#' }}"{% if child.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="block px-4 py-2 text-sm text-foreground hover:bg-accent transition-colors {{ child.css_class }}">{{ child.label }}</a>{% endif %}{% endfor %}</div></div>{% endif %}
|
||||||
|
</div>{% endmacro %}
|
||||||
|
{% macro brand(size) %}<a href="/" class="flex-shrink-0 flex items-center">{% if logoType == "text" and logoText.text %}<span class="mb-caps text-xl {{ logoText.color }}" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--foreground));">{{ logoText.text }}</span>{% elif logo %}<img src="{{ logo }}" alt="{{ alt|default:companyName }}" class="{{ size }} w-auto"{% if logoMaxWidth or logoMaxHeight %} style="{% if logoMaxWidth %}max-width:{{ logoMaxWidth }};{% endif %}{% if logoMaxHeight %}max-height:{{ logoMaxHeight }};{% endif %}"{% endif %}/>{% elif companyName %}<span class="mb-caps text-xl" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--foreground));">{{ companyName }}</span>{% endif %}</a>{% endmacro %}
|
||||||
|
{% macro ctabtn(extra) %}{% if cta_text %}<a href="{{ cta_url|default:'#' }}" class="mb-button inline-flex items-center {{ extra }}">{{ cta_text }}</a>{% endif %}{% endmacro %}
|
||||||
|
<nav data-block-override="magazine-bold:navbar" data-bn-navbar data-bn-nav-variant="{{ nv }}" data-bn-sticky="{{ sb }}"{% if is_transparent %} data-bn-transparent="1"{% endif %} class="bn-navbar w-full {% if sb == "sticky" or sb == "shrink-on-scroll" %}sticky top-0 z-40 {% endif %}{% if is_transparent %}absolute top-0 left-0 z-40 text-white{% else %}{{ bg }} border-b border-[hsl(var(--border))]{% endif %} {{ class }}">
|
||||||
|
{% if showUtilityBar %}<div class="w-full text-xs {% if not showOnMobile %}hidden sm:block{% endif %} mb-panel"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex items-center justify-between h-8"><div>{{ utilityBarText|safe }}</div><div class="flex items-center gap-4">{% if utilityBarPhone %}<a href="tel:{{ utilityBarPhone }}" class="hover:text-[hsl(var(--accent))] transition-colors">{{ utilityBarPhone }}</a>{% endif %}{% if utilityBarEmail %}<a href="mailto:{{ utilityBarEmail }}" class="hover:text-[hsl(var(--accent))] transition-colors">{{ utilityBarEmail }}</a>{% endif %}</div></div></div>{% endif %}
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="bn-navbar-row flex items-center justify-between h-16">
|
||||||
|
{% if nv == "centered-logo" %}
|
||||||
|
<div class="hidden md:flex items-center gap-1 flex-1">{% for item in menu.items %}{% if item.children %}{{ dropdown(item) }}{% else %}{{ navlink(item) }}{% endif %}{% endfor %}</div>
|
||||||
|
<div class="flex items-center justify-center">{{ brand("h-9") }}</div>
|
||||||
|
<div class="hidden md:flex items-center gap-3 flex-1 justify-end">{{ ctabtn("") }}</div>
|
||||||
|
{% elif nv == "split" %}
|
||||||
|
<div class="hidden md:flex items-center gap-1 flex-1">{% for item in menu.items %}{% if forloop.Counter0 * 2 < menu.items|length %}{% if item.children %}{{ dropdown(item) }}{% else %}{{ navlink(item) }}{% endif %}{% endif %}{% endfor %}</div>
|
||||||
|
<div class="flex items-center justify-center">{{ brand("h-9") }}</div>
|
||||||
|
<div class="hidden md:flex items-center gap-1 flex-1 justify-end">{% for item in menu.items %}{% if forloop.Counter0 * 2 >= menu.items|length %}{% if item.children %}{{ dropdown(item) }}{% else %}{{ navlink(item) }}{% endif %}{% endif %}{% endfor %}{{ ctabtn("ml-3") }}</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="flex items-center">{{ brand("h-8") }}</div>
|
||||||
|
<div class="hidden md:flex items-center gap-1">{% for item in menu.items %}{% if item.children %}{{ dropdown(item) }}{% else %}{{ navlink(item) }}{% endif %}{% endfor %}{{ ctabtn("ml-3") }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="flex items-center md:hidden">
|
||||||
|
<label for="bn-navbar-toggle" data-bn-nav-open aria-controls="bn-navbar-drawer" aria-expanded="false" class="inline-flex items-center justify-center p-2 rounded-md cursor-pointer hover:bg-accent/50 transition-colors"><span class="sr-only">Open menu</span><svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{# --- Mobile off-canvas drawer: pure-CSS checkbox baseline (works with no JS) --- #}
|
||||||
|
<input type="checkbox" id="bn-navbar-toggle" class="bn-navbar-toggle" aria-hidden="true"/>
|
||||||
|
<label for="bn-navbar-toggle" class="bn-navbar-overlay fixed inset-0 bg-black/50 z-40 md:hidden" aria-hidden="true"></label>
|
||||||
|
<div id="bn-navbar-drawer" class="bn-navbar-drawer fixed top-0 right-0 h-full w-80 max-w-[85vw] bg-background text-foreground z-50 shadow-xl md:hidden flex flex-col border-l border-[hsl(var(--border))]" role="dialog" aria-modal="true" aria-label="Site menu">
|
||||||
|
<div class="flex items-center justify-between p-4 border-b border-[hsl(var(--border))]">{{ brand("h-8") }}<label for="bn-navbar-toggle" data-bn-nav-close class="p-2 rounded-md cursor-pointer text-muted-foreground hover:text-[hsl(var(--accent))] hover:bg-accent"><span class="sr-only">Close menu</span><svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg></label></div>
|
||||||
|
<nav class="flex-1 overflow-y-auto py-4" aria-label="Mobile">{% for item in menu.items %}{% if item.children %}<details class="group/acc"><summary class="mb-caps flex items-center justify-between px-6 py-3 text-sm cursor-pointer hover:bg-accent transition-colors list-none">{{ item.label }}<svg class="h-4 w-4 transition-transform group-open/acc:rotate-180" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg></summary><div class="pl-8 pb-2">{% for child in item.children %}{% if child.item_type != "header" %}<a href="{{ child.url|default:'#' }}"{% if child.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="block px-2 py-2 text-sm text-muted-foreground hover:text-[hsl(var(--accent))] transition-colors">{{ child.label }}</a>{% endif %}{% endfor %}</div></details>{% else %}<a href="{{ item.url|default:'#' }}"{% if item.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="mb-caps block px-6 py-3 text-sm hover:bg-accent transition-colors">{{ item.label }}</a>{% endif %}{% endfor %}</nav>
|
||||||
|
{% if cta_text %}<div class="p-4 border-t border-[hsl(var(--border))]"><a href="{{ cta_url|default:'#' }}" class="mb-button block w-full text-center">{{ cta_text }}</a></div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<style>
|
||||||
|
.bn-navbar-toggle{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none;}
|
||||||
|
.bn-navbar-drawer{transform:translateX(100%);transition:transform .3s ease;}
|
||||||
|
.bn-navbar-overlay{opacity:0;pointer-events:none;transition:opacity .3s ease;}
|
||||||
|
.bn-navbar-toggle:checked ~ .bn-navbar-drawer{transform:translateX(0);}
|
||||||
|
.bn-navbar-toggle:checked ~ .bn-navbar-overlay{opacity:1;pointer-events:auto;}
|
||||||
|
.bn-navbar-drawer details > summary::-webkit-details-marker{display:none;}
|
||||||
|
[data-bn-sticky="shrink-on-scroll"].bn-navbar-shrunk .bn-navbar-row{height:3rem;}
|
||||||
|
[data-bn-sticky="shrink-on-scroll"] .bn-navbar-row{transition:height .2s ease;}
|
||||||
|
[data-bn-transparent].bn-navbar-solid{position:sticky;color:inherit;background:hsl(var(--background));border-bottom:1px solid hsl(var(--primary) / 0.4);}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
if(window.__bnNavInit)return;window.__bnNavInit=1;
|
||||||
|
function ready(fn){if(document.readyState!='loading')fn();else document.addEventListener('DOMContentLoaded',fn);}
|
||||||
|
ready(function(){
|
||||||
|
document.querySelectorAll('[data-bn-navbar]').forEach(function(nav){
|
||||||
|
var toggle=nav.querySelector('.bn-navbar-toggle');
|
||||||
|
var drawer=nav.querySelector('.bn-navbar-drawer');
|
||||||
|
var opener=nav.querySelector('[data-bn-nav-open]');
|
||||||
|
if(toggle&&drawer){
|
||||||
|
function focusables(){return drawer.querySelectorAll('a[href],button,label[for],input,summary,[tabindex]:not([tabindex="-1"])');}
|
||||||
|
toggle.addEventListener('change',function(){
|
||||||
|
var open=toggle.checked;
|
||||||
|
document.documentElement.style.overflow=open?'hidden':'';
|
||||||
|
document.body.style.overflow=open?'hidden':'';
|
||||||
|
if(opener)opener.setAttribute('aria-expanded',open?'true':'false');
|
||||||
|
if(open){var f=focusables();if(f.length)f[0].focus();}
|
||||||
|
else if(opener&&opener.focus)opener.focus();
|
||||||
|
});
|
||||||
|
drawer.addEventListener('keydown',function(e){
|
||||||
|
if(e.key==='Escape'){toggle.checked=false;toggle.dispatchEvent(new Event('change'));return;}
|
||||||
|
if(e.key!=='Tab')return;
|
||||||
|
var f=focusables();if(!f.length)return;
|
||||||
|
var first=f[0],last=f[f.length-1];
|
||||||
|
if(e.shiftKey&&document.activeElement===first){e.preventDefault();last.focus();}
|
||||||
|
else if(!e.shiftKey&&document.activeElement===last){e.preventDefault();first.focus();}
|
||||||
|
});
|
||||||
|
drawer.querySelectorAll('a[href]').forEach(function(a){a.addEventListener('click',function(){toggle.checked=false;toggle.dispatchEvent(new Event('change'));});});
|
||||||
|
}
|
||||||
|
var sb=nav.getAttribute('data-bn-sticky');
|
||||||
|
var transparent=nav.getAttribute('data-bn-transparent')==='1';
|
||||||
|
if(sb==='shrink-on-scroll'||transparent){
|
||||||
|
var overHero=false;
|
||||||
|
if(transparent){var next=nav.nextElementSibling;overHero=!!next&&next.getBoundingClientRect().height>=240;if(!overHero)nav.classList.add('bn-navbar-solid');}
|
||||||
|
var onScroll=function(){
|
||||||
|
var y=window.scrollY||window.pageYOffset;
|
||||||
|
if(sb==='shrink-on-scroll')nav.classList.toggle('bn-navbar-shrunk',y>40);
|
||||||
|
if(transparent&&overHero)nav.classList.toggle('bn-navbar-solid',y>40);
|
||||||
|
};
|
||||||
|
window.addEventListener('scroll',onScroll,{passive:true});onScroll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
13
templates/overrides/magazine-bold/newsletter-signup.ninjatpl
Normal file
13
templates/overrides/magazine-bold/newsletter-signup.ninjatpl
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{# newsletter-signup (magazine-bold) — the {% signup_form %} tag carries the #}
|
||||||
|
{# untouchable subscribe wiring (/api/lists/subscribe HTMX post + honeypot); #}
|
||||||
|
{# this template wraps it in a quiet mb-panel — no loud filled band. #}
|
||||||
|
<section data-block-override="magazine-bold:newsletter-signup" class="py-12 bg-background text-foreground md:py-16{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-3xl px-4">
|
||||||
|
<div class="mb-panel {% if layout == "inline" %}bg-card px-6 py-12 text-card-foreground md:px-12{% else %}bg-card px-6 py-12 text-card-foreground md:px-10{% endif %} text-center">
|
||||||
|
<div class="mb-4 flex items-center justify-center gap-4"><span class="mb-rule w-12"></span><span class="mb-rule w-12"></span></div>
|
||||||
|
{% if title %}<h2 class="mb-caps text-2xl md:text-3xl" style="font-family:var(--font-heading, "Archivo Black", 'Arial Black', sans-serif)">{{ title }}</h2>{% endif %}
|
||||||
|
{% if description %}<p class="mx-auto mt-4 max-w-xl {% if layout == "inline" %}text-muted-foreground{% else %}text-muted-foreground{% endif %}" style="font-family:var(--font-body, "Inter", system-ui, sans-serif)">{{ description }}</p>{% endif %}
|
||||||
|
<div class="mx-auto mt-6 max-w-md">{% signup_form list=listSlug source=source submit=submitText success_message=successMessage placeholder=placeholder %}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
21
templates/overrides/magazine-bold/page-suggestions.ninjatpl
Normal file
21
templates/overrides/magazine-bold/page-suggestions.ninjatpl
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{# page-suggestions (magazine-bold) — 404 suggestions with underlined heading and fluoro markers. Fields: title, showDescription, emptyMessage; suggestions[] (.slug/.title/.excerpt). #}
|
||||||
|
{% if suggestions %}
|
||||||
|
<div data-block-override="magazine-bold:page-suggestions" class="page-suggestions py-10">
|
||||||
|
{% if title %}<h2 class="mb-caps mb-underline mx-auto w-fit text-xl text-center" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--foreground));">{{ title }}</h2>{% endif %}
|
||||||
|
<ul class="mt-6 space-y-3 max-w-lg mx-auto">
|
||||||
|
{% for suggestion in suggestions %}
|
||||||
|
<li class="flex items-start gap-3">
|
||||||
|
<span class="mt-0.5 shrink-0" style="color:hsl(var(--accent));">::lucide:chevron-right:sm::</span>
|
||||||
|
<div>
|
||||||
|
<a href="{{ suggestion.slug }}" class="mb-link font-medium">{{ suggestion.title }}</a>
|
||||||
|
{% if showDescription and suggestion.excerpt %}<p class="mt-1 text-sm italic" style="color:hsl(var(--muted-foreground));">{{ suggestion.excerpt }}</p>{% endif %}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% elif emptyMessage %}
|
||||||
|
<div data-block-override="magazine-bold:page-suggestions" class="page-suggestions py-10 text-center">
|
||||||
|
<p class="italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Inter', system-ui, sans-serif);">{{ emptyMessage }}</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
{# password-reset-form (magazine-bold) — request stage in an mb-panel with small-caps label. Wiring UNTOUCHABLE: /api/auth/request-password-reset (email), hx-target #reset-message. #}
|
||||||
|
<div data-block-override="magazine-bold:password-reset-form" class="password-reset-form mb-panel mx-auto max-w-md bg-card p-8">
|
||||||
|
<div id="reset-message"></div>
|
||||||
|
<div id="request-reset-panel" class="text-center">
|
||||||
|
<h2 class="mb-caps text-2xl" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif);color:hsl(var(--foreground));">Reset password</h2>
|
||||||
|
<p class="mt-3 text-sm italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Inter', system-ui, sans-serif);">Enter your email and we will send a link to reset your password.</p>
|
||||||
|
<form hx-post="/api/auth/request-password-reset" hx-target="#reset-message" hx-swap="innerHTML" class="mt-6 text-left">
|
||||||
|
<div class="mb-4"><label class="mb-caps text-muted-foreground block text-xs mb-1">Email</label><input type="email" name="email" required class="w-full mb-input" /></div>
|
||||||
|
<button type="submit" class="mb-button w-full" hx-disabled-elt="this">Send reset link</button>
|
||||||
|
</form>
|
||||||
|
<p class="text-center mt-4 text-sm"><a href="/login" class="mb-link">Back to sign in</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
16
templates/overrides/magazine-bold/popular-posts.ninjatpl
Normal file
16
templates/overrides/magazine-bold/popular-posts.ninjatpl
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{# popular-posts (magazine-bold) — compact ranked list from the `posts` provider, hairline-ruled. #}
|
||||||
|
<section data-block-override="magazine-bold:popular-posts" class="bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
{% if heading %}<h2 class="mb-caps mb-5 text-xs text-muted-foreground">{{ heading }}</h2>{% endif %}
|
||||||
|
<ol class="space-y-4">
|
||||||
|
{% for post in posts %}
|
||||||
|
<li class="mb-rule flex items-start gap-3 pt-4">
|
||||||
|
{% if showRank %}<span class="mt-0.5 text-lg font-bold tabular-nums text-[hsl(var(--accent))]" style="font-family:var(--font-heading, "Archivo Black", 'Arial Black', sans-serif)">{{ forloop.Counter }}</span>{% endif %}
|
||||||
|
{% if showThumbnail and post.featured_image_url %}<a href="{{ post.url }}" class="h-12 w-12 flex-shrink-0 overflow-hidden bg-muted">{% img post.featured_image_url alt=post.title class="h-full w-full object-cover" %}</a>{% endif %}
|
||||||
|
<div class="min-w-0">
|
||||||
|
<h3 class="mb-caps text-sm leading-snug text-foreground" style="font-family:var(--font-heading, "Archivo Black", 'Arial Black', sans-serif)"><a href="{{ post.url }}" class="transition-colors hover:text-[hsl(var(--accent))]">{{ post.title }}</a></h3>
|
||||||
|
{% if post.published_at %}<p class="mt-1 text-xs text-muted-foreground">{{ post.published_at|date:"M j, Y" }}</p>{% endif %}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
23
templates/overrides/magazine-bold/post-hero.ninjatpl
Normal file
23
templates/overrides/magazine-bold/post-hero.ninjatpl
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{# post-hero (magazine-bold) — current post from the `post` provider. When the post #}
|
||||||
|
{# has a featured image it becomes an overlaid banner; otherwise a clean text hero. #}
|
||||||
|
{% set center = align != "left" %}
|
||||||
|
{% if post.featured_image_url %}
|
||||||
|
<section data-block-override="magazine-bold:post-hero" class="hero relative flex min-h-[50vh] flex-col justify-end overflow-hidden bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="absolute inset-0 z-0">{% img post.featured_image_url alt=post.featured_image_alt class="h-full w-full object-cover" layout="hero" %}<div class="absolute inset-0 bg-black/70"></div></div>
|
||||||
|
<div class="relative z-10 mx-auto w-full max-w-4xl px-4 py-14 sm:px-6 lg:px-8 {% if center %}text-center{% endif %}">
|
||||||
|
{% if center %}<div class="mb-4 flex items-center justify-center gap-4"><span class="mb-rule w-12"></span><span class="mb-rule w-12"></span></div>{% endif %}
|
||||||
|
<h1 class="mb-caps text-3xl leading-tight text-white sm:text-4xl lg:text-5xl" style="font-family:var(--font-heading, "Archivo Black", 'Arial Black', sans-serif)">{{ post.title }}</h1>
|
||||||
|
{% if showExcerpt and post.excerpt %}<p class="mt-5 max-w-2xl text-lg text-white/80{% if center %} mx-auto{% endif %}" style="font-family:var(--font-body, "Inter", system-ui, sans-serif)">{{ post.excerpt }}</p>{% endif %}
|
||||||
|
{% if showMeta %}<div class="mt-6 flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-white/80{% if center %} justify-center{% endif %}">{% if post.author_name %}<span class="mb-caps text-white">{{ post.author_name }}</span>{% endif %}{% if post.published_at %}<span>{{ post.published_at|date:"F j, Y" }}</span>{% endif %}{% if post.reading_time %}<span>{{ post.reading_time }} min read</span>{% endif %}</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% else %}
|
||||||
|
<section data-block-override="magazine-bold:post-hero" class="bg-background py-16 text-foreground md:py-20{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto w-full max-w-3xl px-4 {% if center %}text-center{% endif %}">
|
||||||
|
{% if center %}<div class="mb-4 flex items-center justify-center gap-4"><span class="mb-rule w-12"></span><span class="mb-rule w-12"></span></div>{% endif %}
|
||||||
|
<h1 class="mb-caps mb-underline text-3xl leading-tight sm:text-4xl lg:text-5xl text-foreground" style="font-family:var(--font-heading, "Archivo Black", 'Arial Black', sans-serif)">{{ post.title }}</h1>
|
||||||
|
{% if showExcerpt and post.excerpt %}<p class="mt-5 text-lg text-muted-foreground{% if center %} mx-auto max-w-2xl{% endif %}" style="font-family:var(--font-body, "Inter", system-ui, sans-serif)">{{ post.excerpt }}</p>{% endif %}
|
||||||
|
{% if showMeta %}<div class="mt-6 flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-muted-foreground{% if center %} justify-center{% endif %}">{% if post.author_name %}<span class="mb-caps text-[hsl(var(--accent))]">{{ post.author_name }}</span>{% endif %}{% if post.published_at %}<span>{{ post.published_at|date:"F j, Y" }}</span>{% endif %}{% if post.reading_time %}<span>{{ post.reading_time }} min read</span>{% endif %}</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
9
templates/overrides/magazine-bold/post-metadata.ninjatpl
Normal file
9
templates/overrides/magazine-bold/post-metadata.ninjatpl
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{# post-metadata (magazine-bold) — author/date/reading-time row from the `post` provider, hairline chips. #}
|
||||||
|
{% if post %}
|
||||||
|
<div data-block-override="magazine-bold:post-metadata" class="flex flex-wrap items-center gap-x-4 gap-y-2 text-sm text-muted-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
{% if showAuthor and post.author_name %}<span class="flex items-center gap-2">{% if showAvatar and post.author_avatar %}{% img post.author_avatar alt=post.author_name class="h-7 w-7 rounded-full object-cover ring-1 ring-[hsl(var(--border))]" %}{% endif %}<span class="mb-caps text-[hsl(var(--accent))]">{{ post.author_name }}</span></span>{% endif %}
|
||||||
|
{% if showDate and post.published_at %}<time datetime="{{ post.published_at|date:"Y-m-d" }}">{{ post.published_at|date:"F j, Y" }}</time>{% endif %}
|
||||||
|
{% if showReadingTime and post.reading_time %}<span>{{ post.reading_time }} min read</span>{% endif %}
|
||||||
|
{% if post.categories %}<span class="flex flex-wrap gap-1.5">{% for cat in post.categories %}<a href="{{ cat.url|default:'#' }}" class="mb-caps rounded-none border border-[hsl(var(--border))] px-2 py-0.5 text-xs text-muted-foreground transition-colors hover:text-[hsl(var(--accent))]">{{ cat.name }}</a>{% endfor %}</span>{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
25
templates/overrides/magazine-bold/pricing.ninjatpl
Normal file
25
templates/overrides/magazine-bold/pricing.ninjatpl
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<section data-block-override="magazine-bold:pricing" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-6xl px-4">
|
||||||
|
{% if heading %}<h2 class="mb-caps text-center text-3xl text-foreground md:text-4xl" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ heading }}</h2>{% endif %}
|
||||||
|
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ intro }}</p>{% endif %}
|
||||||
|
<div class="mt-12 grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{% for tier in tiers %}
|
||||||
|
<div class="relative flex flex-col p-6 {% if tier.highlighted %}mb-panel border-[hsl(var(--accent))] bg-card text-card-foreground{% else %}rounded-none border border-[hsl(var(--border))] bg-card text-card-foreground{% endif %}">
|
||||||
|
{% if tier.badge %}<span class="mb-caps absolute -top-3 left-1/2 -translate-x-1/2 border border-[hsl(var(--accent))] bg-card px-3 py-1 text-xs text-[hsl(var(--accent))]">{{ tier.badge }}</span>{% endif %}
|
||||||
|
<h3 class="mb-caps text-center text-lg text-foreground" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ tier.name }}</h3>
|
||||||
|
<div class="mx-auto my-3 flex items-center justify-center"><span class="mb-rule w-12"></span></div>
|
||||||
|
<p class="flex items-baseline justify-center gap-1">
|
||||||
|
<span class="text-4xl font-bold tracking-tight text-[hsl(var(--accent))]" style="font-family:var(--font-heading)">{% if currency %}{{ currency }}{% else %}${% endif %}{{ tier.price }}</span>
|
||||||
|
{% if tier.period %}<span class="text-sm text-muted-foreground">{{ tier.period }}</span>{% endif %}
|
||||||
|
</p>
|
||||||
|
{% if tier.description %}<p class="mt-3 text-center text-sm text-muted-foreground" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ tier.description }}</p>{% endif %}
|
||||||
|
{% if tier.features %}<ul class="mt-6 flex-1 space-y-3 text-sm">
|
||||||
|
{% for feature in tier.features %}<li class="flex items-start gap-2"><svg class="mt-0.5 h-4 w-4 shrink-0 text-[hsl(var(--accent))]" aria-hidden="true"><use href="/icons/lucide.svg#check"></use></svg><span>{{ feature }}</span></li>{% endfor %}
|
||||||
|
</ul>{% endif %}
|
||||||
|
{% if tier.ctaUrl and tier.ctaLabel %}{% if tier.highlighted %}<a href="{{ tier.ctaUrl }}" class="mb-button mt-8 inline-flex w-full items-center justify-center">{{ tier.ctaLabel }}</a>{% else %}<a href="{{ tier.ctaUrl }}" class="mb-link mt-8 inline-flex w-full items-center justify-center">{{ tier.ctaLabel }}</a>{% endif %}{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% if billingNote %}<p class="mt-8 text-center text-sm text-muted-foreground">{{ billingNote }}</p>{% endif %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
8
templates/overrides/magazine-bold/quote.ninjatpl
Normal file
8
templates/overrides/magazine-bold/quote.ninjatpl
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{# quote override — magazine-bold pull-quote: large italic Inter, hairline panel or fluoro accent rule; centred quotes get a short centred hairline. Fields mirror builtin: quote, style, align, cite, attribution, role. #}
|
||||||
|
{% with style=style|default:"block" align=align|default:"left" %}
|
||||||
|
<figure data-block-override="magazine-bold:quote" class="bn-quote my-10 {% if style == "inline-card" %}mb-panel bg-card p-8 md:p-10{% elif style == "pull" %}border-l-2 border-[hsl(var(--accent))] pl-6 md:pl-8 py-2{% else %}px-2 py-1{% endif %}{% if align == "center" %} text-center{% endif %}{% if class %} {{ class }}{% endif %}">
|
||||||
|
{% if align == "center" %}<span class="mb-rule w-16 mx-auto mb-5 block" aria-hidden="true"></span>{% endif %}
|
||||||
|
<blockquote{% if cite %} cite="{{ cite }}"{% endif %} class="{% if style == "pull" %}text-2xl md:text-3xl leading-snug{% else %}text-xl md:text-2xl leading-relaxed{% endif %} italic" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif);color:hsl(var(--foreground));">{{ quote|safe }}</blockquote>
|
||||||
|
{% if attribution or role or cite %}<figcaption class="mt-4 text-sm{% if align == "center" %} flex flex-col items-center{% endif %}" style="color:hsl(var(--muted-foreground));">{% if attribution %}<span class="mb-caps text-xs" style="color:hsl(var(--accent));">{{ attribution }}</span>{% endif %}{% if role %}<span class="{% if attribution %}block {% endif %}italic">{{ role }}</span>{% endif %}{% if cite %}<a href="{{ cite }}" class="mb-link {% if attribution or role %}mt-1 {% endif %}inline-block text-xs">Source</a>{% endif %}</figcaption>{% endif %}
|
||||||
|
</figure>
|
||||||
|
{% endwith %}
|
||||||
17
templates/overrides/magazine-bold/related-posts.ninjatpl
Normal file
17
templates/overrides/magazine-bold/related-posts.ninjatpl
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{# related-posts (magazine-bold) — hairline-topped grid from the `posts` provider. #}
|
||||||
|
{% if posts %}
|
||||||
|
<section data-block-override="magazine-bold:related-posts" class="mb-rule py-12 md:py-16 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-6xl px-4">
|
||||||
|
<h2 class="mb-caps mb-underline mb-10 text-center text-2xl md:text-3xl text-foreground" style="font-family:var(--font-heading, "Archivo Black", 'Arial Black', sans-serif)">{{ heading|default:"Related posts" }}</h2>
|
||||||
|
<div class="grid grid-cols-1 gap-8 sm:grid-cols-2 {% if columns == 2 %}lg:grid-cols-2{% else %}lg:grid-cols-3{% endif %}">
|
||||||
|
{% for post in posts %}
|
||||||
|
<article class="group flex flex-col text-center">
|
||||||
|
{% if post.featured_image_url %}<a href="{{ post.url }}" class="mb-4 block aspect-video overflow-hidden bg-muted">{% img post.featured_image_url alt=post.title class="h-full w-full object-cover transition-transform duration-500 group-hover:scale-105" %}</a>{% endif %}
|
||||||
|
<h3 class="mb-caps text-base leading-snug text-foreground" style="font-family:var(--font-heading, "Archivo Black", 'Arial Black', sans-serif)"><a href="{{ post.url }}" class="transition-colors hover:text-[hsl(var(--accent))]">{{ post.title }}</a></h3>
|
||||||
|
{% if post.published_at %}<p class="mt-2 text-xs text-muted-foreground">{{ post.published_at|date:"M j, Y" }}</p>{% endif %}
|
||||||
|
</article>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
13
templates/overrides/magazine-bold/rich-section.ninjatpl
Normal file
13
templates/overrides/magazine-bold/rich-section.ninjatpl
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<section data-block-override="magazine-bold:rich-section" class="py-16 md:py-24 {% if background == "muted" %}bg-muted text-foreground{% elif background == "card" %}bg-card text-card-foreground{% else %}bg-background text-foreground{% endif %}{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto grid max-w-6xl items-center gap-10 px-4 md:grid-cols-2 md:gap-16">
|
||||||
|
<div class="{% if mediaPosition == "left" %}md:order-2{% else %}md:order-1{% endif %}">
|
||||||
|
{% if eyebrow %}<div class="mb-3 flex items-center gap-4"><span class="mb-rule w-12"></span><p class="mb-caps text-xs text-muted-foreground">{{ eyebrow }}</p></div>{% endif %}
|
||||||
|
{% if heading %}<h2 class="mb-caps mb-underline mt-2 text-3xl text-foreground md:text-4xl" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ heading }}</h2>{% endif %}
|
||||||
|
{% if body %}<div class="mt-4 space-y-4 text-lg text-muted-foreground [&_a]:text-[hsl(var(--accent))] [&_a]:underline [&_strong]:text-foreground" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ body|safe }}</div>{% endif %}
|
||||||
|
{% if ctaUrl and ctaLabel %}<a href="{{ ctaUrl }}" class="mb-button mt-6 inline-flex items-center gap-2">{{ ctaLabel }}<svg class="h-4 w-4" aria-hidden="true"><use href="/icons/lucide.svg#arrow-right"></use></svg></a>{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="{% if mediaPosition == "left" %}md:order-1{% else %}md:order-2{% endif %}">
|
||||||
|
{% if media %}<div class="overflow-hidden">{% img media alt=mediaAlt class="w-full h-auto object-cover" %}</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
2
templates/overrides/magazine-bold/rich-text.ninjatpl
Normal file
2
templates/overrides/magazine-bold/rich-text.ninjatpl
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
{# rich-text override — long-form Inter magazine-bold column with fluoro links and hairline blockquotes. Field: html (mirrors builtin). #}
|
||||||
|
{% if html %}<article data-block-override="magazine-bold:rich-text" class="rich-text prose prose-lg max-w-none [&_a]:text-[hsl(var(--accent))] [&_a]:no-underline [&_a]:border-b [&_a:hover]:text-accent [&_blockquote]:border-l-2 [&_blockquote]:border-[hsl(var(--border))] [&_blockquote]:italic" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif);color:hsl(var(--foreground));font-size:1.1rem;line-height:1.75;">{{ html|safe }}</article>{% endif %}
|
||||||
13
templates/overrides/magazine-bold/sidebar-nav.ninjatpl
Normal file
13
templates/overrides/magazine-bold/sidebar-nav.ninjatpl
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{# sidebar-nav (magazine-bold) — small-caps vertical menu with hairline divider rule and fluoro link/icon accents. #}
|
||||||
|
{% set compact = template == "compact" %}
|
||||||
|
<nav data-block-override="magazine-bold:sidebar-nav" class="flex-1 {% if compact %}w-56 py-4 pr-3{% else %}w-64 py-6 pr-4{% endif %}{% if showBorder %} border-r border-[hsl(var(--border))]{% endif %}{% if class %} {{ class }}{% endif %}">
|
||||||
|
{% if title %}<h3 class="mb-caps mb-underline {% if compact %}px-2 mb-4 text-xs text-muted-foreground{% else %}px-3 mb-5 text-sm text-foreground{% endif %}" style="font-family:var(--font-heading, "Archivo Black", 'Arial Black', sans-serif)">{{ title }}</h3>{% endif %}
|
||||||
|
<ul class="{% if compact %}space-y-0.5{% else %}space-y-1{% endif %}">
|
||||||
|
{% for item in menu.items %}
|
||||||
|
<li><a href="{{ item.url|default:'#' }}"{% if item.open_in_new_tab %} target="_blank" rel="noopener"{% endif %}{% if item.title %} title="{{ item.title }}"{% endif %} data-bn-sidebar-link class="mb-caps flex items-center rounded-none text-sm text-muted-foreground transition-colors hover:text-[hsl(var(--accent))] hover:bg-accent {% if compact %}px-2 py-1.5{% else %}px-3 py-2{% endif %} {{ item.css_class }}">{% if item.icon %}<svg class="mr-2 h-4 w-4 text-[hsl(var(--accent))]" aria-hidden="true"><use href="/icons/{{ item.icon|split:":"|first }}.svg#{{ item.icon|split:":"|last }}"></use></svg>{% endif %}{{ item.label }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
{% if highlightCurrent %}<script>
|
||||||
|
(function(){var p=window.location.pathname;document.querySelectorAll('[data-bn-sidebar-link]').forEach(function(a){if(a.getAttribute('href')===p){a.classList.remove('text-muted-foreground');a.classList.add('text-foreground','bg-accent','font-medium');}});})();
|
||||||
|
</script>{% endif %}
|
||||||
13
templates/overrides/magazine-bold/social-links.ninjatpl
Normal file
13
templates/overrides/magazine-bold/social-links.ninjatpl
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<div data-block-override="magazine-bold:social-links" class="social-links{% if class %} {{ class }}{% endif %}">
|
||||||
|
{% if heading %}<p class="mb-caps mb-4 text-xs tracking-[0.18em] text-muted-foreground">{{ heading }}</p>{% endif %}
|
||||||
|
<ul class="flex {% if style == "stack" %}flex-col gap-2{% else %}flex-wrap items-center gap-3{% endif %}">
|
||||||
|
{% for link in links %}{% if link.url %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ link.url }}" target="_blank" rel="noopener noreferrer" aria-label="{{ link.label|default:link.network }}" class="inline-flex items-center gap-2 rounded-none border border-[hsl(var(--border))] text-foreground transition-colors hover:border-[hsl(var(--accent))] hover:text-[hsl(var(--accent))] {% if style == "stack" %}w-full px-4 py-2.5{% else %}p-2.5{% endif %}">
|
||||||
|
{% if link.network %}<svg class="h-5 w-5 text-[hsl(var(--accent))]" aria-hidden="true"><use href="/icons/{{ link.network|split:":"|first }}.svg#{{ link.network|split:":"|last }}"></use></svg>{% endif %}
|
||||||
|
{% if style == "stack" and link.label %}<span class="mb-caps text-xs tracking-[0.14em]">{{ link.label }}</span>{% endif %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
48
templates/overrides/magazine-bold/stats.ninjatpl
Normal file
48
templates/overrides/magazine-bold/stats.ninjatpl
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<section data-block-override="magazine-bold:stats" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}"{% if countUp %} data-stats{% endif %}>
|
||||||
|
<div class="mx-auto max-w-6xl px-4">
|
||||||
|
{% if heading %}<h2 class="mb-caps text-center text-3xl text-foreground md:text-4xl" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ heading }}</h2>{% endif %}
|
||||||
|
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ intro }}</p>{% endif %}
|
||||||
|
<dl class="mt-12 grid grid-cols-2 gap-8 divide-x divide-[hsl(var(--border))] {% if columns == 2 %}md:grid-cols-2{% elif columns == 3 %}md:grid-cols-3{% else %}md:grid-cols-4{% endif %}">
|
||||||
|
{% for item in items %}
|
||||||
|
<div class="px-2 text-center">
|
||||||
|
<dd class="text-4xl font-bold tracking-tight text-[hsl(var(--accent))] md:text-5xl" style="font-family:var(--font-heading)">{% if item.prefix %}{{ item.prefix }}{% endif %}<span{% if countUp %} data-countup-value="{{ item.value }}"{% endif %}>{{ item.value }}</span>{% if item.suffix %}{{ item.suffix }}{% endif %}</dd>
|
||||||
|
{% if item.label %}<dt class="mb-caps mt-2 text-xs text-muted-foreground">{{ item.label }}</dt>{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% if countUp %}<script>
|
||||||
|
(function(){
|
||||||
|
var sections = document.querySelectorAll('[data-stats]');
|
||||||
|
var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
|
function animate(section){
|
||||||
|
var nodes = section.querySelectorAll('[data-countup-value]');
|
||||||
|
nodes.forEach(function(node){
|
||||||
|
var raw = node.getAttribute('data-countup-value') || '';
|
||||||
|
var target = parseFloat(raw.replace(/[^0-9.\-]/g, ''));
|
||||||
|
if (isNaN(target)) return;
|
||||||
|
var decimals = (raw.split('.')[1] || '').length;
|
||||||
|
var start = null, dur = 1200;
|
||||||
|
function step(ts){
|
||||||
|
if (start === null) start = ts;
|
||||||
|
var p = Math.min((ts - start) / dur, 1);
|
||||||
|
var eased = 1 - Math.pow(1 - p, 3);
|
||||||
|
node.textContent = (target * eased).toFixed(decimals);
|
||||||
|
if (p < 1) requestAnimationFrame(step);
|
||||||
|
else node.textContent = target.toFixed(decimals);
|
||||||
|
}
|
||||||
|
requestAnimationFrame(step);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
sections.forEach(function(section){
|
||||||
|
if (reduce || !('IntersectionObserver' in window)) return;
|
||||||
|
var io = new IntersectionObserver(function(entries){
|
||||||
|
entries.forEach(function(e){
|
||||||
|
if (e.isIntersecting){ animate(section); io.disconnect(); }
|
||||||
|
});
|
||||||
|
}, { threshold: 0.3 });
|
||||||
|
io.observe(section);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>{% endif %}
|
||||||
21
templates/overrides/magazine-bold/team.ninjatpl
Normal file
21
templates/overrides/magazine-bold/team.ninjatpl
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<section data-block-override="magazine-bold:team" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-6xl px-4">
|
||||||
|
{% if heading %}<h2 class="mb-caps text-center text-3xl text-foreground md:text-4xl" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ heading }}</h2>{% endif %}
|
||||||
|
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ intro }}</p>{% endif %}
|
||||||
|
<div class="mt-12 {% if layout == "list" %}mx-auto max-w-3xl space-y-6{% elif layout == "single" %}mx-auto max-w-xl{% else %}grid grid-cols-1 gap-8 sm:grid-cols-2 {% if columns == 2 %}lg:grid-cols-2{% elif columns == 4 %}lg:grid-cols-4{% else %}lg:grid-cols-3{% endif %}{% endif %}">
|
||||||
|
{% for member in members %}
|
||||||
|
<div class="{% if layout == "list" %}mb-panel flex items-start gap-5 bg-card p-5 text-card-foreground{% else %}text-center{% endif %}">
|
||||||
|
{% if member.photo %}<span class="{% if layout == "list" %}h-20 w-20{% else %}mx-auto h-28 w-28{% endif %} block shrink-0 overflow-hidden border border-[hsl(var(--border))] bg-muted">{% img member.photo alt=member.name class="h-full w-full object-cover" %}</span>{% endif %}
|
||||||
|
<div class="{% if layout != "list" %}mt-4{% endif %}">
|
||||||
|
{% if member.name %}<h3 class="mb-caps text-lg text-foreground" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ member.name }}</h3>{% endif %}
|
||||||
|
{% if member.role %}<p class="mb-caps text-xs text-muted-foreground">{{ member.role }}</p>{% endif %}
|
||||||
|
{% if member.bio %}<p class="mt-2 text-sm text-muted-foreground" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ member.bio }}</p>{% endif %}
|
||||||
|
{% if member.socials %}<div class="mt-3 flex gap-3 {% if layout != "list" %}justify-center{% endif %}">
|
||||||
|
{% for social in member.socials %}{% if social.url %}<a href="{{ social.url }}" class="text-muted-foreground transition-colors hover:text-[hsl(var(--accent))]" aria-label="Social link"><svg class="h-5 w-5" aria-hidden="true"><use href="/icons/{{ social.icon|split:":"|first }}.svg#{{ social.icon|split:":"|last }}"></use></svg></a>{% endif %}{% endfor %}
|
||||||
|
</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
27
templates/overrides/magazine-bold/testimonial.ninjatpl
Normal file
27
templates/overrides/magazine-bold/testimonial.ninjatpl
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<section data-block-override="magazine-bold:testimonial" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-6xl px-4">
|
||||||
|
{% if heading %}<h2 class="mb-caps text-center text-3xl text-foreground md:text-4xl" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ heading }}</h2>{% endif %}
|
||||||
|
<div class="mt-12 {% if layout == "single" %}mx-auto max-w-3xl{% elif layout == "carousel" %}flex snap-x snap-mandatory gap-6 overflow-x-auto pb-4{% else %}grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3{% endif %}">
|
||||||
|
{% for q in quotes %}
|
||||||
|
<figure class="mb-panel flex flex-col bg-card p-6 text-card-foreground{% if layout == "carousel" %} min-w-[18rem] max-w-sm shrink-0 snap-start{% endif %}">
|
||||||
|
{% if q.rating %}<div class="mb-3 flex justify-center gap-0.5" aria-label="Rated {{ q.rating }} out of 5">
|
||||||
|
<svg class="h-4 w-4 {% if q.rating >= 1 %}text-[hsl(var(--accent))]{% else %}text-muted-foreground/30{% endif %}" aria-hidden="true"><use href="/icons/lucide.svg#star"></use></svg>
|
||||||
|
<svg class="h-4 w-4 {% if q.rating >= 2 %}text-[hsl(var(--accent))]{% else %}text-muted-foreground/30{% endif %}" aria-hidden="true"><use href="/icons/lucide.svg#star"></use></svg>
|
||||||
|
<svg class="h-4 w-4 {% if q.rating >= 3 %}text-[hsl(var(--accent))]{% else %}text-muted-foreground/30{% endif %}" aria-hidden="true"><use href="/icons/lucide.svg#star"></use></svg>
|
||||||
|
<svg class="h-4 w-4 {% if q.rating >= 4 %}text-[hsl(var(--accent))]{% else %}text-muted-foreground/30{% endif %}" aria-hidden="true"><use href="/icons/lucide.svg#star"></use></svg>
|
||||||
|
<svg class="h-4 w-4 {% if q.rating >= 5 %}text-[hsl(var(--accent))]{% else %}text-muted-foreground/30{% endif %}" aria-hidden="true"><use href="/icons/lucide.svg#star"></use></svg>
|
||||||
|
</div>{% endif %}
|
||||||
|
<blockquote class="flex-1 text-center text-lg leading-relaxed" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{% if q.quote %}“{{ q.quote }}”{% endif %}</blockquote>
|
||||||
|
<div class="mx-auto my-4 flex items-center justify-center"><span class="mb-rule w-16"></span></div>
|
||||||
|
<figcaption class="flex items-center justify-center gap-3">
|
||||||
|
{% if q.avatar %}<span class="h-10 w-10 shrink-0 overflow-hidden border border-[hsl(var(--border))] bg-muted">{% img q.avatar alt=q.name class="h-full w-full object-cover" %}</span>{% endif %}
|
||||||
|
<span class="text-center">
|
||||||
|
{% if q.name %}<span class="mb-caps block text-sm text-foreground">{{ q.name }}</span>{% endif %}
|
||||||
|
{% if q.role %}<span class="block text-sm text-muted-foreground">{{ q.role }}</span>{% endif %}
|
||||||
|
</span>
|
||||||
|
</figcaption>
|
||||||
|
</figure>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
@ -1 +1,2 @@
|
|||||||
<div class="mb-body font-sans {{ class }}">{{ text|safe }}</div>
|
{# text override — Inter reading measure with fluoro underlined links. Fields: text, proseClass (mirrors builtin). Dropcap is reserved for the article template, not forced on every text block. #}
|
||||||
|
<div data-block-override="magazine-bold:text" class="prose max-w-none flex-1 [&_a]:text-[hsl(var(--accent))] [&_a]:no-underline [&_a]:border-b [&_a]:border-[hsl(var(--border))] [&_a:hover]:text-accent {{ proseClass }}" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif);color:hsl(var(--foreground));font-size:1.075rem;line-height:1.75;">{{ text|safe }}</div>
|
||||||
|
|||||||
21
templates/overrides/magazine-bold/timeline.ninjatpl
Normal file
21
templates/overrides/magazine-bold/timeline.ninjatpl
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<section data-block-override="magazine-bold:timeline" class="py-16 md:py-24 bg-background text-foreground{% if class %} {{ class }}{% endif %}">
|
||||||
|
<div class="mx-auto max-w-4xl px-4">
|
||||||
|
{% if heading %}<h2 class="mb-caps text-center text-3xl text-foreground md:text-4xl" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ heading }}</h2>{% endif %}
|
||||||
|
{% if intro %}<p class="mx-auto mt-4 max-w-2xl text-center text-lg text-muted-foreground" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ intro }}</p>{% endif %}
|
||||||
|
<ol class="mt-12 {% if orientation == "horizontal" %}grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4{% else %}relative space-y-8 before:absolute before:left-4 before:top-2 before:h-[calc(100%-1rem)] before:w-px before:bg-[hsl(var(--border))]{% endif %}">
|
||||||
|
{% for step in steps %}
|
||||||
|
<li class="{% if orientation == "horizontal" %}text-center{% else %}relative pl-14{% endif %}">
|
||||||
|
<span class="mb-caps flex h-8 w-8 items-center justify-center border border-[hsl(var(--accent))] bg-card text-sm text-[hsl(var(--accent))] {% if orientation == "horizontal" %}mx-auto{% else %}absolute left-0 top-0 ring-4 ring-background{% endif %}">
|
||||||
|
{% if marker == "numbered" %}{{ forloop.counter }}{% elif marker == "dot" %}<span class="h-2 w-2 bg-[hsl(var(--accent))]"></span>{% else %}<svg class="h-4 w-4" aria-hidden="true"><use href="/icons/lucide.svg#flag"></use></svg>{% endif %}
|
||||||
|
</span>
|
||||||
|
<div class="{% if orientation == "horizontal" %}mt-4{% else %}pt-1{% endif %}">
|
||||||
|
{% if step.label %}<p class="mb-caps text-xs text-muted-foreground">{{ step.label }}</p>{% endif %}
|
||||||
|
{% if step.title %}<h3 class="mb-caps mt-1 text-lg text-foreground" style="font-family:var(--font-heading, 'Archivo Black', 'Arial Black', sans-serif)">{{ step.title }}</h3>{% endif %}
|
||||||
|
{% if step.text %}<p class="mt-1 text-muted-foreground" style="font-family:var(--font-body, 'Inter', system-ui, sans-serif)">{{ step.text }}</p>{% endif %}
|
||||||
|
{% if step.media %}<div class="mt-3 overflow-hidden">{% img step.media alt=step.title class="w-full h-auto object-cover" %}</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
10
templates/overrides/magazine-bold/utility-bar.ninjatpl
Normal file
10
templates/overrides/magazine-bold/utility-bar.ninjatpl
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{# utility-bar override — quiet contact strip on a hairline-bordered panel, small-caps labels. Fields mirror builtin: variant, showOnMobile, leftItems/rightItems {icon,label,link}. #}
|
||||||
|
{% set ubcls = "mb-panel" %}
|
||||||
|
{% if variant == "primary" %}{% set ubcls = "mb-panel" %}{% elif variant == "dark" %}{% set ubcls = "mb-panel" %}{% endif %}
|
||||||
|
{% macro ubitem(it) %}{% if it.link %}<a href="{{ it.link }}" class="mb-caps inline-flex items-center gap-1.5 hover:text-[hsl(var(--accent))] transition-colors">{% if it.icon %}::{{ it.icon }}::{% endif %}{% if it.label %}<span>{{ it.label }}</span>{% endif %}</a>{% else %}<span class="mb-caps inline-flex items-center gap-1.5">{% if it.icon %}::{{ it.icon }}::{% endif %}{% if it.label %}<span>{{ it.label }}</span>{% endif %}</span>{% endif %}{% endmacro %}
|
||||||
|
<div data-block-override="magazine-bold:utility-bar" class="w-full text-[0.7rem] {% if not showOnMobile %}hidden sm:block{% endif %} {{ ubcls }} {{ class }}">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-9 flex items-center justify-between gap-4">
|
||||||
|
<div class="flex items-center gap-5 flex-wrap">{% for it in leftItems %}{{ ubitem(it) }}{% endfor %}</div>
|
||||||
|
<div class="flex items-center gap-5 flex-wrap justify-end">{% for it in rightItems %}{{ ubitem(it) }}{% endfor %}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
23
templates/overrides/magazine-bold/video-embed.ninjatpl
Normal file
23
templates/overrides/magazine-bold/video-embed.ninjatpl
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{# video-embed override — magazine-bold hairline frame; fluoro play medallion. Fields + facade JS mirror builtin verbatim: aspect, url, media, poster, title, caption. #}
|
||||||
|
{% with aspect=aspect|default:"16:9" %}<figure data-block-override="magazine-bold:video-embed" class="bn-video my-8{% if class %} {{ class }}{% endif %}"><div class="bn-video-frame relative w-full overflow-hidden bg-muted {% if aspect == "4:3" %}aspect-[4/3]{% elif aspect == "1:1" %}aspect-square{% elif aspect == "21:9" %}aspect-[21/9]{% else %}aspect-video{% endif %}" data-bn-video data-video-url="{{ url|default:"" }}" data-video-media="{% if media %}{{ media|mediaURL }}{% endif %}"><button type="button" data-bn-video-play class="group absolute inset-0 flex h-full w-full items-center justify-center focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring" aria-label="Play video{% if title %}: {{ title }}{% endif %}">{% if poster %}{% img poster alt=title|default:"" class="absolute inset-0 h-full w-full object-cover" %}{% endif %}<span class="absolute inset-0 bg-foreground/10 transition-colors group-hover:bg-foreground/20" aria-hidden="true"></span><span class="relative inline-flex h-16 w-16 items-center justify-center rounded-full bg-background/90 text-[hsl(var(--accent))] transition-transform group-hover:scale-105 motion-reduce:transform-none">::lucide:play:xl::</span></button></div>{% if caption %}<figcaption class="mt-3 text-center text-sm italic" style="color:hsl(var(--muted-foreground));font-family:var(--font-body, 'Inter', system-ui, sans-serif);">{{ caption }}</figcaption>{% endif %}</figure><script>
|
||||||
|
(function(){
|
||||||
|
if(window.__bnVideoFacade)return;window.__bnVideoFacade=true;
|
||||||
|
function yt(u){var m=u.match(/(?:youtube\.com\/(?:watch\?v=|embed\/|shorts\/)|youtu\.be\/)([\w-]{6,})/);return m?m[1]:null;}
|
||||||
|
function vim(u){var m=u.match(/vimeo\.com\/(?:video\/)?(\d+)/);return m?m[1]:null;}
|
||||||
|
document.addEventListener('click',function(e){
|
||||||
|
var btn=e.target.closest('[data-bn-video-play]');if(!btn)return;
|
||||||
|
var frame=btn.closest('[data-bn-video]');if(!frame)return;
|
||||||
|
e.preventDefault();
|
||||||
|
var url=frame.getAttribute('data-video-url')||'',media=frame.getAttribute('data-video-media')||'',node;
|
||||||
|
var vstyle='position:absolute;inset:0;width:100%;height:100%;object-fit:contain;background:rgba(0,0,0,1);';
|
||||||
|
if(media){node=document.createElement('video');node.src=media;node.controls=true;node.autoplay=true;node.setAttribute('playsinline','');node.style.cssText=vstyle;}
|
||||||
|
else{var y=yt(url),v=vim(url),embed;
|
||||||
|
if(y)embed='https://www.youtube-nocookie.com/embed/'+y+'?autoplay=1&rel=0';
|
||||||
|
else if(v)embed='https://player.vimeo.com/video/'+v+'?autoplay=1';
|
||||||
|
if(embed){node=document.createElement('iframe');node.src=embed;node.allow='autoplay; fullscreen; picture-in-picture';node.setAttribute('allowfullscreen','');node.setAttribute('title',btn.getAttribute('aria-label')||'Video');node.style.cssText='position:absolute;inset:0;width:100%;height:100%;border:0;';}
|
||||||
|
else if(url){node=document.createElement('video');node.src=url;node.controls=true;node.autoplay=true;node.setAttribute('playsinline','');node.style.cssText=vstyle;}
|
||||||
|
}
|
||||||
|
if(node){btn.remove();frame.appendChild(node);}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>{% endwith %}
|
||||||
Loading…
x
Reference in New Issue
Block a user