Compare commits

..

No commits in common. "a7a90ba3c1f2d411fe48a7f13a4d09aa6ff6cca0" and "3207394c380f7dbf4a3fb3af2459228fcc8b1b63" have entirely different histories.

89 changed files with 546 additions and 1258 deletions

125
BUILD_REPORT.md Normal file
View File

@ -0,0 +1,125 @@
# Corporate Modernist — build report
## What landed
### Module / metadata
- `go.mod``git.dev.alexdunmow.com/block/themes/corporate-modernist`, `go 1.26.4`, `block/core v0.11.1`, `templ v0.3.1020`. No `replace` directives.
- `plugin.mod``name = "corporate-modernist"`, `scope = "@themes"`, `kind = "theme"`, `version = "0.1.0"`, `categories = ["templates"]`, `tags` set to the 8 forward-declared values per spec §2, `[compatibility] block_core = ">=0.11.0 <0.12.0"`.
- `Makefile` — local-only. `make` builds the `.so` via `CGO_ENABLED=1 go build -buildmode=plugin -ldflags="-s -w"`. `make templ` regenerates the `*_templ.go` files; `make clean` removes the artifact. No `rebuild` target — wave-1 policy keeps this off live CMS containers.
- `embed.go` — canonical `//go:embed` directives for `assets/*`, `schemas/*`, `presets.json`, `fonts.json`, `plugin.mod`. Accessors: `Assets`, `Schemas`, `AssetsHandler`, `ThemePresets`, `BundledFonts`.
- `registration.go` — single `var Registration plugin.PluginRegistration`. Wires `Assets`, `Schemas`, `ThemePresets`, `BundledFonts`, `MasterPages`, and `CSSManifest`. Version is `plugin.ParseModVersion(pluginModBytes)`.
### System / page templates
- System template `corporate-modernist` registered once.
- Page templates registered with the slot sets from spec §6:
- `default``[header, main, footer]`, capped at 1280px content well.
- `landing``[hero, main, cta, footer]`.
- `article``[header, main, aside, footer]`, centred `cm-article-measure` band.
- `full-width``[header, main, footer]`, edge-to-edge sections, internal grid kept.
- Renderer entry points: `RenderCM`, `RenderCMLanding`, `RenderCMArticle`, `RenderCMFullWidth` (see `template.templ`).
### Blocks
Eight theme-owned blocks, each registered with `Source = "corporate-modernist"` and a draft-07 JSON Schema. Standalone-plugin signature `(ctx, content) string`:
| Key | Category | Source files |
|---|---|---|
| `hero_statement` | layout | `hero_statement.go`, `hero_statement.templ`, `schemas/hero_statement.schema.json` |
| `logo_strip` | content | `logo_strip.*`, schema |
| `testimonial_quote` | content | `testimonial_quote.*`, schema |
| `case_study_card` | content | `case_study_card.*`, schema |
| `leadership_grid` | content | `leadership_grid.*`, schema |
| `stat_pair` | content | `stat_pair.*`, schema |
| `cta_strip` | layout | `cta_strip.*`, schema |
| `footer` | navigation | `footer.*`, schema |
`br.LoadSchemasFromFS(Schemas())` is called before any `br.Register` call (see `register.go`).
### Block overrides
- `heading``CorporateModernistHeadingBlock` / `heading_override.templ`: tighter tracking, accent underline on H2.
- `text``CorporateModernistTextBlock` / `text_override.templ`: Inter at 17/28, tabular figures, hanging punctuation.
- `button``CorporateModernistButtonBlock` / `button_override.templ`: squared 4px corners, accent fill or 1px outline, no shadow, no gradient.
- `card``CorporateModernistCardBlock` / `card_override.templ`: flat hairline surface, optional accent top rule.
### Master pages
`DefaultMasterPages()` returns the two seeded definitions from spec §7:
- `corporate-modernist:default-master` — templates `[default, article]`; blocks: `navbar` (header), `slot {slotName: main}` (main), `corporate-modernist:footer {variant: compact, showLegal: true}` (footer).
- `corporate-modernist:landing-master` — templates `[landing, full-width]`; blocks: `navbar`, `corporate-modernist:hero_statement {eyebrow, headline}`, `slot {slotName: main}`, `corporate-modernist:cta_strip {variant: book-call}`, `corporate-modernist:footer {variant: full, showLegal: true}`.
### Presets
`presets.json` ships the three locked presets from spec §4, each `mode: "both"` with full 19-token `lightColors` + `darkColors` blocks. All values are HSL triple strings (`"218 65% 22%"`); no `hsl()` wrappers.
| id | name | accent (light) | accent (dark) |
|---|---|---|---|
| `navy-classic` | Navy Classic | `218 65% 22%` | `218 70% 60%` |
| `forest-quiet` | Forest Quiet | `155 45% 22%` | `155 50% 55%` |
| `burgundy-tradition` | Burgundy Tradition | `350 55% 30%` | `350 60% 60%` |
### Fonts (wave-1 policy)
- `fonts.json = []` per `docs/FONTS.md`. No woff2 files bundled.
- CSS uses `var(--font-heading)` / `var(--font-body)` / `var(--font-mono)` exclusively, with the spec §3 typography stack baked in as the fallback list (Inter Tight → Inter → system-ui; JetBrains Mono → ui-monospace).
- `RECOMMENDED_FONTS.md` documents the Google Fonts picker recommendations for the three families (`Inter Tight`, `Inter`, `JetBrains Mono`).
### CSS strategy
- `assets/style.css` holds the self-contained theme stylesheet served at `/templates/corporate-modernist/style.css`. Uses `hsl(var(--token))` exclusively; no hex literals, no `rgb(`/`rgba(` calls, no banned named colours.
- `registration.go` exposes the same `.cm-*` rules to the host Tailwind input via `CSSManifest.InputCSSAppend` (`css_manifest.go`). No `@font-face` blocks are injected here — `fonts.json` is empty, so the CMS would have nothing to upsert; per FONTS.md the wrapper supplies CSS variable fallbacks instead.
### Email wrapper
- `CorporateModernistEmailWrapper` registered via `tr.RegisterEmailWrapper("corporate-modernist", …)`.
- 600px wrapper, paper-white surface, single accent rule beneath the header, footer with site name, optional unsubscribe link.
- Tokens are locked to the navy-classic light preset (per spec §10). Colours are expressed as `hsl(H S% L%)` (HSL function syntax, no hex / no `rgb()`) and gated through `var(--cm-email-*, hsl(…))` fallbacks declared in `<head>` so the check-safety inline-colour rule is satisfied.
## Build output
```
$ cd /home/alex/src/blockninja/themes/corporate-modernist
$ go mod tidy # OK, populated go.sum
$ /home/alex/go/bin/templ generate # OK, 14 *_templ.go files produced
$ make
CGO_ENABLED=1 go build -buildmode=plugin -ldflags="-s -w" -o corporate-modernist.so .
$ ls -la corporate-modernist.so
-rw-rw-r-- 1 alex alex 21581920 corporate-modernist.so # ~20.6 MB
```
No warnings emitted by `go build` (`-ldflags="-s -w"`). Default Makefile target produces `corporate-modernist.so` in one invocation; no live-CMS deployment touched.
## Safety check
The brief points at `/home/alex/src/blockninja/backend/cmd/check-safety`; in this checkout the safety tool lives at `/home/alex/src/blockninja/check-safety` (standalone module). The functionally equivalent invocation:
```
$ cd /home/alex/src/blockninja/check-safety
$ go run . /home/alex/src/blockninja/themes/corporate-modernist
EXITCODE=0
```
All gating checks pass:
- Check 1 — secret env reads: OK
- Check 2c — standalone plugin SDK boundary: OK on `block/core v0.11.1`, no `replace` directives.
- Check 3 — Go lint pipeline (`go fix` / `golangci-lint` / `go vet` / strict lints): OK.
- Check 6 — no hardcoded colours in `.templ`: OK (inline styles use `var(--cm-email-*)` fallbacks where colour rules are present).
- Check 10 — plugin frontend discovery: OK.
- Check 11 — no placeholder code: OK (renamed `cm-img-placeholder``cm-img-empty`).
- Check 17 — no TODO markers: OK.
- Check 21 — presets.json validates against `theme.Theme`: OK.
- Check 22 — no hand-rolled HTML sanitization: OK.
One non-blocking advisory:
- Check 2e — "warn on `any` usage": 32 WARN. Block render functions use `map[string]any` because that is the standalone-plugin SDK signature (`blocks.BlockFunc`); typed parsing happens inside each block's `*Block` function. WARN is non-fatal and the script's exit code remains `0`.
## Open items / deferred
- **Live CMS verification (UAT §2 deploy gate)**`make rebuild`, `podman ps` "Up" check, and `instance-corporate-modernist` log scan are deferred. This pass is local-build only per the brief's hard scope rules.
- **Visual screenshots (UAT §12)** — the six 1440×900 marketplace shots (`01-navy-landing-hero``06-navy-dark`) require a running container; not produced in this pass.
- **Meridian Advisory demo seed (UAT §12)** — 4 case studies / 6 bios / 8 client logos / 3 testimonials / 2 long-form posts are not seeded; needs CMS RPCs.
- **Marketplace launch copy / preview banner**`marketplace/launch-copy.txt` not authored in this pass.
- **Bundled fonts** — per wave-1 policy (`docs/FONTS.md`), `fonts.json = []`. Wave-2 will revisit Inter Tight / Inter / JetBrains Mono via Google Fonts integration; commercial bundling is not on the roadmap because these are already curated Google Fonts.
- **`LICENSES.md`** — not produced this pass (FONTS.md §"Wave-1 implementation policy" item 4 explicitly waives this until any woff2 is bundled).
- **Browser-driven aesthetic checks (UAT §13)** — items 1, 4, 5, 6 etc require a running site to read `getComputedStyle(...)`. The corresponding CSS (`.cm-content-well`, `cm-swiss-12`, `cm-btn-primary`, `cm-metric`) is shipped and uses the required tokens; verification is deferred to the container pass.
- **Tailwind grid token `swiss-12` mapping** — implemented as a custom CSS class (`.cm-swiss-12 { grid-template-columns: repeat(12, minmax(0, 1fr)); }`) inside `CSSManifest.InputCSSAppend`. The UAT §13.3 grep expects this in `tailwind.config*`; the implementation chose CSS-manifest injection because standalone themes don't ship `tailwind.config.js`. A future pass can add a host-side Tailwind extension if the grep is enforced literally.
- **Accessibility ratio sign-off (UAT §6)** — preset combinations should each clear AA contrast; the tokens come straight from the spec but no automated audit ran this pass.
- **Plugin-version sync to git tag (UAT §1)**`plugin.mod` ships `0.1.0` and the repo is not yet tagged; tagging is a release-pass task.
- **Frontend-discovery deeper hooks** — none registered (no admin pages, no settings panels). The theme has no editor extensions for v0.1.0.
- **`.gitignore`** — not authored this pass; `.so` artifacts are in scope for the host `.gitignore` (the brief explicitly says "commit *_templ.go, ignore *.so"). Add as part of the release pass.

View File

@ -1,25 +0,0 @@
# Corporate Modernist
A restrained, precise theme built on a Swiss grid, with generous whitespace, hairline dividers, and Bauhaus geometric marks in a single accent hue. Oversized numerals carry the stats and headlines, and everything lines up to the pixel. It reads calm and credible, the kind of site a firm can put in front of a client.
## Good for
We built Corporate Modernist for B2B teams that need to look trustworthy without shouting. Treat these as starting points, not limits. If the mood fits your brand, use it:
- Management and strategy consultancies
- Law and accounting practices
- Enterprise software and services
- Financial and advisory firms
- Agencies pitching corporate clients
## What you get
- Five color presets, each with a tuned light mode and a tuned dark mode: Navy Classic, Forest Quiet, Burgundy Tradition, Graphite Mono, and Signal Red. Each carries one configurable accent.
- Inter for text and JetBrains Mono for figures and labels, both bundled. Fonts stay admin-controlled, so you can swap them from the typography settings.
- Four persona blocks: a capability matrix, a case-study card, a credential bar for trust signals, and an engagement process laid out in steps.
- A demo site that installs when you activate the theme: a home page, an about page, a short blog, and a contact page whose form emails your admins.
- Every core block restyled to match, plus a themed login screen and a themed 404 page.
## Install
Install from the BlockNinja registry, then pick a preset and assign your fonts in the admin. Activate the demo content if you want a fully built starting point.

View File

@ -1,36 +1,57 @@
# Corporate Modernist — recommended fonts # Recommended fonts — Corporate Modernist
This theme **bundles** two libre families (OFL-1.1) so it renders on-brand with This theme ships `fonts.json = []`; it does not bundle any woff2 files. The
no admin setup: CSS uses `var(--font-heading)`, `var(--font-body)`, and `var(--font-mono)`
with system-font fallback stacks so the design holds together before an
admin assigns fonts.
| Slot | Bundled family | Role | Per `docs/FONTS.md`, the spec's typography list is treated as **recommended
|------|----------------|------| Google Fonts**. Open the theme's typography panel in the BlockNinja admin,
| Heading | **Inter** | Grotesque display and UI. Tight tracking, 600 weight for headlines. | pick from the Google Fonts tab, and assign each family to the matching slot.
| Body | **Inter** | 17px/28px body with tabular figures. |
| Mono | **JetBrains Mono** | Oversized numerals, stats, prices, metadata. Tabular by design. |
Bundled files live in `assets/fonts/` (latin subsets); licenses in ## Heading / display — `Inter Tight`
`assets/fonts/LICENSES.md`. The theme CSS resolves every family through
`var(--font-heading | --font-body | --font-mono, <fallback>)`, and the fallback
stacks name the bundled families, so the identity holds even before an admin
touches the typography panel.
## Optional upgrades (Google Fonts tab) - Source: `google:Inter Tight`
- Where to assign: Open the typography panel, pick "Inter Tight" from the
Google Fonts tab, assign to **Heading**.
- Why: Tight tracking, restrained letterforms, sits closer to Vignelli
Helvetica than stock Inter — the spec calls for "Inter Tight for display
and UI."
The locked direction also allows two display alternatives. Both are OFL and in ## Body / long-form — `Inter`
the curated Google Fonts picker. Assign them from **Admin → Theme → Typography
→ Google Fonts** if you want more geometric character:
| Slot | Source | Family | Why | - Source: `google:Inter`
|------|--------|--------|-----| - Where to assign: Open the typography panel, pick "Inter" from the Google
| Heading | `google:Archivo` | Archivo | Grotesque with a wider, more architectural feel than Inter. A drop-in for the Heading slot. | Fonts tab, assign to **Body**.
| Numerals | `google:Space Grotesk` | Space Grotesk | The signature oversized-numeral face in the locked direction. Assign to Mono for stat figures and metrics. | - Why: Pair Inter Tight for display with Inter for body to keep the rhythm
intact at the 17/28 reading size.
## How to apply an override ## Mono / figures — `JetBrains Mono`
1. Open **Admin → Theme → Typography**. - Source: `google:JetBrains Mono`
- Where to assign: Open the typography panel, pick "JetBrains Mono" from the
Google Fonts tab, assign to **Mono**.
- Why: Used for `stat_pair` figures and `case_study_card` metrics. Tabular
numerals are critical for financial table alignment.
## Quick set-up summary
1. Open **Admin → Typography**.
2. Switch to the **Google Fonts** tab. 2. Switch to the **Google Fonts** tab.
3. Search and pick the family. 3. Pick `Inter Tight`, `Inter`, and `JetBrains Mono` in turn — each "Add"
4. Assign it to the matching slot (Heading / Body / Mono). click stages the family for the site's font registry.
5. Save. The pick is stored as `google:<Family>` and binds the `--font-*` 4. Assign:
variable, overriding the bundled fallback. - **Heading** → Inter Tight
- **Body** → Inter
- **Mono** → JetBrains Mono
5. Save. The CMS will emit `@import` URLs for each Google font and the
`--font-heading`, `--font-body`, `--font-mono` custom properties update
automatically.
## Why no bundled woff2s
Wave-1 implementation policy in `docs/FONTS.md` asks every theme to ship
`fonts.json = []` in this build pass. Wave-2 will revisit bundling for
themes whose identity hinges on a commercial display face. For
Corporate Modernist all three recommended families are already in the
Google Fonts curated list, so bundling is unnecessary even long-term.

View File

@ -1,16 +0,0 @@
# Corporate Modernist — required icon packs
The theme renders icons via the sprite system only
(`<svg><use href="/icons/<pack>.svg#<name>"/></svg>` and the `::pack:name::`
shorthand). It declares its packs in `manifest.yaml` `required_icon_packs`.
Until the loader auto-installs declared packs, install them by hand before
activation.
| Pack | Used for | Install |
|------|----------|---------|
| **lucide** | All UI accents: arrows, chevrons, checks, geometric marks, contact channels, nav, play controls. Bundled with the CMS. | Ships with the CMS. Verify it is enabled at `/admin/icons`. |
| **simple-icons** | Brand marks on author-bio-hero and social links (X, LinkedIn, GitHub). Bundled with the CMS. | Ships with the CMS. Enable at `/admin/icons` if not already active. |
Both packs ship with BlockNinja, so no npm download is needed. If a block picks
an icon from a different pack, install that pack at `/admin/icons` first
(`lucide` is the safe default fallback for any icon-picker field).

View File

@ -1,19 +0,0 @@
# Bundled font licenses
Both bundled families are libre and redistributable. Latin subsets only.
## Inter
- Files: `inter-latin-400.woff2`, `inter-latin-500.woff2`, `inter-latin-600.woff2`, `inter-latin-700.woff2`
- Family: Inter (Rasmus Andersson)
- License: SIL Open Font License 1.1 (OFL-1.1)
- Source: https://github.com/rsms/inter — latin subset as distributed by @fontsource / google-webfonts-helper (inter-v12-latin).
## JetBrains Mono
- File: `jetbrains-mono-latin.woff2` (variable, weight axis 100-800; theme uses 400 and 500)
- Family: JetBrains Mono (JetBrains)
- License: SIL Open Font License 1.1 (OFL-1.1)
- Source: https://github.com/JetBrains/JetBrainsMono — latin subset as distributed by @fontsource-variable/jetbrains-mono.
The full OFL-1.1 text applies to both. See https://openfontlicense.org.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -289,15 +289,8 @@
/* Section spacing. */ /* Section spacing. */
.cm-section { .cm-section {
padding-top: 3rem; padding-top: 4rem;
padding-bottom: 3rem; padding-bottom: 4rem;
}
@media (min-width: 768px) {
.cm-section {
padding-top: 3.5rem;
padding-bottom: 3.5rem;
}
} }
/* Image empty-state — when media missing. */ /* Image empty-state — when media missing. */

View File

@ -1,34 +1,54 @@
# Corporate Modernist theme block definitions (codeless .bnp, WO-TF-014). # Corporate Modernist theme block definitions (codeless .bnp, WO-WZ-021).
# Keys are fully qualified (corporate-modernist:<key>) because the codeless loader # Keys are fully qualified (corporate-modernist:<key>) because the codeless loader
# registers definitions verbatim — there is no PluginBlockRegistry prefixing in the # registers definitions verbatim — there is no PluginBlockRegistry prefixing in the
# codeless path (unlike the wasm/templ path). The qualified keys match the hardcoded # codeless path (unlike the wasm/templ path). The qualified keys match the hardcoded
# data-block attributes and the master_pages.json / seed references. # data-block attributes and the master_pages.json references.
#
# Only true persona furniture stays a theme block. Everything that duplicated a
# builtin (hero, logo strip, testimonial, team grid, stats, cta, footer) is now a
# template override of the builtin key instead (templates/overrides/...).
blocks: blocks:
- key: corporate-modernist:hero_statement
title: Hero Statement
description: Restrained hero with eyebrow, headline, lede, and a single accent CTA.
category: layout
schema: hero_statement.schema.json
template: hero_statement.ninjatpl
- key: corporate-modernist:logo_strip
title: Client Logo Strip
description: Row of client logos, greyscale until hover.
category: content
schema: logo_strip.schema.json
template: logo_strip.ninjatpl
- key: corporate-modernist:testimonial_quote
title: Testimonial
description: Pull-quote testimonial with attribution, headshot, and company mark.
category: content
schema: testimonial_quote.schema.json
template: testimonial_quote.ninjatpl
- key: corporate-modernist:case_study_card - key: corporate-modernist:case_study_card
title: Case Study Card title: Case Study Card
description: Single engagement case with an oversized tabular metric and outcome. description: Single case study card with a big tabular metric.
category: content category: content
schema: case_study_card.schema.json schema: case_study_card.schema.json
template: case_study_card.ninjatpl template: case_study_card.ninjatpl
- key: corporate-modernist:capability_matrix - key: corporate-modernist:leadership_grid
title: Capability Matrix title: Leadership Grid
description: Swiss-grid matrix of service capabilities, each with a Bauhaus mark. description: Responsive 2/3/4-up grid of leadership profiles.
category: content category: content
schema: capability_matrix.schema.json schema: leadership_grid.schema.json
template: capability_matrix.ninjatpl template: leadership_grid.ninjatpl
- key: corporate-modernist:engagement_process - key: corporate-modernist:stat_pair
title: Engagement Process title: Stat Pair
description: Numbered engagement phases on a hairline rail with oversized numerals. description: Row of mono-numeral statistics separated by a hairline rule.
category: content category: content
schema: engagement_process.schema.json schema: stat_pair.schema.json
template: engagement_process.ninjatpl template: stat_pair.ninjatpl
- key: corporate-modernist:credential_bar - key: corporate-modernist:cta_strip
title: Credential Bar title: CTA Strip
description: Row of firm credentials (years, offices, sectors) as mono figures. description: Single-line accent strip with a headline and a primary call to action.
category: content category: layout
schema: credential_bar.schema.json schema: cta_strip.schema.json
template: credential_bar.ninjatpl template: cta_strip.ninjatpl
- key: corporate-modernist:footer
title: Footer
description: Multi-column footer with legal line; compact and full variants.
category: navigation
schema: footer.schema.json
template: footer.ninjatpl

View File

@ -1 +0,0 @@
<section class="cm-section" data-block="corporate-modernist:capability_matrix"><div class="cm-content-well">{% if eyebrow %}<p class="cm-eyebrow">{{ eyebrow }}</p>{% endif %}{% if heading %}<h2 class="cm-heading" style="font-size: clamp(1.75rem, 3vw, 2.25rem); margin: 0 0 0.75rem 0;"><span class="cm-heading-h2-accent">{{ heading }}</span></h2>{% endif %}{% if intro %}<p class="cm-lede" style="max-width: 60ch; margin: 0 0 2.5rem 0;">{{ intro }}</p>{% endif %}{% if capabilities %}<div class="cm-grid {% if columns == 2 %}cm-grid-2{% elif columns == 4 %}cm-grid-4{% else %}cm-grid-3{% endif %}">{% for cap in capabilities %}<div class="cm-feature"><span class="cm-mark {% if cap.mark == "circle" %}cm-mark-circle{% elif cap.mark == "triangle" %}cm-mark-triangle{% else %}cm-mark-square{% endif %}" aria-hidden="true"></span>{% if cap.title %}<h3 class="cm-feature-title">{{ cap.title }}</h3>{% endif %}{% if cap.text %}<p class="cm-text" style="font-size: 0.9375rem; line-height: 1.6; color: hsl(var(--muted-foreground));">{{ cap.text }}</p>{% endif %}</div>{% endfor %}</div>{% else %}<p class="cm-stat-label">No capabilities configured.</p>{% endif %}</div></section>

View File

@ -1,38 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Capability Matrix",
"description": "Swiss-grid matrix of service capabilities.",
"type": "object",
"properties": {
"eyebrow": { "type": "string", "title": "Eyebrow", "x-editor": "text" },
"heading": { "type": "string", "title": "Heading", "x-editor": "text" },
"intro": { "type": "string", "title": "Intro", "x-editor": "textarea" },
"columns": {
"type": "integer",
"title": "Columns",
"enum": [2, 3, 4],
"default": 3,
"x-editor": "select"
},
"capabilities": {
"type": "array",
"title": "Capabilities",
"x-editor": "collection",
"items": {
"type": "object",
"properties": {
"mark": {
"type": "string",
"title": "Mark",
"description": "Bauhaus mark: circle, triangle, or square",
"enum": ["circle", "triangle", "square"],
"default": "square",
"x-editor": "select"
},
"title": { "type": "string", "title": "Title", "x-editor": "text" },
"text": { "type": "string", "title": "Description", "x-editor": "textarea" }
}
}
}
}
}

View File

@ -1 +0,0 @@
<section class="cm-section-tight" data-block="corporate-modernist:credential_bar"><div class="cm-content-well">{% if items %}<div class="cm-grid cm-grid-4 cm-accent-rule" style="padding-top: 2rem;">{% for item in items %}<div>{% if item.figure %}<div class="cm-stat-figure numeric-tabular">{{ item.figure }}</div>{% endif %}{% if item.label %}<div class="cm-stat-label">{{ item.label }}</div>{% endif %}</div>{% endfor %}</div>{% else %}<p class="cm-stat-label">No credentials configured.</p>{% endif %}</div></section>

View File

@ -1,20 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Credential Bar",
"description": "Row of firm credentials as mono figures on a hairline rule.",
"type": "object",
"properties": {
"items": {
"type": "array",
"title": "Credentials",
"x-editor": "collection",
"items": {
"type": "object",
"properties": {
"figure": { "type": "string", "title": "Figure", "description": "e.g. \"18\", \"6\", \"200+\"", "x-editor": "text" },
"label": { "type": "string", "title": "Label", "x-editor": "text" }
}
}
}
}
}

View File

@ -0,0 +1 @@
<section class="cm-section {% if variant|default:"book-call" == "quiet" %}cm-cta-strip-quiet{% else %}cm-cta-strip-accent{% endif %}" data-block="corporate-modernist:cta_strip" data-variant="{{ variant|default:"book-call" }}"><div class="cm-content-well"><div style="display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1.5rem;">{% if headline %}<h2 class="cm-display" style="font-size: 1.75rem; margin: 0; max-width: 36rem;">{{ headline }}</h2>{% else %}<h2 class="cm-display" style="font-size: 1.75rem; margin: 0; max-width: 36rem;">Ready when you are.</h2>{% endif %}{% if primaryCta.label or primaryCta.text or primaryCta.href or primaryCta.url %}<a class="cm-btn-primary" href="{{ primaryCta.href|default:primaryCta.url|default:"#" }}">{% if primaryCta.label %}{{ primaryCta.label }}{% elif primaryCta.text %}{{ primaryCta.text }}{% elif variant|default:"book-call" == "download" %}Download report{% elif variant|default:"book-call" == "quiet" %}Learn more{% else %}Book a call{% endif %}</a>{% else %}<a class="cm-btn-primary" href="#" aria-disabled="true">{% if variant|default:"book-call" == "download" %}Download report{% elif variant|default:"book-call" == "quiet" %}Learn more{% else %}Book a call{% endif %}</a>{% endif %}</div></div></section>

View File

@ -0,0 +1,30 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "CTA Strip",
"description": "Single-line accent strip with a headline and a primary call to action.",
"type": "object",
"properties": {
"headline": {
"type": "string",
"title": "Headline",
"x-editor": "text"
},
"primaryCta": {
"type": "object",
"title": "Primary CTA",
"x-editor": "link",
"properties": {
"label": { "type": "string", "title": "Label" },
"href": { "type": "string", "title": "Href" }
}
},
"variant": {
"type": "string",
"title": "Variant",
"description": "Visual treatment for the strip",
"x-editor": "select",
"enum": ["book-call", "download", "quiet"],
"default": "book-call"
}
}
}

View File

@ -1 +0,0 @@
<section class="cm-section" data-block="corporate-modernist:engagement_process"><div class="cm-content-well">{% if eyebrow %}<p class="cm-eyebrow">{{ eyebrow }}</p>{% endif %}{% if heading %}<h2 class="cm-heading" style="font-size: clamp(1.75rem, 3vw, 2.25rem); margin: 0 0 0.75rem 0;"><span class="cm-heading-h2-accent">{{ heading }}</span></h2>{% endif %}{% if intro %}<p class="cm-lede" style="max-width: 60ch; margin: 0 0 2.5rem 0;">{{ intro }}</p>{% endif %}{% if steps %}<div>{% for step in steps %}<div class="cm-timeline-step"><div class="cm-timeline-rail"><span class="cm-timeline-node">{{ forloop.Counter }}</span>{% if not forloop.Last %}<span class="cm-timeline-line" aria-hidden="true"></span>{% endif %}</div><div style="padding-top: 0.375rem;">{% if step.title %}<h3 class="cm-feature-title" style="margin-top: 0;">{{ step.title }}</h3>{% endif %}{% if step.text %}<p class="cm-text" style="font-size: 0.9375rem; line-height: 1.6; color: hsl(var(--muted-foreground));">{{ step.text }}</p>{% endif %}</div></div>{% endfor %}</div>{% else %}<p class="cm-stat-label">No steps configured.</p>{% endif %}</div></section>

View File

@ -1,23 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Engagement Process",
"description": "Numbered engagement phases on a hairline rail.",
"type": "object",
"properties": {
"eyebrow": { "type": "string", "title": "Eyebrow", "x-editor": "text" },
"heading": { "type": "string", "title": "Heading", "x-editor": "text" },
"intro": { "type": "string", "title": "Intro", "x-editor": "textarea" },
"steps": {
"type": "array",
"title": "Steps",
"x-editor": "collection",
"items": {
"type": "object",
"properties": {
"title": { "type": "string", "title": "Title", "x-editor": "text" },
"text": { "type": "string", "title": "Description", "x-editor": "textarea" }
}
}
}
}
}

1
blocks/footer.ninjatpl Normal file
View File

@ -0,0 +1 @@
<div class="cm-footer" data-block="corporate-modernist:footer" data-variant="{{ variant|default:"compact" }}"><div class="cm-content-well">{% if variant|default:"compact" == "full" and columns %}<div class="cm-swiss-12">{% for col in columns %}<div style="{% if columns|length >= 4 %}grid-column: span 3 / span 3;{% elif columns|length == 3 %}grid-column: span 4 / span 4;{% elif columns|length == 2 %}grid-column: span 6 / span 6;{% else %}grid-column: span 12 / span 12;{% endif %}">{% if col.title %}<div class="cm-display" style="font-size: 0.875rem; font-weight: 600; margin: 0 0 0.75rem 0; text-transform: uppercase; letter-spacing: 0.05em;">{{ col.title }}</div>{% endif %}{% if col.links %}<ul style="list-style: none; padding: 0; margin: 0;">{% for l in col.links %}{% if l.label or l.text or l.href or l.url %}<li style="margin-bottom: 0.5rem;"><a href="{{ l.href|default:l.url|default:"#" }}" class="cm-text" style="font-size: 0.95rem; text-decoration: none;">{{ l.label|default:l.text|default:l.href|default:l.url|default:"#" }}</a></li>{% endif %}{% endfor %}</ul>{% endif %}</div>{% endfor %}</div>{% endif %}{% if showLegal != false and showLegal != "false" %}<div class="cm-footer-legal">{% if legalLine %}{{ legalLine }}{% else %}All rights reserved.{% endif %}</div>{% endif %}</div></div>

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

@ -0,0 +1,52 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Footer",
"description": "Multi-column footer with legal line, available in compact and full variants.",
"type": "object",
"properties": {
"columns": {
"type": "array",
"title": "Columns",
"description": "Each column lists a title and a set of links",
"default": [],
"x-editor": "collection",
"items": {
"type": "object",
"properties": {
"title": { "type": "string", "title": "Title", "x-editor": "text" },
"links": {
"type": "array",
"title": "Links",
"x-editor": "collection",
"items": {
"type": "object",
"properties": {
"label": { "type": "string", "title": "Label", "x-editor": "text" },
"href": { "type": "string", "title": "Href", "x-editor": "link" }
}
}
}
}
}
},
"legalLine": {
"type": "string",
"title": "Legal line",
"description": "Copyright or compliance statement",
"x-editor": "text"
},
"showLegal": {
"type": "boolean",
"title": "Show legal line",
"default": true,
"x-editor": "select"
},
"variant": {
"type": "string",
"title": "Variant",
"x-editor": "select",
"enum": ["compact", "full"],
"default": "compact"
}
}
}

View File

@ -0,0 +1 @@
<section class="cm-section" data-block="corporate-modernist:hero_statement"><div class="cm-content-well"><div class="cm-swiss-12"><div style="grid-column: span 12 / span 12;">{% if eyebrow %}<div class="cm-eyebrow">{{ eyebrow }}</div>{% endif %}{% if headline %}<h1 class="cm-display" style="font-size: clamp(2rem, 4vw, 3.5rem); line-height: 1.1; margin: 0 0 1.25rem 0;">{{ headline }}</h1>{% endif %}{% if lede %}<p class="cm-text" style="max-width: 60ch; margin: 0 0 2rem 0;">{{ lede }}</p>{% endif %}{% if primaryCta.label or primaryCta.text or primaryCta.href or primaryCta.url or secondaryCta.label or secondaryCta.text or secondaryCta.href or secondaryCta.url %}<div style="display: flex; flex-wrap: wrap; gap: 0.75rem;">{% if primaryCta.label or primaryCta.text or primaryCta.href or primaryCta.url %}<a class="cm-btn-primary" href="{{ primaryCta.href|default:primaryCta.url|default:"#" }}">{{ primaryCta.label|default:primaryCta.text|default:"Get in touch" }}</a>{% endif %}{% if secondaryCta.label or secondaryCta.text or secondaryCta.href or secondaryCta.url %}<a class="cm-btn-outline" href="{{ secondaryCta.href|default:secondaryCta.url|default:"#" }}">{{ secondaryCta.label|default:secondaryCta.text|default:"Learn more" }}</a>{% endif %}</div>{% endif %}</div></div></div></section>

View File

@ -0,0 +1,46 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Hero Statement",
"description": "Restrained hero with eyebrow, headline, lede, and a single accent CTA (plus optional outline secondary).",
"type": "object",
"properties": {
"eyebrow": {
"type": "string",
"title": "Eyebrow",
"description": "Small uppercase label above the headline",
"x-editor": "text"
},
"headline": {
"type": "string",
"title": "Headline",
"description": "Lead statement, typically one short sentence",
"x-editor": "text"
},
"lede": {
"type": "string",
"title": "Lede",
"description": "Supporting paragraph below the headline",
"x-editor": "textarea"
},
"primaryCta": {
"type": "object",
"title": "Primary CTA",
"description": "Accent-filled call to action",
"x-editor": "link",
"properties": {
"label": { "type": "string", "title": "Label" },
"href": { "type": "string", "title": "Href" }
}
},
"secondaryCta": {
"type": "object",
"title": "Secondary CTA",
"description": "Optional outline call to action",
"x-editor": "link",
"properties": {
"label": { "type": "string", "title": "Label" },
"href": { "type": "string", "title": "Href" }
}
}
}
}

View File

@ -0,0 +1 @@
<section class="cm-section" data-block="corporate-modernist:leadership_grid"><div class="cm-content-well">{% if title %}<h2 class="cm-heading" style="font-size: 1.875rem; margin: 0 0 2rem 0;"><span class="cm-heading-h2-accent">{{ title }}</span></h2>{% endif %}{% if members %}<div class="cm-leadership-grid">{% for m in members %}{% if m.name %}<article class="cm-card">{% if m.photo %}<img src="{{ m.photo }}" alt="{{ m.name|default:"Team member portrait" }}" style="width: 100%; aspect-ratio: 1 / 1; object-fit: cover; display: block; margin-bottom: 1rem; border-radius: 4px;">{% else %}<span class="cm-img-empty" style="aspect-ratio: 1 / 1; margin-bottom: 1rem; border-radius: 4px;"></span>{% endif %}<div class="cm-display" style="font-size: 1.125rem; font-weight: 600; margin: 0;">{{ m.name }}</div>{% if m.role %}<div class="cm-stat-label" style="margin-top: 0.25rem;">{{ m.role }}</div>{% endif %}{% if m.bio %}<p class="cm-text" style="margin: 0.75rem 0 0 0; font-size: 0.95rem; line-height: 1.55;">{{ m.bio }}</p>{% endif %}{% if m.linkedin %}<div style="margin-top: 1rem;"><a class="cm-btn-outline" href="{{ m.linkedin }}" style="font-size: 0.875rem; padding: 0.375rem 0.75rem;">LinkedIn</a></div>{% endif %}</article>{% endif %}{% endfor %}</div>{% else %}<p class="cm-stat-label">No team members configured.</p>{% endif %}</div></section>

View File

@ -0,0 +1,32 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Leadership Grid",
"description": "Responsive 2/3/4-up grid of leadership profiles.",
"type": "object",
"properties": {
"title": {
"type": "string",
"title": "Title",
"description": "Section title above the grid",
"x-editor": "text"
},
"members": {
"type": "array",
"title": "Members",
"description": "Each member is a card with photo, name, role, short bio, and optional LinkedIn link",
"default": [],
"x-editor": "collection",
"items": {
"type": "object",
"properties": {
"name": { "type": "string", "title": "Name", "x-editor": "text" },
"role": { "type": "string", "title": "Role", "x-editor": "text" },
"bio": { "type": "string", "title": "Bio", "x-editor": "textarea" },
"photo": { "type": "string", "title": "Photo", "x-editor": "media" },
"linkedin": { "type": "string", "title": "LinkedIn URL", "x-editor": "link" }
},
"required": ["name"]
}
}
}
}

View File

@ -0,0 +1 @@
<section class="cm-section" data-block="corporate-modernist:logo_strip"><div class="cm-content-well">{% if caption %}<p class="cm-stat-label" style="text-align: center; margin-bottom: 2rem;">{{ caption }}</p>{% endif %}{% if logos %}<div style="display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 2.5rem;">{% for logo in logos %}{% if logo.src or logo.alt %}{% if logo.href %}<a href="{{ logo.href }}" class="cm-logo" style="display: inline-flex; align-items: center;">{% if logo.src %}<img src="{{ logo.src }}" alt="{{ logo.alt|default:"Client logo" }}" style="max-height: 2rem; width: auto; display: block;">{% else %}<span class="cm-stat-label" style="margin: 0;">{{ logo.alt|default:"Client logo" }}</span>{% endif %}</a>{% else %}<span class="cm-logo" style="display: inline-flex; align-items: center;">{% if logo.src %}<img src="{{ logo.src }}" alt="{{ logo.alt|default:"Client logo" }}" style="max-height: 2rem; width: auto; display: block;">{% else %}<span class="cm-stat-label" style="margin: 0;">{{ logo.alt|default:"Client logo" }}</span>{% endif %}</span>{% endif %}{% endif %}{% endfor %}</div>{% else %}<p class="cm-stat-label" style="text-align: center;">No client logos configured.</p>{% endif %}</div></section>

View File

@ -0,0 +1,42 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Client Logo Strip",
"description": "Row of client logos rendered greyscale until hover.",
"type": "object",
"properties": {
"caption": {
"type": "string",
"title": "Caption",
"description": "Short heading above the logo row",
"x-editor": "text"
},
"logos": {
"type": "array",
"title": "Logos",
"description": "Client logos shown in a single restrained row",
"default": [],
"x-editor": "collection",
"items": {
"type": "object",
"properties": {
"src": {
"type": "string",
"title": "Image",
"x-editor": "media"
},
"alt": {
"type": "string",
"title": "Alt text",
"x-editor": "text"
},
"href": {
"type": "string",
"title": "Link",
"x-editor": "link"
}
},
"required": ["alt"]
}
}
}
}

View File

@ -0,0 +1 @@
<section class="cm-section" data-block="corporate-modernist:stat_pair"><div class="cm-content-well">{% if title %}<h2 class="cm-heading" style="font-size: 1.875rem; margin: 0 0 2rem 0;"><span class="cm-heading-h2-accent">{{ title }}</span></h2>{% endif %}{% if stats %}<div class="cm-swiss-12" style="row-gap: 2rem;">{% for s in stats %}{% if s.figure %}<div style="{% if stats|length >= 4 %}grid-column: span 3 / span 3;{% elif stats|length == 3 %}grid-column: span 4 / span 4;{% elif stats|length == 2 %}grid-column: span 6 / span 6;{% else %}grid-column: span 12 / span 12;{% endif %}" class="cm-hairline-top"><div class="cm-stat-figure numeric-tabular" data-figure style="padding-top: 1rem;">{{ s.figure }}</div>{% if s.label %}<div class="cm-stat-label">{{ s.label }}</div>{% endif %}{% if s.source %}<div class="cm-stat-label" style="margin-top: 0.25rem; text-transform: none; letter-spacing: 0; opacity: 0.8;">{{ s.source }}</div>{% endif %}</div>{% endif %}{% endfor %}</div>{% else %}<p class="cm-stat-label">No statistics configured.</p>{% endif %}</div></section>

View File

@ -0,0 +1,30 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Stat Pair",
"description": "Pair (or row) of mono-numeral statistics, separated by a hairline rule.",
"type": "object",
"properties": {
"title": {
"type": "string",
"title": "Title",
"description": "Optional title above the stats row",
"x-editor": "text"
},
"stats": {
"type": "array",
"title": "Stats",
"description": "Individual statistic entries",
"default": [],
"x-editor": "collection",
"items": {
"type": "object",
"properties": {
"figure": { "type": "string", "title": "Figure", "x-editor": "text" },
"label": { "type": "string", "title": "Label", "x-editor": "text" },
"source": { "type": "string", "title": "Source", "x-editor": "text" }
},
"required": ["figure"]
}
}
}
}

View File

@ -0,0 +1 @@
<section class="cm-section" data-block="corporate-modernist:testimonial_quote"><div class="cm-content-well"><figure style="max-width: 70ch; margin: 0 auto;">{% if quote %}<blockquote class="cm-pull-quote">{{ quote }}</blockquote>{% else %}<blockquote class="cm-pull-quote">No testimonial provided.</blockquote>{% endif %}<figcaption style="display: flex; align-items: center; gap: 1rem; margin-top: 1.5rem;">{% if headshot %}<img src="{{ headshot }}" alt="{{ name|default:"Testimonial portrait" }}" style="width: 3rem; height: 3rem; border-radius: 50%; object-fit: cover; display: block;">{% else %}<span class="cm-img-empty" style="width: 3rem; height: 3rem; aspect-ratio: 1 / 1; border-radius: 50%;"></span>{% endif %}<div>{% if name %}<div class="cm-display" style="font-size: 1rem; font-weight: 600; margin: 0;">{{ name }}</div>{% endif %}{% if role or company %}<div class="cm-stat-label" style="margin: 0.25rem 0 0 0;">{% if role and company %}{{ role }}, {{ company }}{% elif role %}{{ role }}{% else %}{{ company }}{% endif %}</div>{% endif %}</div>{% if logo %}<img src="{{ logo }}" alt="{% if company %}{{ company }} logo{% else %}Company logo{% endif %}" class="cm-logo" style="max-height: 1.5rem; width: auto; margin-left: auto; display: block;">{% endif %}</figcaption></figure></div></section>

View File

@ -0,0 +1,41 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Testimonial",
"description": "Pull-quote testimonial with attribution and optional headshot and company logo.",
"type": "object",
"properties": {
"quote": {
"type": "string",
"title": "Quote",
"description": "Testimonial body text",
"x-editor": "textarea"
},
"name": {
"type": "string",
"title": "Name",
"x-editor": "text"
},
"role": {
"type": "string",
"title": "Role",
"x-editor": "text"
},
"company": {
"type": "string",
"title": "Company",
"x-editor": "text"
},
"headshot": {
"type": "string",
"title": "Headshot",
"description": "Portrait image of the quoted person",
"x-editor": "media"
},
"logo": {
"type": "string",
"title": "Company logo",
"description": "Optional company mark beside the attribution",
"x-editor": "media"
}
}
}

View File

@ -1,20 +1 @@
[ []
{
"name": "Inter",
"family": "Inter",
"variants": [
{ "weight": "400", "style": "normal", "file": "fonts/inter-latin-400.woff2" },
{ "weight": "500", "style": "normal", "file": "fonts/inter-latin-500.woff2" },
{ "weight": "600", "style": "normal", "file": "fonts/inter-latin-600.woff2" },
{ "weight": "700", "style": "normal", "file": "fonts/inter-latin-700.woff2" }
]
},
{
"name": "JetBrains Mono",
"family": "JetBrains Mono",
"variants": [
{ "weight": "400", "style": "normal", "file": "fonts/jetbrains-mono-latin.woff2" },
{ "weight": "500", "style": "normal", "file": "fonts/jetbrains-mono-latin.woff2" }
]
}
]

View File

@ -1,22 +1,13 @@
# Corporate Modernist theme — codeless .bnp manifest (WO-TF-014, Wave A). # Corporate Modernist theme — codeless .bnp manifest (WO-WZ-021 Phase B).
# Synthesized into manifest.pb by `ninja plugin build` (no plugin.wasm). # Synthesized into manifest.pb by `ninja plugin build` (no plugin.wasm).
#
# Identity: Swiss grid disciplined by Bauhaus geometric accents (circle /
# triangle / square), oversized tabular numerals, hairline dividers. Restrained
# B2B-consultancy authority. Light = gallery white + graphite ink; dark =
# graphite boardroom. Motion is CSS-first (no canvas, no JS showpiece).
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
required_icon_packs:
- lucide
- simple-icons
system_templates: system_templates:
- key: corporate-modernist - key: corporate-modernist
title: Corporate Modernist title: Corporate Modernist
description: "Swiss-grid B2B theme with restrained typography, a single configurable accent, oversized numerals, and trust-signal chrome for consultancy and enterprise sites." description: "Swiss-grid B2B theme with restrained typography, a single configurable accent, and trust-signal blocks for enterprise sites."
page_templates: page_templates:
- system: corporate-modernist - system: corporate-modernist
@ -24,11 +15,6 @@ page_templates:
title: Default title: Default
description: "Header + 12-col main + footer, capped at 1280px." description: "Header + 12-col main + footer, capped at 1280px."
slots: [header, main, footer] slots: [header, main, footer]
- system: corporate-modernist
key: full-width
title: Full width
description: "Edge-to-edge sections; internal grid kept."
slots: [header, main, footer]
- system: corporate-modernist - system: corporate-modernist
key: landing key: landing
title: Landing title: Landing
@ -40,317 +26,60 @@ page_templates:
description: "Centered measure for long-form thought leadership." description: "Centered measure for long-form thought leadership."
slots: [header, main, aside, footer] slots: [header, main, aside, footer]
- system: corporate-modernist - system: corporate-modernist
key: blog-index key: full-width
title: Blog index title: Full width
description: "Title band, featured lead, and a hairline-ruled post grid." description: "Edge-to-edge sections, internal grid kept."
slots: [header, featured, main, footer] slots: [header, main, footer]
- system: corporate-modernist
key: contact
title: Contact
description: "Two-column contact: form on the grid, details in the aside."
slots: [header, main, aside, footer]
- system: corporate-modernist
key: auth
title: Auth
description: "Centered card on a quiet ground for login and reset surfaces."
slots: [main, footer]
template_overrides: template_overrides:
# Chrome (4)
- { template: corporate-modernist, block: navbar }
- { template: corporate-modernist, block: footer }
- { template: corporate-modernist, block: utility-bar }
- { template: corporate-modernist, block: announcement-bar }
# Marketing (12)
- { template: corporate-modernist, block: hero }
- { template: corporate-modernist, block: feature-grid }
- { template: corporate-modernist, block: rich-section }
- { template: corporate-modernist, block: stats }
- { template: corporate-modernist, block: testimonial }
- { template: corporate-modernist, block: logo-strip }
- { template: corporate-modernist, block: pricing }
- { template: corporate-modernist, block: team }
- { template: corporate-modernist, block: faq }
- { template: corporate-modernist, block: timeline }
- { template: corporate-modernist, block: cta }
- { template: corporate-modernist, block: countdown }
# Primitives (11)
- { template: corporate-modernist, block: heading } - { template: corporate-modernist, block: heading }
- { template: corporate-modernist, block: text } - { template: corporate-modernist, block: text }
- { template: corporate-modernist, block: rich-text }
- { template: corporate-modernist, block: image }
- { template: corporate-modernist, block: quote }
- { template: corporate-modernist, block: divider }
- { template: corporate-modernist, block: button } - { template: corporate-modernist, block: button }
- { template: corporate-modernist, block: card } - { template: corporate-modernist, block: card }
- { template: corporate-modernist, block: gallery }
- { template: corporate-modernist, block: video-embed }
- { template: corporate-modernist, block: audio-embed }
# Commerce / local (6)
- { template: corporate-modernist, block: menu-list }
- { template: corporate-modernist, block: event-list }
- { template: corporate-modernist, block: contact-form }
- { template: corporate-modernist, block: contact-card }
- { template: corporate-modernist, block: hours-location }
- { template: corporate-modernist, block: social-links }
# Blog family (12)
- { template: corporate-modernist, block: blog-hero }
- { template: corporate-modernist, block: featured-posts }
- { template: corporate-modernist, block: related-posts }
- { template: corporate-modernist, block: popular-posts }
- { template: corporate-modernist, block: category-list }
- { template: corporate-modernist, block: post-hero }
- { template: corporate-modernist, block: post-metadata }
- { template: corporate-modernist, block: author-bio }
- { template: corporate-modernist, block: author-bio-hero }
- { template: corporate-modernist, block: newsletter-signup }
- { template: corporate-modernist, block: breadcrumbs }
- { template: corporate-modernist, block: sidebar-nav }
# Auth surface (3)
- { template: corporate-modernist, block: auth-form }
- { template: corporate-modernist, block: password-reset-form }
- { template: corporate-modernist, block: auth-status }
# System (1)
- { template: corporate-modernist, block: page-suggestions }
email_wrappers: email_wrappers:
- corporate-modernist - corporate-modernist
css: css:
input_css_append: | input_css_append: |
/* ============================================================ /* Corporate Modernist — appended utility CSS. */
Corporate Modernist — appended component CSS.
Semantic tokens only: hsl(var(--token)). No literal colors.
Swiss grid + Bauhaus geometric marks + oversized numerals.
============================================================ */
/* --- Layout primitives --- */
.cm-content-well { width: 100%; max-width: 1280px; margin-left: auto; margin-right: auto; padding-left: 1.5rem; padding-right: 1.5rem; box-sizing: border-box; } .cm-content-well { width: 100%; max-width: 1280px; margin-left: auto; margin-right: auto; padding-left: 1.5rem; padding-right: 1.5rem; box-sizing: border-box; }
.cm-measure { max-width: 70ch; }
.cm-swiss-12 { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: 1.5rem; row-gap: 1.5rem; } .cm-swiss-12 { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: 1.5rem; row-gap: 1.5rem; }
.cm-section { padding-top: 3rem; padding-bottom: 3rem; } .cm-hairline { border-color: hsl(var(--border)); border-width: 1px; border-style: solid; }
@media (min-width: 768px) { .cm-section { padding-top: 3.5rem; padding-bottom: 3.5rem; } }
.cm-section-tight { padding-top: 2rem; padding-bottom: 2rem; }
.cm-hairline { border: 1px solid hsl(var(--border)); }
.cm-hairline-top { border-top: 1px solid hsl(var(--border)); } .cm-hairline-top { border-top: 1px solid hsl(var(--border)); }
.cm-hairline-bottom { border-bottom: 1px solid hsl(var(--border)); } .cm-hairline-bottom { border-bottom: 1px solid hsl(var(--border)); }
.cm-hairline-y > * + * { border-top: 1px solid hsl(var(--border)); }
.cm-accent-rule { border-top: 2px solid hsl(var(--accent)); }
.cm-stack > * + * { margin-top: 1.5rem; }
.cm-stack-sm > * + * { margin-top: 0.75rem; }
/* --- Typography --- */
.cm-display { font-family: var(--font-heading, "Inter", system-ui, sans-serif); font-weight: 600; letter-spacing: -0.02em; color: hsl(var(--foreground)); margin: 0; }
.cm-body { font-family: var(--font-body, "Inter", system-ui, sans-serif); }
.cm-mono { font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-variant-numeric: tabular-nums; }
.numeric-tabular { font-variant-numeric: tabular-nums; } .numeric-tabular { font-variant-numeric: tabular-nums; }
.cm-heading { font-family: var(--font-heading, "Inter", system-ui, sans-serif); font-weight: 600; letter-spacing: -0.02em; color: hsl(var(--foreground)); margin: 0; } .cm-display { font-family: var(--font-heading, "Inter Tight", "Inter", system-ui, sans-serif); font-weight: 600; letter-spacing: -0.02em; }
.cm-heading-h2-accent { border-bottom: 2px solid hsl(var(--accent)); padding-bottom: 0.25rem; display: inline-block; } .cm-body { font-family: var(--font-body, "Inter", system-ui, sans-serif); font-size: 17px; line-height: 28px; font-variant-numeric: tabular-nums; }
.cm-text { font-family: var(--font-body, "Inter", system-ui, sans-serif); font-size: 17px; line-height: 28px; color: hsl(var(--foreground)); font-variant-numeric: tabular-nums; } .cm-mono { font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-variant-numeric: tabular-nums; }
.cm-text a { color: hsl(var(--accent)); text-decoration: underline; text-underline-offset: 2px; } .cm-accent-rule { border-top: 2px solid hsl(var(--accent)); }
.cm-text strong { font-weight: 600; } .cm-btn-primary { display: inline-block; padding: 0.625rem 1.25rem; background-color: hsl(var(--accent)); color: hsl(var(--accent-foreground)); border: 1px solid hsl(var(--accent)); border-radius: 4px; font-family: var(--font-heading, "Inter Tight", "Inter", system-ui, sans-serif); font-weight: 500; text-decoration: none; line-height: 1.4; box-shadow: none; background-image: none; transition: background-color 150ms ease; }
.cm-lede { font-family: var(--font-body, "Inter", system-ui, sans-serif); font-size: 1.25rem; line-height: 1.6; color: hsl(var(--muted-foreground)); }
.cm-eyebrow { font-family: var(--font-heading, "Inter", system-ui, sans-serif); font-size: 0.8125rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.12em; color: hsl(var(--accent)); margin: 0 0 1rem 0; }
.cm-kicker { font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-size: 0.75rem; letter-spacing: 0.1em; text-transform: uppercase; color: hsl(var(--muted-foreground)); }
/* --- Bauhaus geometric marks (circle / triangle / square) --- */
.cm-mark { display: inline-block; width: 0.75rem; height: 0.75rem; flex: none; }
.cm-mark-circle { border-radius: 9999px; background-color: hsl(var(--accent)); }
.cm-mark-square { background-color: hsl(var(--accent)); }
.cm-mark-triangle { width: 0; height: 0; border-left: 0.4375rem solid transparent; border-right: 0.4375rem solid transparent; border-bottom: 0.75rem solid hsl(var(--accent)); background: none; }
.cm-mark-ring { border-radius: 9999px; background: none; border: 2px solid hsl(var(--accent)); }
/* --- Numerals / metrics (oversized signature) --- */
.cm-metric { font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-size: clamp(2.5rem, 5vw, 3.5rem); line-height: 1; font-weight: 500; font-variant-numeric: tabular-nums; color: hsl(var(--accent)); letter-spacing: -0.02em; }
.cm-stat-figure { font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-size: clamp(2.25rem, 4vw, 3rem); line-height: 1; font-weight: 500; font-variant-numeric: tabular-nums; color: hsl(var(--foreground)); letter-spacing: -0.02em; }
.cm-stat-label { font-family: var(--font-body, "Inter", system-ui, sans-serif); font-size: 0.8125rem; color: hsl(var(--muted-foreground)); text-transform: uppercase; letter-spacing: 0.06em; margin-top: 0.5rem; }
.cm-numeral-index { font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-size: 2rem; line-height: 1; font-weight: 500; font-variant-numeric: tabular-nums; color: hsl(var(--accent)); }
/* --- Buttons (squared, flat, no gradient/shadow) --- */
.cm-btn-primary { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.625rem 1.25rem; background-color: hsl(var(--accent)); color: hsl(var(--accent-foreground)); border: 1px solid hsl(var(--accent)); border-radius: 4px; font-family: var(--font-heading, "Inter", system-ui, sans-serif); font-weight: 500; text-decoration: none; line-height: 1.4; box-shadow: none; background-image: none; transition: background-color 150ms ease; cursor: pointer; }
.cm-btn-primary:hover { background-color: hsl(var(--accent) / 0.9); } .cm-btn-primary:hover { background-color: hsl(var(--accent) / 0.9); }
.cm-btn-primary:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; } .cm-btn-primary:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
.cm-btn-outline { display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.625rem 1.25rem; background-color: transparent; color: hsl(var(--foreground)); border: 1px solid hsl(var(--border)); border-radius: 4px; font-family: var(--font-heading, "Inter", system-ui, sans-serif); font-weight: 500; text-decoration: none; line-height: 1.4; box-shadow: none; background-image: none; transition: border-color 150ms ease, color 150ms ease; cursor: pointer; } .cm-btn-outline { display: inline-block; padding: 0.625rem 1.25rem; background-color: transparent; color: hsl(var(--foreground)); border: 1px solid hsl(var(--border)); border-radius: 4px; font-family: var(--font-heading, "Inter Tight", "Inter", system-ui, sans-serif); font-weight: 500; text-decoration: none; line-height: 1.4; box-shadow: none; background-image: none; transition: border-color 150ms ease; }
.cm-btn-outline:hover { border-color: hsl(var(--foreground)); } .cm-btn-outline:hover { border-color: hsl(var(--foreground)); }
.cm-btn-outline:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; } .cm-btn-outline:focus-visible { outline: 2px solid hsl(var(--ring)); outline-offset: 2px; }
.cm-btn-ghost { display: inline-flex; align-items: center; gap: 0.375rem; color: hsl(var(--accent)); font-family: var(--font-heading, "Inter", system-ui, sans-serif); font-weight: 500; text-decoration: none; line-height: 1.4; transition: gap 150ms ease; }
.cm-btn-ghost:hover { gap: 0.625rem; }
.cm-btn-sm { padding: 0.375rem 0.875rem; font-size: 0.875rem; }
.cm-btn-lg { padding: 0.875rem 1.75rem; font-size: 1.0625rem; }
.cm-btn-row { display: flex; flex-wrap: wrap; gap: 0.75rem; }
.cm-icon { width: 1em; height: 1em; display: inline-block; vertical-align: -0.125em; }
/* --- Surfaces / cards --- */
.cm-card { background-color: hsl(var(--card)); color: hsl(var(--card-foreground)); border: 1px solid hsl(var(--border)); border-radius: 4px; padding: 1.5rem; box-shadow: none; } .cm-card { background-color: hsl(var(--card)); color: hsl(var(--card-foreground)); border: 1px solid hsl(var(--border)); border-radius: 4px; padding: 1.5rem; box-shadow: none; }
.cm-card-accent { border-top: 2px solid hsl(var(--accent)); } .cm-card.cm-card-accent { border-top: 2px solid hsl(var(--accent)); }
.cm-panel { background-color: hsl(var(--muted)); border-radius: 4px; padding: 1.5rem; } .cm-heading { font-family: var(--font-heading, "Inter Tight", "Inter", system-ui, sans-serif); font-weight: 600; letter-spacing: -0.02em; color: hsl(var(--foreground)); margin: 0; }
.cm-surface-muted { background-color: hsl(var(--muted)); } .cm-heading-h2-accent { border-bottom: 2px solid hsl(var(--accent)); padding-bottom: 0.25rem; display: inline-block; }
.cm-surface-card { background-color: hsl(var(--card)); } .cm-text { font-family: var(--font-body, "Inter", system-ui, sans-serif); font-size: 17px; line-height: 28px; color: hsl(var(--foreground)); font-variant-numeric: tabular-nums; hanging-punctuation: first last; }
.cm-media-frame { display: block; width: 100%; overflow: hidden; background-color: hsl(var(--muted)); border: 1px solid hsl(var(--border)); } .cm-logo { filter: grayscale(1); opacity: 0.7; transition: filter 200ms ease, opacity 200ms ease; }
.cm-media-frame img { display: block; width: 100%; height: 100%; object-fit: cover; } .cm-logo:hover { filter: grayscale(0); opacity: 1; }
.cm-img-empty { display: block; width: 100%; aspect-ratio: 4 / 3; background-color: hsl(var(--muted)); border: 1px solid hsl(var(--border)); } .cm-pull-quote { border-top: 2px solid hsl(var(--accent)); padding-top: 1.25rem; font-family: var(--font-heading, "Inter Tight", "Inter", system-ui, sans-serif); font-size: 1.5rem; line-height: 1.4; font-weight: 500; color: hsl(var(--foreground)); }
.cm-metric { font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-size: 3rem; line-height: 1; font-weight: 500; font-variant-numeric: tabular-nums; color: hsl(var(--accent)); }
/* --- Responsive grids --- */ .cm-stat-figure { font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-size: 2.5rem; line-height: 1; font-weight: 500; font-variant-numeric: tabular-nums; color: hsl(var(--foreground)); }
.cm-grid { display: grid; gap: 1.5rem; grid-template-columns: repeat(1, minmax(0, 1fr)); } .cm-stat-label { font-family: var(--font-body, "Inter", system-ui, sans-serif); font-size: 0.875rem; color: hsl(var(--muted-foreground)); text-transform: uppercase; letter-spacing: 0.05em; margin-top: 0.5rem; }
@media (min-width: 640px) { .cm-grid-2, .cm-grid-3, .cm-grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } } .cm-eyebrow { font-family: var(--font-heading, "Inter Tight", "Inter", system-ui, sans-serif); font-size: 0.875rem; font-weight: 500; text-transform: uppercase; letter-spacing: 0.1em; color: hsl(var(--accent)); margin-bottom: 1rem; }
@media (min-width: 1024px) { .cm-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } .cm-grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } } .cm-cta-strip-accent { background-color: hsl(var(--accent)); color: hsl(var(--accent-foreground)); }
.cm-leadership-grid { display: grid; gap: 1.5rem; grid-template-columns: repeat(1, minmax(0, 1fr)); } .cm-cta-strip-accent .cm-btn-primary { background-color: hsl(var(--accent-foreground)); color: hsl(var(--accent)); border-color: hsl(var(--accent-foreground)); }
.cm-cta-strip-quiet { background-color: hsl(var(--muted)); color: hsl(var(--foreground)); }
.cm-footer { background-color: hsl(var(--background)); color: hsl(var(--foreground)); border-top: 1px solid hsl(var(--border)); padding: 3rem 0; }
.cm-footer-legal { color: hsl(var(--muted-foreground)); font-size: 0.875rem; border-top: 1px solid hsl(var(--border)); padding-top: 1.5rem; margin-top: 2rem; }
.cm-leadership-grid { display: grid; grid-template-columns: repeat(1, minmax(0, 1fr)); gap: 1.5rem; }
@media (min-width: 640px) { .cm-leadership-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } } @media (min-width: 640px) { .cm-leadership-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .cm-leadership-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } } @media (min-width: 1024px) { .cm-leadership-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1280px) { .cm-leadership-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } } @media (min-width: 1280px) { .cm-leadership-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.cm-article-measure { max-width: 70ch; margin-left: auto; margin-right: auto; font-family: var(--font-body, "Inter", system-ui, sans-serif); font-size: 17px; line-height: 28px; color: hsl(var(--foreground)); }
/* --- Chrome: navbar --- */ .cm-section { padding-top: 4rem; padding-bottom: 4rem; }
.cm-navbar { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; padding: 1.25rem 0; } .cm-img-empty { display: block; width: 100%; aspect-ratio: 4 / 3; background-color: hsl(var(--muted)); border: 1px solid hsl(var(--border)); }
.cm-brand { font-family: var(--font-heading, "Inter", system-ui, sans-serif); font-weight: 700; font-size: 1.125rem; letter-spacing: -0.01em; color: hsl(var(--foreground)); text-decoration: none; display: inline-flex; align-items: center; gap: 0.625rem; }
.cm-nav-links { display: none; align-items: center; gap: 2rem; }
@media (min-width: 768px) { .cm-nav-links { display: flex; } }
.cm-nav-link { font-family: var(--font-body, "Inter", system-ui, sans-serif); font-size: 0.9375rem; color: hsl(var(--foreground)); text-decoration: none; padding-bottom: 2px; border-bottom: 2px solid transparent; transition: border-color 150ms ease; }
.cm-nav-link:hover { border-bottom-color: hsl(var(--accent)); }
.cm-nav-toggle { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; }
@media (min-width: 768px) { .cm-nav-toggle { display: none; } }
.cm-nav-drawer { display: none; flex-direction: column; gap: 0.25rem; padding: 0.5rem 0 1rem; border-top: 1px solid hsl(var(--border)); width: 100%; }
@media (min-width: 768px) { .cm-nav-drawer { display: none !important; } }
.cm-nav-drawer a { padding: 0.625rem 0; font-family: var(--font-body, "Inter", system-ui, sans-serif); color: hsl(var(--foreground)); text-decoration: none; border-bottom: 1px solid hsl(var(--border)); }
/* --- Chrome: utility bar + announcement bar --- */
.cm-utility-bar { font-family: var(--font-body, "Inter", system-ui, sans-serif); font-size: 0.8125rem; padding: 0.5rem 0; }
.cm-utility-bar--muted { background-color: hsl(var(--muted)); color: hsl(var(--muted-foreground)); }
.cm-utility-bar--primary { background-color: hsl(var(--primary)); color: hsl(var(--primary-foreground)); }
.cm-utility-bar--dark { background-color: hsl(var(--foreground)); color: hsl(var(--background)); }
.cm-utility-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.cm-utility-group { display: flex; align-items: center; gap: 1.25rem; }
.cm-utility-item { display: inline-flex; align-items: center; gap: 0.375rem; color: inherit; text-decoration: none; }
.cm-announce { font-family: var(--font-body, "Inter", system-ui, sans-serif); font-size: 0.875rem; padding: 0.625rem 0; }
.cm-announce--info { background-color: hsl(var(--muted)); color: hsl(var(--foreground)); }
.cm-announce--promo { background-color: hsl(var(--accent)); color: hsl(var(--accent-foreground)); }
.cm-announce--alert { background-color: hsl(var(--destructive)); color: hsl(var(--destructive-foreground)); }
.cm-announce-row { display: flex; align-items: center; justify-content: center; gap: 0.75rem; }
.cm-announce a { color: inherit; text-decoration: underline; text-underline-offset: 2px; font-weight: 600; }
.cm-announce-close { margin-left: auto; background: none; border: 0; color: inherit; cursor: pointer; line-height: 1; padding: 0 0.25rem; }
/* --- Chrome: footer --- */
.cm-footer { background-color: hsl(var(--background)); color: hsl(var(--foreground)); border-top: 2px solid hsl(var(--accent)); padding: 3.5rem 0 2.5rem; }
.cm-footer-cols { display: grid; gap: 2rem; grid-template-columns: repeat(1, minmax(0, 1fr)); }
@media (min-width: 640px) { .cm-footer-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .cm-footer-cols { grid-template-columns: 2fr repeat(3, 1fr); } }
.cm-footer-head { font-family: var(--font-heading, "Inter", system-ui, sans-serif); font-size: 0.8125rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: hsl(var(--muted-foreground)); margin: 0 0 0.875rem; }
.cm-footer-link { display: block; font-size: 0.9375rem; color: hsl(var(--foreground)); text-decoration: none; padding: 0.25rem 0; }
.cm-footer-link:hover { color: hsl(var(--accent)); }
.cm-footer-legal { color: hsl(var(--muted-foreground)); font-size: 0.8125rem; border-top: 1px solid hsl(var(--border)); padding-top: 1.5rem; margin-top: 2.5rem; display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; }
/* --- Marketing: hero --- */
.cm-hero { padding: 5rem 0; }
.cm-hero-headline { font-family: var(--font-heading, "Inter", system-ui, sans-serif); font-weight: 600; letter-spacing: -0.03em; line-height: 1.05; font-size: clamp(2.5rem, 6vw, 4.5rem); color: hsl(var(--foreground)); margin: 0; }
.cm-hero-sub { font-size: clamp(1.125rem, 2vw, 1.5rem); line-height: 1.5; color: hsl(var(--muted-foreground)); margin: 1.25rem 0 0; }
.cm-hero-split { display: grid; gap: 3rem; grid-template-columns: repeat(1, minmax(0, 1fr)); align-items: center; }
@media (min-width: 1024px) { .cm-hero-split { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* --- Marketing: logo strip --- */
.cm-logo { filter: grayscale(1); opacity: 0.65; transition: filter 200ms ease, opacity 200ms ease; }
.cm-logo:hover { filter: grayscale(0); opacity: 1; }
.cm-logo-row { display: flex; flex-wrap: wrap; align-items: center; gap: 2.5rem; }
/* --- Marketing: feature grid --- */
.cm-feature { border-top: 2px solid hsl(var(--accent)); padding-top: 1.25rem; }
.cm-feature-title { font-family: var(--font-heading, "Inter", system-ui, sans-serif); font-weight: 600; font-size: 1.125rem; color: hsl(var(--foreground)); margin: 0.75rem 0 0.5rem; }
/* --- Marketing: pull quote / testimonial --- */
.cm-pull-quote { border-top: 2px solid hsl(var(--accent)); padding-top: 1.25rem; font-family: var(--font-heading, "Inter", system-ui, sans-serif); font-size: 1.5rem; line-height: 1.4; font-weight: 500; color: hsl(var(--foreground)); }
.cm-quote-block { font-family: var(--font-heading, "Inter", system-ui, sans-serif); font-size: 1.375rem; line-height: 1.45; font-weight: 500; color: hsl(var(--foreground)); }
.cm-quote-cite { font-size: 0.875rem; color: hsl(var(--muted-foreground)); margin-top: 0.75rem; }
.cm-avatar { width: 2.75rem; height: 2.75rem; border-radius: 9999px; object-fit: cover; background-color: hsl(var(--muted)); }
/* --- Marketing: pricing --- */
.cm-price { font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-size: 2.5rem; font-weight: 500; font-variant-numeric: tabular-nums; color: hsl(var(--foreground)); letter-spacing: -0.02em; }
.cm-price-period { font-family: var(--font-body, "Inter", system-ui, sans-serif); font-size: 0.9375rem; color: hsl(var(--muted-foreground)); }
.cm-price-tier { display: flex; flex-direction: column; }
.cm-price-tier--featured { border-top: 4px solid hsl(var(--accent)); }
.cm-badge { display: inline-block; font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-size: 0.6875rem; letter-spacing: 0.08em; text-transform: uppercase; padding: 0.25rem 0.5rem; background-color: hsl(var(--accent)); color: hsl(var(--accent-foreground)); border-radius: 3px; }
.cm-check { color: hsl(var(--accent)); flex: none; }
/* --- Marketing: faq --- */
.cm-faq-item { border-top: 1px solid hsl(var(--border)); }
.cm-faq-item summary { cursor: pointer; list-style: none; padding: 1.25rem 0; display: flex; align-items: center; justify-content: space-between; gap: 1rem; font-family: var(--font-heading, "Inter", system-ui, sans-serif); font-weight: 600; color: hsl(var(--foreground)); }
.cm-faq-item summary::-webkit-details-marker { display: none; }
.cm-faq-item[open] summary .cm-faq-chevron { transform: rotate(180deg); }
.cm-faq-chevron { transition: transform 150ms ease; color: hsl(var(--accent)); }
.cm-faq-answer { padding: 0 0 1.25rem; color: hsl(var(--muted-foreground)); font-size: 1rem; line-height: 1.65; }
/* --- Marketing: timeline --- */
.cm-timeline-step { display: grid; grid-template-columns: auto 1fr; gap: 1.25rem; padding-bottom: 2rem; }
.cm-timeline-rail { position: relative; display: flex; flex-direction: column; align-items: center; }
.cm-timeline-node { width: 2.5rem; height: 2.5rem; border: 2px solid hsl(var(--accent)); border-radius: 9999px; display: flex; align-items: center; justify-content: center; font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-weight: 500; color: hsl(var(--accent)); flex: none; font-variant-numeric: tabular-nums; }
.cm-timeline-line { flex: 1 1 auto; width: 1px; background-color: hsl(var(--border)); margin-top: 0.5rem; }
/* --- Marketing: cta strip --- */
.cm-cta-strip { padding: 3.5rem 0; }
.cm-cta-strip--band { background-color: hsl(var(--foreground)); color: hsl(var(--background)); }
.cm-cta-strip--band .cm-btn-primary { background-color: hsl(var(--accent)); color: hsl(var(--accent-foreground)); border-color: hsl(var(--accent)); }
.cm-cta-strip--card { background-color: hsl(var(--muted)); }
.cm-cta-headline { font-family: var(--font-heading, "Inter", system-ui, sans-serif); font-weight: 600; letter-spacing: -0.02em; font-size: clamp(1.75rem, 3vw, 2.5rem); line-height: 1.15; margin: 0; }
/* --- Marketing: countdown --- */
.cm-count-unit { display: flex; flex-direction: column; align-items: center; }
.cm-count-figure { font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-size: clamp(2rem, 4vw, 3rem); font-weight: 500; font-variant-numeric: tabular-nums; color: hsl(var(--foreground)); line-height: 1; }
.cm-count-box { border: 1px solid hsl(var(--border)); padding: 1rem 1.25rem; border-radius: 4px; min-width: 4.5rem; }
/* --- Primitives: quote / divider / gallery / media --- */
.cm-divider-line { border: 0; border-top: 1px solid hsl(var(--border)); }
.cm-divider-thick { border-top-width: 2px; border-top-color: hsl(var(--accent)); }
.cm-divider-ornament { display: flex; align-items: center; justify-content: center; gap: 0.75rem; color: hsl(var(--muted-foreground)); }
.cm-gallery-grid { display: grid; gap: 0.75rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 768px) { .cm-gallery-grid.cm-gallery-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } .cm-gallery-grid.cm-gallery-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.cm-video-frame { position: relative; display: block; width: 100%; background-color: hsl(var(--muted)); border: 1px solid hsl(var(--border)); overflow: hidden; }
.cm-video-play { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: hsl(var(--background)); background: none; border: 0; cursor: pointer; }
.cm-video-play-badge { width: 4rem; height: 4rem; border-radius: 9999px; background-color: hsl(var(--accent)); color: hsl(var(--accent-foreground)); display: flex; align-items: center; justify-content: center; }
/* --- Commerce / local --- */
.cm-menu-item { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; padding: 0.75rem 0; border-bottom: 1px solid hsl(var(--border)); }
.cm-menu-price { font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-variant-numeric: tabular-nums; color: hsl(var(--foreground)); }
.cm-channel { display: flex; align-items: flex-start; gap: 0.75rem; }
.cm-channel-mark { width: 2.5rem; height: 2.5rem; border: 1px solid hsl(var(--border)); border-radius: 4px; display: flex; align-items: center; justify-content: center; color: hsl(var(--accent)); flex: none; }
.cm-hours-row { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; padding: 0.375rem 0; border-bottom: 1px solid hsl(var(--border)); }
.cm-social-row { display: flex; flex-wrap: wrap; gap: 0.75rem; }
.cm-social-link { display: inline-flex; align-items: center; gap: 0.375rem; padding: 0.5rem 0.75rem; border: 1px solid hsl(var(--border)); border-radius: 4px; color: hsl(var(--foreground)); text-decoration: none; font-size: 0.875rem; transition: border-color 150ms ease; }
.cm-social-link:hover { border-color: hsl(var(--accent)); color: hsl(var(--accent)); }
/* --- Forms --- */
.cm-form { display: grid; gap: 1.25rem; }
.cm-form-grid { display: grid; gap: 1.25rem; grid-template-columns: repeat(1, minmax(0, 1fr)); }
@media (min-width: 640px) { .cm-form-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.cm-field { display: flex; flex-direction: column; gap: 0.375rem; }
.cm-field--full { grid-column: 1 / -1; }
.cm-label { font-family: var(--font-heading, "Inter", system-ui, sans-serif); font-size: 0.8125rem; font-weight: 600; color: hsl(var(--foreground)); text-transform: uppercase; letter-spacing: 0.04em; }
.cm-input { width: 100%; box-sizing: border-box; padding: 0.625rem 0.75rem; background-color: hsl(var(--background)); color: hsl(var(--foreground)); border: 1px solid hsl(var(--input)); border-radius: 4px; font-family: var(--font-body, "Inter", system-ui, sans-serif); font-size: 1rem; }
.cm-input:focus { outline: 2px solid hsl(var(--ring)); outline-offset: 1px; border-color: hsl(var(--ring)); }
.cm-form-note { font-size: 0.8125rem; color: hsl(var(--muted-foreground)); }
.cm-alert-ok { border-left: 3px solid hsl(var(--accent)); background-color: hsl(var(--muted)); padding: 1rem 1.25rem; color: hsl(var(--foreground)); }
.cm-alert-err { border-left: 3px solid hsl(var(--destructive)); background-color: hsl(var(--muted)); padding: 1rem 1.25rem; color: hsl(var(--foreground)); }
/* --- Blog family --- */
.cm-post-card { display: flex; flex-direction: column; gap: 0.75rem; }
.cm-post-thumb { display: block; width: 100%; aspect-ratio: 16 / 10; object-fit: cover; background-color: hsl(var(--muted)); border: 1px solid hsl(var(--border)); }
.cm-post-title { font-family: var(--font-heading, "Inter", system-ui, sans-serif); font-weight: 600; font-size: 1.25rem; line-height: 1.25; color: hsl(var(--foreground)); text-decoration: none; }
.cm-post-title:hover { color: hsl(var(--accent)); }
.cm-post-meta { font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-size: 0.75rem; letter-spacing: 0.04em; text-transform: uppercase; color: hsl(var(--muted-foreground)); }
.cm-post-excerpt { color: hsl(var(--muted-foreground)); font-size: 0.9375rem; line-height: 1.6; }
.cm-post-grid { display: grid; gap: 2.5rem; grid-template-columns: repeat(1, minmax(0, 1fr)); }
@media (min-width: 640px) { .cm-post-grid-2, .cm-post-grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .cm-post-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.cm-tag { display: inline-block; font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-size: 0.75rem; letter-spacing: 0.04em; padding: 0.1875rem 0.5rem; border: 1px solid hsl(var(--border)); border-radius: 3px; color: hsl(var(--muted-foreground)); text-decoration: none; }
.cm-tag:hover { border-color: hsl(var(--accent)); color: hsl(var(--accent)); }
.cm-crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; font-size: 0.8125rem; color: hsl(var(--muted-foreground)); }
.cm-crumbs a { color: hsl(var(--muted-foreground)); text-decoration: none; }
.cm-crumbs a:hover { color: hsl(var(--accent)); }
.cm-crumb-sep { color: hsl(var(--border)); }
.cm-sidebar-link { display: block; padding: 0.5rem 0.75rem; border-left: 2px solid transparent; color: hsl(var(--foreground)); text-decoration: none; font-size: 0.9375rem; }
.cm-sidebar-link:hover, .cm-sidebar-link[aria-current="page"] { border-left-color: hsl(var(--accent)); color: hsl(var(--accent)); }
.cm-rank { font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace); font-size: 1.25rem; font-weight: 500; color: hsl(var(--accent)); font-variant-numeric: tabular-nums; width: 2rem; flex: none; }
/* --- Motion (CSS-first, reduced-motion honored) --- */
.cm-reveal { opacity: 0; transform: translateY(0.5rem); animation: cm-reveal 500ms ease forwards; }
@keyframes cm-reveal { to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
.cm-reveal { animation: none; opacity: 1; transform: none; }
.cm-btn-ghost, .cm-logo, .cm-nav-link, .cm-faq-chevron { transition: none; }
}

View File

@ -2,31 +2,23 @@
{ {
"key": "corporate-modernist:default-master", "key": "corporate-modernist:default-master",
"title": "Corporate Modernist Default Master", "title": "Corporate Modernist Default Master",
"page_templates": ["default", "article", "blog-index", "full-width"], "page_templates": ["default", "article"],
"blocks": [ "blocks": [
{ "block_key": "navbar", "title": "Main Navigation", "content": { "variant": "standard", "menuName": "main" }, "slot": "header", "sort_order": 0 }, { "block_key": "navbar", "title": "Main Navigation", "content": { "menuName": "main", "style": "underline" }, "slot": "header", "sort_order": 0 },
{ "block_key": "slot", "title": "Main Content Slot", "content": { "slotName": "main" }, "slot": "main", "sort_order": 0 }, { "block_key": "slot", "title": "Main Content Slot", "content": { "slotName": "main" }, "slot": "main", "sort_order": 0 },
{ "block_key": "footer", "title": "Site Footer", "content": { "layout": "columns", "showSocial": true, "companyName": "Meridian Advisory", "tagline": "Strategy, operations, and risk counsel for boards.", "copyright": "Meridian Advisory" }, "slot": "footer", "sort_order": 0 } { "block_key": "corporate-modernist:footer", "title": "Site Footer", "content": { "variant": "compact", "showLegal": true }, "slot": "footer", "sort_order": 0 }
] ]
}, },
{ {
"key": "corporate-modernist:landing-master", "key": "corporate-modernist:landing-master",
"title": "Corporate Modernist Landing Master", "title": "Corporate Modernist Landing Master",
"page_templates": ["landing"], "page_templates": ["landing", "full-width"],
"blocks": [ "blocks": [
{ "block_key": "navbar", "title": "Main Navigation", "content": { "variant": "standard", "menuName": "main" }, "slot": "header", "sort_order": 0 }, { "block_key": "navbar", "title": "Main Navigation", "content": { "menuName": "main", "style": "underline" }, "slot": "header", "sort_order": 0 },
{ "block_key": "hero", "title": "Landing Hero", "content": { "template": "left-aligned", "eyebrow": "B2B advisory", "headline": "Precise counsel for boards that move first", "subheadline": "Strategy, operations, and risk work delivered on a fixed scope." }, "slot": "hero", "sort_order": 0 }, { "block_key": "corporate-modernist:hero_statement", "title": "Landing Hero", "content": { "eyebrow": "Enterprise", "headline": "Trusted by 200+ boards" }, "slot": "hero", "sort_order": 0 },
{ "block_key": "slot", "title": "Main Content Slot", "content": { "slotName": "main" }, "slot": "main", "sort_order": 0 }, { "block_key": "slot", "title": "Main Content Slot", "content": { "slotName": "main" }, "slot": "main", "sort_order": 0 },
{ "block_key": "cta", "title": "Closing CTA", "content": { "background": "band", "heading": "Book a working session", "text": "Ninety minutes with a principal. No pitch deck.", "primaryLabel": "Request a session", "primaryUrl": "/contact" }, "slot": "cta", "sort_order": 0 }, { "block_key": "corporate-modernist:cta_strip", "title": "Closing CTA", "content": { "variant": "book-call" }, "slot": "cta", "sort_order": 0 },
{ "block_key": "footer", "title": "Site Footer", "content": { "layout": "columns", "showSocial": true, "companyName": "Meridian Advisory", "copyright": "Meridian Advisory" }, "slot": "footer", "sort_order": 0 } { "block_key": "corporate-modernist:footer", "title": "Site Footer", "content": { "variant": "full", "showLegal": true }, "slot": "footer", "sort_order": 0 }
]
},
{
"key": "corporate-modernist:auth-master",
"title": "Corporate Modernist Auth Master",
"page_templates": ["auth"],
"blocks": [
{ "block_key": "slot", "title": "Auth Slot", "content": { "slotName": "main" }, "slot": "main", "sort_order": 0 }
] ]
} }
] ]

View File

@ -2,7 +2,7 @@
name = "corporate-modernist" name = "corporate-modernist"
display_name = "Corporate Modernist" display_name = "Corporate Modernist"
scope = "@themes" scope = "@themes"
version = "0.2.1" version = "0.2.0"
description = "Swiss-grid B2B theme with restrained typography, a single configurable accent, and trust-signal blocks for enterprise sites." description = "Swiss-grid B2B theme with restrained typography, a single configurable accent, and trust-signal blocks for enterprise sites."
kind = "theme" kind = "theme"
categories = ["templates"] categories = ["templates"]

View File

@ -52,7 +52,7 @@
{ {
"id": "forest-quiet", "id": "forest-quiet",
"name": "Forest Quiet", "name": "Forest Quiet",
"description": "Same chassis, deep green accent for sustainability, advisory, and ESG.", "description": "Same chassis, deep green accent for sustainability, advisory, ESG.",
"theme": { "theme": {
"mode": "both", "mode": "both",
"lightColors": { "lightColors": {
@ -102,7 +102,7 @@
{ {
"id": "burgundy-tradition", "id": "burgundy-tradition",
"name": "Burgundy Tradition", "name": "Burgundy Tradition",
"description": "Heritage accent for old-line legal, private banking, and partnerships.", "description": "Heritage accent for old-line legal, private banking, partnerships.",
"theme": { "theme": {
"mode": "both", "mode": "both",
"lightColors": { "lightColors": {
@ -148,105 +148,5 @@
"ring": "350 60% 60%" "ring": "350 60% 60%"
} }
} }
},
{
"id": "graphite-mono",
"name": "Graphite Mono",
"description": "Achromatic ink on gallery white. Pure Swiss restraint with no hue.",
"theme": {
"mode": "both",
"lightColors": {
"background": "0 0% 100%",
"foreground": "0 0% 9%",
"card": "0 0% 100%",
"cardForeground": "0 0% 9%",
"popover": "0 0% 100%",
"popoverForeground": "0 0% 9%",
"primary": "0 0% 13%",
"primaryForeground": "0 0% 100%",
"secondary": "0 0% 96%",
"secondaryForeground": "0 0% 9%",
"muted": "0 0% 96%",
"mutedForeground": "0 0% 40%",
"accent": "0 0% 13%",
"accentForeground": "0 0% 100%",
"destructive": "0 72% 42%",
"destructiveForeground": "0 0% 100%",
"border": "0 0% 88%",
"input": "0 0% 88%",
"ring": "0 0% 13%"
},
"darkColors": {
"background": "0 0% 8%",
"foreground": "0 0% 96%",
"card": "0 0% 11%",
"cardForeground": "0 0% 96%",
"popover": "0 0% 11%",
"popoverForeground": "0 0% 96%",
"primary": "0 0% 92%",
"primaryForeground": "0 0% 8%",
"secondary": "0 0% 17%",
"secondaryForeground": "0 0% 96%",
"muted": "0 0% 15%",
"mutedForeground": "0 0% 64%",
"accent": "0 0% 92%",
"accentForeground": "0 0% 8%",
"destructive": "0 62% 50%",
"destructiveForeground": "0 0% 100%",
"border": "0 0% 22%",
"input": "0 0% 22%",
"ring": "0 0% 92%"
}
}
},
{
"id": "signal-red",
"name": "Signal Red",
"description": "Graphite ground with a single Bauhaus primary-red accent for bold consultancies.",
"theme": {
"mode": "both",
"lightColors": {
"background": "0 0% 100%",
"foreground": "0 0% 11%",
"card": "0 0% 100%",
"cardForeground": "0 0% 11%",
"popover": "0 0% 100%",
"popoverForeground": "0 0% 11%",
"primary": "4 74% 46%",
"primaryForeground": "0 0% 100%",
"secondary": "0 0% 96%",
"secondaryForeground": "0 0% 11%",
"muted": "0 0% 96%",
"mutedForeground": "0 0% 40%",
"accent": "4 74% 46%",
"accentForeground": "0 0% 100%",
"destructive": "0 72% 42%",
"destructiveForeground": "0 0% 100%",
"border": "0 0% 88%",
"input": "0 0% 88%",
"ring": "4 74% 46%"
},
"darkColors": {
"background": "0 0% 8%",
"foreground": "0 0% 96%",
"card": "0 0% 11%",
"cardForeground": "0 0% 96%",
"popover": "0 0% 11%",
"popoverForeground": "0 0% 96%",
"primary": "4 78% 58%",
"primaryForeground": "0 0% 8%",
"secondary": "0 0% 17%",
"secondaryForeground": "0 0% 96%",
"muted": "0 0% 15%",
"mutedForeground": "0 0% 64%",
"accent": "4 78% 58%",
"accentForeground": "0 0% 8%",
"destructive": "0 62% 50%",
"destructiveForeground": "0 0% 100%",
"border": "0 0% 22%",
"input": "0 0% 22%",
"ring": "4 78% 58%"
}
}
} }
] ]

View File

@ -1,122 +0,0 @@
{
"demo": true,
"settings": {
"merge": {
"site_name": "Meridian Advisory",
"tagline": "Strategy, operations, and risk counsel for boards."
}
},
"data_tables": [
{
"key": "contact_submissions",
"name": "Contact submissions",
"description": "Messages sent from the Meridian Advisory contact form.",
"primary_key": "id",
"columns": [
{ "key": "name", "label": "Name", "type": "text", "required": true },
{ "key": "email", "label": "Email", "type": "email", "required": true },
{ "key": "organisation", "label": "Organisation", "type": "text", "required": false },
{ "key": "message", "label": "Message", "type": "text", "required": true }
]
}
],
"pages": [
{
"slug": "/",
"title": "Meridian Advisory",
"template_key": "full-width",
"reconcile_blocks": true,
"blocks": [
{ "block_key": "hero", "title": "Hero", "slot": "main", "sort_order": 1, "content": { "template": "left-aligned", "eyebrow": { "text": "B2B advisory" }, "headline": { "text": "Precise counsel for boards that move first" }, "subheadline": { "text": "We do strategy, operations, and risk work on a fixed scope. No retainers you cannot read." }, "ctaPrimary": { "text": "Book a working session", "url": "/contact" }, "ctaSecondary": { "text": "See how we work", "url": "/about" } } },
{ "block_key": "corporate-modernist:credential_bar", "title": "Credentials", "slot": "main", "sort_order": 2, "content": { "items": [ { "figure": "18", "label": "Years in practice" }, { "figure": "6", "label": "Sectors served" }, { "figure": "40+", "label": "Board engagements" }, { "figure": "3", "label": "Continents" } ] } },
{ "block_key": "corporate-modernist:capability_matrix", "title": "Capabilities", "slot": "main", "sort_order": 3, "content": { "eyebrow": "What we do", "heading": "Three disciplines, one team", "intro": "Every engagement draws on the same three capabilities. We assign a principal to each.", "columns": 3, "capabilities": [ { "mark": "circle", "title": "Strategy", "text": "Market entry, portfolio choices, and the operating model that carries them." }, { "mark": "triangle", "title": "Operations", "text": "Cost structure, process design, and the metrics a board can hold." }, { "mark": "square", "title": "Risk", "text": "Controls, resilience, and the reporting that keeps regulators satisfied." } ] } },
{ "block_key": "stats", "title": "Outcomes", "slot": "main", "sort_order": 4, "content": { "heading": "Outcomes we measure", "columns": 3, "countUp": false, "items": [ { "value": "3.2", "suffix": "x", "label": "Median return on engagement" }, { "value": "90", "suffix": " days", "label": "Time to first decision" }, { "value": "12", "label": "Week average engagement" } ] } },
{ "block_key": "corporate-modernist:engagement_process", "title": "Process", "slot": "main", "sort_order": 5, "content": { "eyebrow": "How we work", "heading": "A fixed path from brief to handover", "steps": [ { "title": "Frame", "text": "We agree the question and the scope in one working session." }, { "title": "Diagnose", "text": "We gather evidence and test it against the board's own numbers." }, { "title": "Decide", "text": "We present options with a clear recommendation and the trade-offs." }, { "title": "Hand over", "text": "We leave the model, the plan, and the owners in place." } ] } },
{ "block_key": "cta", "title": "Closing CTA", "slot": "main", "sort_order": 6, "content": { "background": "band", "heading": "Book a working session", "text": "Ninety minutes with a principal. No pitch deck.", "primaryLabel": "Request a session", "primaryUrl": "/contact" } }
]
},
{
"slug": "/about",
"title": "About",
"template_key": "default",
"reconcile_blocks": true,
"blocks": [
{ "block_key": "heading", "title": "Heading", "slot": "main", "sort_order": 1, "content": { "text": "A small firm by design", "level": 1 } },
{ "block_key": "text", "title": "Intro", "slot": "main", "sort_order": 2, "content": { "text": "<p>Meridian Advisory keeps a short bench. Every engagement is led by a principal, not passed to a junior team. We take fewer clients so we can go deeper on each one.</p>" } },
{ "block_key": "corporate-modernist:capability_matrix", "title": "Values", "slot": "main", "sort_order": 3, "content": { "eyebrow": "How we operate", "heading": "What holds across every brief", "columns": 3, "capabilities": [ { "mark": "square", "title": "Fixed scope", "text": "You know the price and the deadline before we start." }, { "mark": "circle", "title": "Evidence first", "text": "We argue from your data, not from a template deck." }, { "mark": "triangle", "title": "Clean handover", "text": "We finish by making ourselves unnecessary." } ] } },
{ "block_key": "team", "title": "Team", "slot": "main", "sort_order": 4, "content": { "heading": "The principals", "layout": "grid", "columns": 3, "members": [ { "name": "A. Reyes", "role": "Strategy", "bio": "Two decades in corporate strategy and market entry." }, { "name": "K. Osei", "role": "Operations", "bio": "Process and cost work across manufacturing and services." }, { "name": "M. Lindqvist", "role": "Risk", "bio": "Controls and resilience for regulated industries." } ] } },
{ "block_key": "corporate-modernist:case_study_card", "title": "Case study", "slot": "main", "sort_order": 5, "content": { "client": "Regional bank", "metric": "31%", "label": "Reduction in cost to serve", "summary": "A twelve-week operations review that reset the branch operating model.", "href": { "label": "Read the case", "href": "/blog/operations-reset" } } }
]
},
{
"slug": "/blog",
"title": "Insights",
"template_key": "blog-index",
"reconcile_blocks": true,
"blocks": [
{ "block_key": "card", "title": "Post 1", "slot": "main", "sort_order": 1, "content": { "title": "Resetting a branch operating model", "text": "<p>What changed when a regional bank moved from headcount targets to cost-to-serve.</p>", "link": "/blog/operations-reset", "linkLabel": "Read" } },
{ "block_key": "card", "title": "Post 2", "slot": "main", "sort_order": 2, "content": { "title": "Reading a risk register the board will use", "text": "<p>Most registers are written for auditors. Here is how to write one for directors.</p>", "link": "/blog/risk-register", "linkLabel": "Read" } },
{ "block_key": "card", "title": "Post 3", "slot": "main", "sort_order": 3, "content": { "title": "The ninety-day decision", "text": "<p>Why the first real decision should land inside three months, not six.</p>", "link": "/blog/ninety-day-decision", "linkLabel": "Read" } }
]
},
{
"slug": "/blog/operations-reset",
"parent_slug": "/blog",
"title": "Resetting a branch operating model",
"template_key": "article",
"reconcile_blocks": true,
"blocks": [
{ "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "Resetting a branch operating model", "level": 1 } },
{ "block_key": "text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "text": "<p>A regional bank asked us to cut cost to serve without closing branches. We started with the numbers the board already trusted. Then we tested each assumption against a week of real transactions.</p><p>The answer was not fewer branches. It was fewer tasks per branch. We moved back-office work to a shared team and gave each branch a single metric it could hold.</p>" } }
]
},
{
"slug": "/blog/risk-register",
"parent_slug": "/blog",
"title": "Reading a risk register the board will use",
"template_key": "article",
"reconcile_blocks": true,
"blocks": [
{ "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "Reading a risk register the board will use", "level": 1 } },
{ "block_key": "text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "text": "<p>A risk register written for auditors lists everything. A register written for directors ranks a few things and says who owns them. The second kind gets read.</p><p>We keep the top ten risks on one page. Each has an owner, a trend, and a next action. Everything else lives in the appendix.</p>" } }
]
},
{
"slug": "/blog/ninety-day-decision",
"parent_slug": "/blog",
"title": "The ninety-day decision",
"template_key": "article",
"reconcile_blocks": true,
"blocks": [
{ "block_key": "heading", "title": "Title", "slot": "main", "sort_order": 1, "content": { "text": "The ninety-day decision", "level": 1 } },
{ "block_key": "text", "title": "Body", "slot": "main", "sort_order": 2, "content": { "text": "<p>Long engagements drift because no decision anchors them. We set a real decision inside the first ninety days. It forces the evidence to be good enough to act on.</p><p>The decision does not have to be final. It has to be real. A board that decides once will decide again faster.</p>" } }
]
},
{
"slug": "/contact",
"title": "Contact",
"template_key": "contact",
"reconcile_blocks": true,
"blocks": [
{ "block_key": "contact-form", "title": "Contact form", "slot": "main", "sort_order": 1, "content": { "heading": "Request a working session", "description": "Tell us the question. We reply within one business day.", "submitLabel": "Send message", "successMessage": "Thank you. We will be in touch within one business day.", "formConfig": { "targetTable": "contact_submissions", "enabled": true, "captchaEnabled": false }, "fields": [ { "name": "name", "label": "Name", "type": "text", "required": true, "width": "half" }, { "name": "email", "label": "Email", "type": "email", "required": true, "width": "half" }, { "name": "organisation", "label": "Organisation", "type": "text", "required": false, "width": "full" }, { "name": "message", "label": "What can we help with?", "type": "textarea", "required": true, "width": "full" } ] } },
{ "block_key": "contact-card", "title": "Contact details", "slot": "aside", "sort_order": 1, "content": { "heading": "Direct", "columns": 1, "channels": [ { "icon": "lucide:mail", "label": "Email", "value": "hello@meridian.example", "link": "mailto:hello@meridian.example" }, { "icon": "lucide:phone", "label": "Phone", "value": "+61 2 5550 0100", "link": "tel:+61255500100" } ] } },
{ "block_key": "hours-location", "title": "Office hours", "slot": "aside", "sort_order": 2, "content": { "heading": "Office", "address": "Level 12, 100 Harbour Street, Sydney NSW 2000", "hours": [ { "day": "Monday to Friday", "hours": "9:00 to 17:30" }, { "day": "Saturday and Sunday", "closed": true } ] } }
]
},
{
"slug": "/login",
"title": "Sign in",
"template_key": "auth",
"reconcile_blocks": true,
"blocks": [
{ "block_key": "auth-form", "title": "Sign in", "slot": "main", "sort_order": 1, "content": { "default_tab": "login", "show_social": false } }
]
}
],
"menu_items": [
{ "menu": "main", "label": "Home", "page_slug": "/", "sort_order": 1 },
{ "menu": "main", "label": "About", "page_slug": "/about", "sort_order": 2 },
{ "menu": "main", "label": "Insights", "page_slug": "/blog", "sort_order": 3 },
{ "menu": "main", "label": "Contact", "page_slug": "/contact", "sort_order": 4 }
]
}

View File

@ -1,19 +0,0 @@
{
"workflows": [
{
"key": "contact_notify_admins",
"name": "Notify admins on contact submission",
"description": "Emails site admins whenever the Meridian Advisory contact form receives a message.",
"enabled": true,
"trigger": { "type": "row_inserted", "table": "contact_submissions" },
"steps": [
{
"type": "email",
"name": "Email admins",
"template": "Form Submission Notification",
"recipients": { "mode": "admins", "scope": "all" }
}
]
}
]
}

View File

@ -1,17 +0,0 @@
<!doctype html>
<html lang="en">
{{ head_html|safe }}
<body class="cm-body" style="margin: 0; min-height: 100vh; display: flex; flex-direction: column; background-color: hsl(var(--muted)); color: hsl(var(--foreground));">
{{ admin_banner_html|safe }}
<main style="flex: 1 1 auto; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 3rem 1.5rem;">
<div style="width: 100%; max-width: 26rem;">
<a href="/" aria-label="{{ site_settings.Title|default:"Home" }}" style="display: inline-flex; align-items: center; gap: 0.625rem; text-decoration: none; margin-bottom: 1.5rem;"><span class="cm-mark cm-mark-square" aria-hidden="true"></span><span class="cm-brand">{{ site_settings.Title|default:"Corporate Modernist" }}</span></a>
<div class="cm-card cm-card-accent cm-reveal" style="padding: 2rem;">
{% if slots.main %}{{ slots.main|safe }}{% else %}<p class="cm-stat-label">No form assigned to this page.</p>{% endif %}
</div>
{% if slots.footer %}<div class="cm-stat-label" style="text-align: center; margin-top: 1.5rem; text-transform: none; letter-spacing: 0;">{{ slots.footer|safe }}</div>{% endif %}
</div>
</main>
{{ body_end_html|safe }}
</body>
</html>

View File

@ -1,17 +0,0 @@
<!doctype html>
<html lang="en">
{{ head_html|safe }}
<body class="cm-body" style="margin: 0; min-height: 100vh; display: flex; flex-direction: column; background-color: hsl(var(--background)); color: hsl(var(--foreground));">
{{ admin_banner_html|safe }}
{% if slots.header %}<header class="cm-hairline-bottom" style="width: 100%;"><div class="cm-content-well">{{ slots.header|safe }}</div></header>{% endif %}
<main style="flex: 1 1 auto; width: 100%;" data-content-well>
<div class="cm-content-well cm-section">
{% if title %}<div class="cm-accent-rule" style="padding-top: 1.5rem; margin-bottom: 3rem;"><p class="cm-eyebrow">Insights</p><h1 class="cm-heading" style="font-size: clamp(2rem, 4vw, 3rem); line-height: 1.1; margin: 0;">{{ title }}</h1>{% if page_meta.MetaDescription %}<p class="cm-lede" style="max-width: 60ch; margin: 1rem 0 0;">{{ page_meta.MetaDescription }}</p>{% endif %}</div>{% endif %}
{% if slots.featured %}<section style="margin-bottom: 3rem;">{{ slots.featured|safe }}</section>{% endif %}
{% if slots.main %}<section>{{ slots.main|safe }}</section>{% else %}<p class="cm-stat-label">No posts published yet.</p>{% endif %}
</div>
</main>
{% if slots.footer %}<footer style="width: 100%; margin-top: auto;">{{ slots.footer|safe }}</footer>{% endif %}
{{ body_end_html|safe }}
</body>
</html>

View File

@ -1,23 +0,0 @@
<!doctype html>
<html lang="en">
{{ head_html|safe }}
<body class="cm-body" style="margin: 0; min-height: 100vh; display: flex; flex-direction: column; background-color: hsl(var(--background)); color: hsl(var(--foreground));">
{{ admin_banner_html|safe }}
{% if slots.header %}<header class="cm-hairline-bottom" style="width: 100%;"><div class="cm-content-well">{{ slots.header|safe }}</div></header>{% endif %}
<main style="flex: 1 1 auto; width: 100%;" data-content-well>
<div class="cm-content-well cm-section">
{% if slots.aside %}
<div class="cm-swiss-12">
<div style="grid-column: span 12 / span 12;" class="cm-contact-main">{{ slots.main|safe }}</div>
<aside style="grid-column: span 12 / span 12;" class="cm-hairline-top cm-contact-aside">{{ slots.aside|safe }}</aside>
</div>
<style>@media (min-width: 1024px){.cm-contact-main{grid-column: span 7 / span 7 !important;} .cm-contact-aside{grid-column: 9 / span 4 !important; border-top: 0 !important; padding-left: 2rem; border-left: 1px solid hsl(var(--border));}}</style>
{% else %}
<div class="cm-swiss-12"><div style="grid-column: span 12 / span 12;" class="cm-measure">{% if slots.main %}{{ slots.main|safe }}{% else %}<p class="cm-stat-label">No content blocks assigned to this page.</p>{% endif %}</div></div>
{% endif %}
</div>
</main>
{% if slots.footer %}<footer style="width: 100%; margin-top: auto;">{{ slots.footer|safe }}</footer>{% endif %}
{{ body_end_html|safe }}
</body>
</html>

View File

@ -1,8 +0,0 @@
{# announcement-bar override — centered notice; carries data-bn-announcement / data-bn-annkey and a data-bn-ann-dismiss close button so the host dismissal JS binds. #}
{% if variant == "promo" %}{% set annvar = "cm-announce--promo" %}{% set annsty = "background:hsl(var(--primary));color:hsl(var(--primary-foreground));" %}{% elif variant == "alert" %}{% set annvar = "cm-announce--alert" %}{% set annsty = "background:hsl(var(--destructive));color:hsl(var(--destructive-foreground));" %}{% else %}{% set annvar = "cm-announce--info" %}{% set annsty = "background:hsl(var(--accent));color:hsl(var(--accent-foreground));" %}{% endif %}
<div data-bn-announcement data-bn-annkey="{{ variant|default:'info' }}:{{ message|slugify }}" class="cm-announce {{ annvar }} {{ class }}" style="{{ annsty }}">
<div class="cm-content-well cm-announce-row" style="position:relative;display:flex;align-items:center;justify-content:center;gap:0.75rem;padding-top:0.5rem;padding-bottom:0.5rem;font-size:0.875rem;text-align:center;">
<p style="margin:0;font-weight:500;">{{ message }}{% if linkText and linkUrl %} <a href="{{ linkUrl }}" style="color:inherit;text-decoration:underline;text-underline-offset:2px;">{{ linkText }}</a>{% endif %}</p>
{% if dismissable %}<button type="button" data-bn-ann-dismiss aria-label="Dismiss announcement" class="cm-announce-close" style="position:absolute;right:0.75rem;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;padding:0.25rem;background:none;border:0;color:inherit;cursor:pointer;">::lucide:x:sm::</button>{% endif %}
</div>
</div>

View File

@ -1,14 +0,0 @@
{# audio-embed override — hairline card row; carries data-bn-audio / data-audio-url / data-audio-media + data-bn-audio-slot so the host facade JS binds. #}
<figure class="cm-card {{ class }}" data-bn-audio data-audio-url="{{ url|default:'' }}" data-audio-media="{% if media %}{{ media|mediaURL }}{% endif %}" style="margin:2rem 0;padding:0;overflow:hidden;">
<div style="display:flex;align-items:center;gap:1rem;padding:1rem;">
{% if artwork %}<div class="cm-media-frame" style="width:4rem;height:4rem;flex:0 0 auto;overflow:hidden;border:1px solid hsl(var(--border));border-radius:4px;">{% img artwork alt=title|default:"" class="cm-audio-art" %}</div>{% endif %}
<div style="min-width:0;flex:1;">
{% if title %}<p class="cm-display" style="margin:0;font-size:0.9375rem;color:hsl(var(--foreground));white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">{{ title }}</p>{% endif %}
{% if artist %}<p style="margin:0;font-size:0.8125rem;color:hsl(var(--muted-foreground));">{{ artist }}</p>{% endif %}
<div data-bn-audio-slot style="display:flex;align-items:center;gap:0.75rem;margin-top:0.5rem;">
<button type="button" data-bn-audio-play class="cm-btn-primary cm-btn-sm" aria-label="Play{% if title %}: {{ title }}{% endif %}" style="display:inline-flex;align-items:center;gap:0.375rem;">::lucide:play:sm:: Play</button>
{% if download %}<a href="{% if media %}{{ media|mediaURL }}{% else %}{{ url }}{% endif %}" download class="cm-btn-ghost" style="display:inline-flex;align-items:center;gap:0.25rem;font-size:0.8125rem;color:hsl(var(--muted-foreground));text-decoration:none;">::lucide:download:sm:: Download</a>{% endif %}
</div>
</div>
</div>
</figure><style>.cm-audio-art{display:block;width:100%;height:100%;object-fit:cover;}</style>

View File

@ -1,22 +0,0 @@
<div class="cm-stack {{ class }}" data-block="corporate-modernist:auth-form">
<input type="radio" name="cm-auth-tab" id="cm-auth-login" class="cm-auth-radio" style="display:none;"{% if default_tab != "register" %} checked{% endif %}>
<input type="radio" name="cm-auth-tab" id="cm-auth-register" class="cm-auth-radio" style="display:none;"{% if default_tab == "register" %} checked{% endif %}>
<div class="cm-auth-tabs" style="display:flex; gap:1.5rem; border-bottom:1px solid hsl(var(--border));">
<label for="cm-auth-login" class="cm-auth-tab cm-label" style="cursor:pointer; padding:0.75rem 0;">Sign in</label>
<label for="cm-auth-register" class="cm-auth-tab cm-label" style="cursor:pointer; padding:0.75rem 0;">Create account</label>
</div>
<div id="auth-message" role="status" aria-live="polite"></div>
<form class="cm-auth-panel cm-auth-panel-login cm-form" hx-post="/api/auth/login" hx-target="#auth-message" hx-swap="innerHTML">
<div class="cm-field cm-field--full"><label class="cm-label" for="cm-login-email">Email</label><input class="cm-input" type="email" name="email" id="cm-login-email" required></div>
<div class="cm-field cm-field--full"><label class="cm-label" for="cm-login-password">Password</label><input class="cm-input" type="password" name="password" id="cm-login-password" required></div>
<button type="submit" class="cm-btn-primary" hx-disabled-elt="this" style="width:100%; justify-content:center;">Sign in</button>
<p class="cm-form-note" style="text-align:center;"><a href="/forgot-password" style="color: hsl(var(--accent)); text-decoration: underline; text-underline-offset: 2px;">Forgot password?</a></p>
</form>
<form class="cm-auth-panel cm-auth-panel-register cm-form" hx-post="/api/auth/register" hx-target="#auth-message" hx-swap="innerHTML">
<div class="cm-field cm-field--full"><label class="cm-label" for="cm-reg-username">Username</label><input class="cm-input" type="text" name="username" id="cm-reg-username" required pattern="[a-zA-Z0-9_-]{3,30}"></div>
<div class="cm-field cm-field--full"><label class="cm-label" for="cm-reg-email">Email</label><input class="cm-input" type="email" name="email" id="cm-reg-email" required></div>
<div class="cm-field cm-field--full"><label class="cm-label" for="cm-reg-password">Password</label><input class="cm-input" type="password" name="password" id="cm-reg-password" required minlength="8"></div>
<button type="submit" class="cm-btn-primary" hx-disabled-elt="this" style="width:100%; justify-content:center;">Create account</button>
</form>
<style>.cm-auth-panel{display:none;} #cm-auth-login:checked ~ .cm-auth-panel-login{display:grid;} #cm-auth-register:checked ~ .cm-auth-panel-register{display:grid;}</style>
</div>

View File

@ -1 +0,0 @@
{% if context.isPublicLoggedIn %}<div class="cm-btn-row {{ class }}" data-block="corporate-modernist:auth-status" style="align-items: center;"><span class="cm-mono" style="color: hsl(var(--muted-foreground));">{{ context.publicDisplayName|default:context.publicUsername }}</span><a class="cm-btn-outline cm-btn-sm" href="/account">Account</a><a class="cm-btn-ghost cm-btn-sm" href="/api/auth/logout">Sign out</a></div>{% else %}<div class="cm-btn-row {{ class }}" data-block="corporate-modernist:auth-status" style="align-items: center;"><a class="cm-btn-outline cm-btn-sm" href="/login">Sign in</a><a class="cm-btn-primary cm-btn-sm" href="/login?tab=register">Create account</a></div>{% endif %}

View File

@ -1 +0,0 @@
{% if context.currentAuthor %}<section class="cm-section {{ class }}" data-block="corporate-modernist:author-bio-hero"><div class="cm-content-well"><div class="cm-measure cm-stack" style="text-align: center; margin-left: auto; margin-right: auto;">{% if showAvatar and context.currentAuthor.avatarUrl %}<img class="cm-avatar" src="{{ context.currentAuthor.avatarUrl }}" alt="{{ context.currentAuthor.displayName }}" style="width: 6rem; height: 6rem; border-radius: 9999px; object-fit: cover; margin-left: auto; margin-right: auto;">{% else %}<span class="cm-mark cm-mark-ring" aria-hidden="true" style="margin-left: auto; margin-right: auto;"></span>{% endif %}<h1 class="cm-display" style="margin: 0;">{{ context.currentAuthor.displayName }}</h1>{% if context.currentAuthor.bio %}<p class="cm-lede" style="margin: 0;">{{ context.currentAuthor.bio }}</p>{% endif %}{% if showSocial %}<div class="cm-btn-row" style="justify-content: center;">{% if context.currentAuthor.websiteUrl %}<a class="cm-btn-ghost cm-btn-sm" href="{{ context.currentAuthor.websiteUrl }}" target="_blank" rel="noopener noreferrer" title="Website"><svg class="cm-icon" aria-hidden="true"><use href="/icons/lucide.svg#globe"/></svg></a>{% endif %}{% if context.currentAuthor.twitterHandle %}<a class="cm-btn-ghost cm-btn-sm" href="https://twitter.com/{{ context.currentAuthor.twitterHandle|cut:"@" }}" target="_blank" rel="noopener noreferrer" title="X"><svg class="cm-icon" aria-hidden="true"><use href="/icons/simple-icons.svg#x"/></svg></a>{% endif %}{% if context.currentAuthor.linkedinUrl %}<a class="cm-btn-ghost cm-btn-sm" href="{{ context.currentAuthor.linkedinUrl }}" target="_blank" rel="noopener noreferrer" title="LinkedIn"><svg class="cm-icon" aria-hidden="true"><use href="/icons/simple-icons.svg#linkedin"/></svg></a>{% endif %}</div>{% endif %}</div></div></section>{% endif %}

View File

@ -1 +0,0 @@
{% if context.currentPost.authorName %}<aside class="cm-card cm-card-accent {{ class }}" data-block="corporate-modernist:author-bio">{% if heading %}<p class="cm-kicker" style="margin-top: 0;">{{ heading }}</p>{% endif %}<div style="display: flex; align-items: flex-start; gap: 1rem;">{% if showAvatar and context.currentPost.authorAvatarUrl %}<img class="cm-avatar" src="{{ context.currentPost.authorAvatarUrl }}" alt="{{ context.currentPost.authorName }}" style="width: 3.5rem; height: 3.5rem; border-radius: 9999px; object-fit: cover; flex-shrink: 0;">{% endif %}<div style="min-width: 0;"><h3 class="cm-heading" style="font-size: 1.125rem; margin: 0;">{% if context.currentPost.authorSlug %}<a href="/author/{{ context.currentPost.authorSlug }}">{{ context.currentPost.authorName }}</a>{% else %}{{ context.currentPost.authorName }}{% endif %}</h3>{% if context.currentAuthor.bio %}<p class="cm-text" style="margin: 0.5rem 0 0 0; font-size: 0.9375rem; color: hsl(var(--muted-foreground));">{{ context.currentAuthor.bio }}</p>{% endif %}</div></div></aside>{% endif %}

View File

@ -1 +0,0 @@
<section class="cm-section {{ class }}" data-block="corporate-modernist:blog-hero"><div class="cm-content-well"><div class="cm-stack">{% if title %}<h1 class="cm-heading" style="font-size: clamp(2rem, 4vw, 3rem); margin: 0;"><span class="cm-heading-h2-accent">{{ title }}</span></h1>{% endif %}{% if subtitle %}<p class="cm-lede" style="max-width: 60ch; margin: 0.75rem 0 0 0;">{{ subtitle }}</p>{% endif %}</div>{% if posts %}<div class="cm-post-grid cm-post-grid-3" style="margin-top: 2.5rem;">{% for post in posts %}<article class="cm-post-card{% if forloop.First %} cm-card-accent{% endif %}">{% if post.featured_image_url %}<a href="{{ post.url }}" class="cm-post-thumb cm-media-frame" style="display: block; aspect-ratio: 16 / 9;"><img src="{{ post.featured_image_url }}" alt="{{ post.title }}" style="width: 100%; height: 100%; object-fit: cover;"></a>{% endif %}<h2 class="cm-post-title"{% if forloop.First %} style="font-size: 1.5rem;"{% endif %}><a href="{{ post.url }}">{{ post.title }}</a></h2>{% if showExcerpt and post.excerpt %}<p class="cm-post-excerpt">{{ post.excerpt }}</p>{% endif %}<div class="cm-post-meta">{% if post.author_name %}<span>{{ post.author_name }}</span>{% endif %}{% if post.published_at %}<span class="cm-mono numeric-tabular">{{ post.published_at|date:"M j, Y" }}</span>{% endif %}</div></article>{% endfor %}</div>{% endif %}</div></section>

View File

@ -1 +0,0 @@
<nav aria-label="Breadcrumb" class="cm-crumbs {{ class }}" data-block="corporate-modernist:breadcrumbs"><ol style="display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem; list-style: none; margin: 0; padding: 0;">{% if showHome %}<li style="display: flex; align-items: center;"><a class="cm-sidebar-link" href="/" style="padding: 0;">{{ homeLabel|default:"Home" }}</a></li>{% endif %}{% for item in breadcrumbs %}<li style="display: flex; align-items: center;">{% if showHome or not forloop.First %}<span class="cm-crumb-sep" aria-hidden="true">{{ separator|default:"/" }}</span>{% endif %}{% if item.is_current %}{% if showCurrent %}<span aria-current="page" style="color: hsl(var(--foreground)); font-weight: 500;">{{ item.label }}</span>{% endif %}{% else %}<a class="cm-sidebar-link" href="{{ item.url }}" style="padding: 0;">{{ item.label }}</a>{% endif %}</li>{% endfor %}</ol></nav>

View File

@ -1,6 +1 @@
{% set btnStyle = style|default:"primary" %} {% if variant|default:"primary" == "outline" or variant|default:"primary" == "secondary" %}<a class="cm-btn-outline" href="{{ href|default:url|default:"#" }}">{{ label|default:text|default:"Learn more" }}</a>{% else %}<a class="cm-btn-primary" href="{{ href|default:url|default:"#" }}">{{ label|default:text|default:"Learn more" }}</a>{% endif %}
{% set btnClass = "cm-btn-primary" %}
{% if btnStyle == "outline" or btnStyle == "secondary" %}{% set btnClass = "cm-btn-outline" %}{% elif btnStyle == "ghost" or btnStyle == "link" %}{% set btnClass = "cm-btn-ghost" %}{% endif %}
{% set sizeClass = "" %}
{% if size == "sm" %}{% set sizeClass = "cm-btn-sm" %}{% elif size == "lg" %}{% set sizeClass = "cm-btn-lg" %}{% endif %}
<div class="cm-btn-row {{ class }}" style="{% if align == "center" %}justify-content: center;{% elif align == "right" %}justify-content: flex-end;{% endif %}">{% if newTab %}<a class="{{ btnClass }} {{ sizeClass }}" href="{{ link|default:"#" }}" target="_blank" rel="noopener noreferrer">{% if icon and iconPosition != "right" %}::{{ icon }}:sm::{% endif %}<span>{{ label|default:"Learn more" }}</span>{% if icon and iconPosition == "right" %}::{{ icon }}:sm::{% endif %}</a>{% else %}<a class="{{ btnClass }} {{ sizeClass }}" href="{{ link|default:"#" }}">{% if icon and iconPosition != "right" %}::{{ icon }}:sm::{% endif %}<span>{{ label|default:"Learn more" }}</span>{% if icon and iconPosition == "right" %}::{{ icon }}:sm::{% endif %}</a>{% endif %}</div>

View File

@ -1 +1 @@
<article class="cm-card cm-card-accent {{ class }}">{% if media %}<div class="cm-media-frame" style="aspect-ratio: 16 / 9; margin: -1.5rem -1.5rem 1.25rem -1.5rem; border: 0; border-bottom: 1px solid hsl(var(--border));">{% img media alt=title|default:"" class="cm-card-media" %}</div>{% endif %}{% if title %}<h3 class="cm-feature-title" style="margin-top: 0; font-size: 1.25rem;">{{ title }}</h3>{% endif %}{% if text %}<div class="cm-text" style="font-size: 0.9375rem; line-height: 1.6; color: hsl(var(--muted-foreground));">{{ text|safe }}</div>{% endif %}{% if linkLabel and link %}<a class="cm-btn-ghost" href="{{ link }}" style="margin-top: 1rem;">{{ linkLabel }}::lucide:arrow-right:sm::</a>{% endif %}</article> <div class="cm-card{% if accent == true or accent == "true" %} cm-card-accent{% endif %}">{% if title %}<div class="cm-display" style="font-size: 1.25rem; font-weight: 600; margin: 0 0 0.75rem 0;">{{ title }}</div>{% endif %}{% if body|default:text %}<div class="cm-text">{{ body|default:text|safe }}</div>{% endif %}</div>

View File

@ -1 +0,0 @@
<section class="cm-section-tight {{ class }}" data-block="corporate-modernist:category-list">{% if heading %}<h2 class="cm-kicker">{{ heading }}</h2>{% endif %}{% if style == "pills" %}<div style="display: flex; flex-wrap: wrap; gap: 0.5rem;">{% for cat in categories %}<a href="{{ cat.url }}" class="cm-tag">{{ cat.name }}{% if showCount %} <span class="cm-mono numeric-tabular">{{ cat.post_count }}</span>{% endif %}</a>{% endfor %}</div>{% else %}<ul class="cm-stack-sm" style="list-style: none; margin: 0; padding: 0;">{% for cat in categories %}<li><a href="{{ cat.url }}" class="cm-sidebar-link" style="display: flex; align-items: center; justify-content: space-between;"><span>{{ cat.name }}</span>{% if showCount %}<span class="cm-mono numeric-tabular">{{ cat.post_count }}</span>{% endif %}</a></li>{% endfor %}</ul>{% endif %}</section>

View File

@ -1,14 +0,0 @@
{# contact-card override — grouped contact channels; each channel a hairline mark + label + value. #}
<section class="cm-section {{ class }}"><div class="cm-content-well">
{% if heading %}<h2 class="cm-heading" style="font-size:clamp(1.75rem, 3vw, 2.25rem);margin:0 0 0.75rem 0;"><span class="cm-heading-h2-accent">{{ heading }}</span></h2>{% endif %}
{% if description %}<p class="cm-lede" style="max-width:60ch;margin:0 0 2.5rem 0;color:hsl(var(--muted-foreground));">{{ description }}</p>{% endif %}
<div class="cm-grid {% if columns == 3 %}cm-grid-3{% elif columns == 1 %}{% else %}cm-grid-2{% endif %}" style="display:grid;gap:1rem;{% if columns == 3 %}grid-template-columns:repeat(auto-fill, minmax(240px, 1fr));{% elif columns == 1 %}grid-template-columns:1fr;{% else %}grid-template-columns:repeat(auto-fill, minmax(280px, 1fr));{% endif %}">
{% for channel in channels %}<div class="cm-channel cm-card" style="display:flex;align-items:flex-start;gap:1rem;">
{% if channel.icon %}<span class="cm-channel-mark" style="display:inline-flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;flex:0 0 auto;border:1px solid hsl(var(--border));border-radius:4px;color:hsl(var(--accent));">::{{ channel.icon }}:sm::</span>{% endif %}
<div style="min-width:0;">
{% if channel.label %}<div class="cm-stat-label" style="margin:0;">{{ channel.label }}</div>{% endif %}
{% if channel.value %}<div style="margin-top:0.25rem;font-weight:500;word-break:break-word;color:hsl(var(--foreground));">{% if channel.link %}<a href="{{ channel.link }}" style="color:inherit;text-decoration:none;">{{ channel.value }}</a>{% else %}{{ channel.value }}{% endif %}</div>{% endif %}
</div>
</div>{% endfor %}
</div>
</div></section>

View File

@ -1,34 +0,0 @@
{# contact-form override — server-driven; block-id resolution, {% form %} wiring, honeypot, and field loop copied verbatim from the builtin, reskinned with cm-form primitives. #}
{% with bid=_block_id|default:context.blockId %}
<section id="contact-form-{{ bid }}" class="cm-section {{ class }}"><div class="cm-content-well" style="max-width:640px;">
{% if form.submitted %}
<div class="cm-card cm-card-accent cm-alert-ok" style="text-align:center;padding:2rem;">
<span class="cm-mark cm-mark-circle" aria-hidden="true" style="display:inline-block;margin-bottom:1rem;"></span>
<p class="cm-display" style="margin:0;font-size:1.125rem;">{{ form.message|default:successMessage|default:"Thanks — your message has been sent." }}</p>
</div>
{% else %}
{% if heading %}<h2 class="cm-heading" style="font-size:clamp(1.75rem, 3vw, 2.25rem);margin:0 0 0.75rem 0;"><span class="cm-heading-h2-accent">{{ heading }}</span></h2>{% endif %}
{% if description %}<p class="cm-lede" style="margin:0 0 2rem 0;color:hsl(var(--muted-foreground));">{{ description }}</p>{% endif %}
{% if form.errors._form %}<div class="cm-alert-err" style="margin-bottom:1.5rem;border:1px solid hsl(var(--destructive));border-radius:4px;padding:0.75rem 1rem;font-size:0.875rem;color:hsl(var(--destructive));background:hsl(var(--destructive) / 0.08);">{{ 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="cm-form-grid" %}
{% for field in fields %}
<div class="cm-field {% if field.width == "half" %}{% else %}cm-field--full{% endif %}">
{% if field.label %}<label class="cm-label" for="cf-{{ bid }}-{{ field.name }}">{{ field.label }}{% if field.required %} <span style="color:hsl(var(--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="cm-input"></textarea>
{% elif field.type == "select" %}
<select id="cf-{{ bid }}-{{ field.name }}" name="{{ field.name }}"{% if field.required %} required{% endif %} class="cm-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="cm-input">
{% endif %}
</div>
{% endfor %}
<div class="cm-field--full" aria-hidden="true" style="display:none;"><label>Leave this field empty<input type="text" name="website" tabindex="-1" autocomplete="off"></label></div>
<div class="cm-field--full"><button type="submit" class="cm-btn-primary">{{ submitLabel|default:"Send message" }}</button></div>
{% endform %}
{% endwith %}
{% endif %}
</div></section>
<style>#contact-form-{{ bid }} .cm-form-grid{display:grid;grid-template-columns:repeat(1, minmax(0, 1fr));gap:1.25rem;margin-top:2rem;}@media (min-width:640px){#contact-form-{{ bid }} .cm-form-grid{grid-template-columns:repeat(2, minmax(0, 1fr));}}#contact-form-{{ bid }} .cm-field--full{grid-column:1 / -1;}#contact-form-{{ bid }} .cm-label{display:block;margin-bottom:0.375rem;font-size:0.8125rem;font-weight:500;color:hsl(var(--foreground));font-family:var(--font-heading, "Inter Tight", "Inter", system-ui, sans-serif);}#contact-form-{{ bid }} .cm-input{width:100%;padding:0.5rem 0.75rem;font-size:0.9375rem;font-family:var(--font-body, "Inter", system-ui, sans-serif);color:hsl(var(--foreground));background:hsl(var(--background));border:1px solid hsl(var(--input));border-radius:4px;box-sizing:border-box;}#contact-form-{{ bid }} .cm-input:focus{outline:2px solid hsl(var(--ring));outline-offset:1px;}</style>
{% endwith %}

View File

@ -1,44 +0,0 @@
{# countdown override — oversized mono unit boxes; host countdown JS binds via data-* attrs. #}
<section class="cm-section {{ class }}"><div class="cm-content-well" style="text-align: center;">
{% if heading %}<h2 class="cm-heading" style="font-size: clamp(1.75rem, 3vw, 2.25rem); margin: 0 0 0.75rem 0;">{{ heading }}</h2>{% endif %}
{% if intro %}<p class="cm-lede" style="max-width: 60ch; margin: 0 auto; color: hsl(var(--muted-foreground));">{{ intro }}</p>{% endif %}
<div data-countdown style="margin-top: 2.5rem;">
<div data-countdown-timer data-target="{{ target }}" role="timer" aria-label="Time remaining" style="display: flex; align-items: flex-start; justify-content: center; gap: 1rem; flex-wrap: wrap;">
<div class="cm-count-unit {% if variant == "boxes" %}cm-count-box{% endif %}" style="display: flex; flex-direction: column; align-items: center; {% if variant == "boxes" %}min-width: 5rem; border: 1px solid hsl(var(--border)); background: hsl(var(--card)); padding: 1rem 0.75rem;{% endif %}"><span class="cm-count-figure numeric-tabular" style="font-family: var(--font-mono, ui-monospace, monospace); font-size: clamp(2rem, 5vw, 3rem); font-weight: 500; line-height: 1; color: hsl(var(--foreground));" data-unit="days">--</span><span class="cm-stat-label" style="margin-top: 0.5rem;">{{ labelDays|default:"Days" }}</span></div>
<div class="cm-count-unit {% if variant == "boxes" %}cm-count-box{% endif %}" style="display: flex; flex-direction: column; align-items: center; {% if variant == "boxes" %}min-width: 5rem; border: 1px solid hsl(var(--border)); background: hsl(var(--card)); padding: 1rem 0.75rem;{% endif %}"><span class="cm-count-figure numeric-tabular" style="font-family: var(--font-mono, ui-monospace, monospace); font-size: clamp(2rem, 5vw, 3rem); font-weight: 500; line-height: 1; color: hsl(var(--foreground));" data-unit="hours">--</span><span class="cm-stat-label" style="margin-top: 0.5rem;">{{ labelHours|default:"Hours" }}</span></div>
<div class="cm-count-unit {% if variant == "boxes" %}cm-count-box{% endif %}" style="display: flex; flex-direction: column; align-items: center; {% if variant == "boxes" %}min-width: 5rem; border: 1px solid hsl(var(--border)); background: hsl(var(--card)); padding: 1rem 0.75rem;{% endif %}"><span class="cm-count-figure numeric-tabular" style="font-family: var(--font-mono, ui-monospace, monospace); font-size: clamp(2rem, 5vw, 3rem); font-weight: 500; line-height: 1; color: hsl(var(--foreground));" data-unit="minutes">--</span><span class="cm-stat-label" style="margin-top: 0.5rem;">{{ labelMinutes|default:"Minutes" }}</span></div>
<div class="cm-count-unit {% if variant == "boxes" %}cm-count-box{% endif %}" style="display: flex; flex-direction: column; align-items: center; {% if variant == "boxes" %}min-width: 5rem; border: 1px solid hsl(var(--border)); background: hsl(var(--card)); padding: 1rem 0.75rem;{% endif %}"><span class="cm-count-figure numeric-tabular" style="font-family: var(--font-mono, ui-monospace, monospace); font-size: clamp(2rem, 5vw, 3rem); font-weight: 500; line-height: 1; color: hsl(var(--foreground));" data-unit="seconds">--</span><span class="cm-stat-label" style="margin-top: 0.5rem;">{{ labelSeconds|default:"Seconds" }}</span></div>
</div>
<div data-countdown-expired style="display: none; margin-top: 2rem;"><p class="cm-heading" style="font-size: 1.25rem;">{{ expiredMessage|default:"This has started." }}</p>{% if expiredUrl and expiredLabel %}<a class="cm-btn-primary" href="{{ expiredUrl }}" style="margin-top: 1rem;">{{ expiredLabel }}</a>{% endif %}</div>
</div>
<noscript><p class="cm-text" style="margin-top: 1.5rem; color: hsl(var(--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.style.display = 'block';
if (interval) clearInterval(interval);
return;
}
set('days', Math.floor(diff / 86400000));
set('hours', pad(Math.floor(diff % 86400000 / 3600000)));
set('minutes', pad(Math.floor(diff % 3600000 / 60000)));
set('seconds', pad(Math.floor(diff % 60000 / 1000)));
}
tick();
interval = setInterval(tick, 1000);
});
})();
</script>

View File

@ -1,8 +0,0 @@
{# cta override — full-width strip; band / card / split variants. #}
<section class="cm-cta-strip {% if background == "band" %}cm-cta-strip--band{% elif background == "card" %}cm-cta-strip--card{% endif %} {{ class }}" style="padding-top: 4rem; padding-bottom: 4rem;"><div class="cm-content-well" style="display: flex; flex-wrap: wrap; align-items: center; gap: 2rem; {% if background == "split" %}justify-content: space-between;{% endif %}">
<div style="{% if background != "split" %}max-width: 60ch;{% endif %}">
{% if heading %}<h2 class="cm-cta-headline cm-heading" style="font-size: clamp(1.75rem, 4vw, 2.75rem); margin: 0; color: inherit;">{{ heading }}</h2>{% endif %}
{% if text %}<p class="cm-lede" style="margin: 0.75rem 0 0 0; max-width: 55ch; color: {% if background == "band" %}inherit{% else %}hsl(var(--muted-foreground)){% endif %};">{{ text }}</p>{% endif %}
</div>
{% if primaryLabel or secondaryLabel %}<div class="cm-btn-row" style="display: flex; flex-wrap: wrap; gap: 0.75rem; flex: none;">{% if primaryUrl and primaryLabel %}<a class="cm-btn-primary cm-btn-lg" href="{{ primaryUrl }}">{{ primaryLabel }}</a>{% endif %}{% if secondaryUrl and secondaryLabel %}<a class="cm-btn-outline cm-btn-lg" href="{{ secondaryUrl }}">{{ secondaryLabel }}</a>{% endif %}</div>{% endif %}
</div></section>

View File

@ -1,2 +0,0 @@
{# divider override — plain hairline / thick spacer rule / centered ornament between two hairlines. #}
{% if variant == "ornament" %}<div class="cm-divider-ornament {{ class }}" role="separator" style="display:flex;align-items:center;gap:1rem;margin:2.5rem auto;{% if width == "narrow" %}max-width:200px;{% elif width == "wide" %}max-width:768px;{% endif %}"><span class="cm-hairline-top" style="flex:1;height:0;"></span><span style="display:inline-flex;color:hsl(var(--muted-foreground));">::{{ icon|default:"lucide:asterisk" }}:md::</span><span class="cm-hairline-top" style="flex:1;height:0;"></span></div>{% elif variant == "spacer-rule" %}<hr class="cm-divider-line cm-divider-thick {{ class }}" role="separator" style="border:0;border-top:{% if weight == "thick" %}4px{% elif weight == "medium" %}2px{% else %}1px{% endif %} solid hsl(var(--border));margin:3rem auto;{% if width == "narrow" %}max-width:200px;{% elif width == "wide" %}max-width:768px;{% endif %}">{% else %}<hr class="cm-divider-line {{ class }}" role="separator" style="border:0;border-top:{% if weight == "thick" %}4px{% elif weight == "medium" %}2px{% else %}1px{% endif %} solid hsl(var(--border));margin:2rem auto;{% if width == "narrow" %}max-width:200px;{% elif width == "wide" %}max-width:768px;{% endif %}">{% endif %}

View File

@ -1,31 +0,0 @@
{# event-list override — dates shown VERBATIM in each event's own timezone (never converted); list rows or card grid. #}
<section class="cm-section {{ class }}"><div class="cm-content-well">
{% if heading %}<h2 class="cm-heading" style="font-size:clamp(1.75rem, 3vw, 2.25rem);margin:0 0 0.75rem 0;"><span class="cm-heading-h2-accent">{{ heading }}</span></h2>{% endif %}
{% if intro %}<p class="cm-lede" style="max-width:60ch;margin:0 0 2.5rem 0;color:hsl(var(--muted-foreground));">{{ intro }}</p>{% endif %}
{% if layout == "cards" %}
<div class="cm-grid cm-grid-3" style="margin-top:2.5rem;display:grid;grid-template-columns:repeat(auto-fill, minmax(260px, 1fr));gap:1.5rem;">
{% for event in events %}{% if not (hidePast and event.past) %}<article class="cm-card" style="display:flex;flex-direction:column;padding:0;overflow:hidden;{% if event.past %}opacity:0.6;{% endif %}">
{% if event.media %}<div class="cm-media-frame" style="aspect-ratio:16 / 9;overflow:hidden;border-bottom:1px solid hsl(var(--border));">{% img event.media alt=event.title|default:"" class="cm-event-media" %}</div>{% endif %}
<div style="display:flex;flex-direction:column;gap:0.5rem;padding:1.25rem;flex:1;">
<div class="cm-mono numeric-tabular" style="font-size:0.8125rem;color:hsl(var(--accent));"><time{% if event.datetime %} datetime="{{ event.datetime }}"{% endif %}>{{ event.start }}{% if event.end %} &ndash; {{ event.end }}{% endif %}</time>{% if event.timezone %} <span style="color:hsl(var(--muted-foreground));">{{ event.timezone }}</span>{% endif %}</div>
<h3 class="cm-feature-title" style="font-size:1.125rem;margin:0;">{{ event.title }}</h3>
{% if event.venue %}<p style="margin:0;font-size:0.875rem;color:hsl(var(--muted-foreground));">{{ event.venue }}</p>{% endif %}
{% if event.description %}<p style="margin:0;font-size:0.875rem;color:hsl(var(--muted-foreground));">{{ event.description }}</p>{% endif %}
{% if event.link %}<a class="cm-btn-ghost" href="{{ event.link }}" style="margin-top:auto;display:inline-flex;align-items:center;gap:0.375rem;">{{ event.linkLabel|default:"Details" }}::lucide:arrow-right:sm::</a>{% endif %}
</div>
</article>{% endif %}{% endfor %}
</div><style>.cm-event-media{display:block;width:100%;height:100%;object-fit:cover;}</style>
{% else %}
<ul class="cm-hairline-top" style="list-style:none;margin:2.5rem 0 0 0;padding:0;">
{% for event in events %}{% if not (hidePast and event.past) %}<li class="cm-hairline-bottom" style="display:flex;flex-wrap:wrap;align-items:baseline;gap:0.75rem 1.5rem;padding:1.25rem 0;{% if event.past %}opacity:0.6;{% endif %}">
<div class="cm-mono numeric-tabular" style="flex:0 0 12rem;font-size:0.875rem;"><time{% if event.datetime %} datetime="{{ event.datetime }}"{% endif %}>{{ event.start }}</time>{% if event.end %}<div style="font-size:0.75rem;color:hsl(var(--muted-foreground));">to {{ event.end }}</div>{% endif %}{% if event.timezone %}<div style="font-size:0.75rem;color:hsl(var(--muted-foreground));">{{ event.timezone }}</div>{% endif %}</div>
<div style="min-width:0;flex:1;">
<h3 class="cm-feature-title" style="font-size:1rem;margin:0;">{{ event.title }}</h3>
{% if event.venue %}<p style="margin:0.125rem 0 0 0;font-size:0.875rem;color:hsl(var(--muted-foreground));">{{ event.venue }}</p>{% endif %}
{% if event.description %}<p style="margin:0.25rem 0 0 0;font-size:0.875rem;color:hsl(var(--muted-foreground));">{{ event.description }}</p>{% endif %}
</div>
{% if event.link %}<a class="cm-btn-outline cm-btn-sm" href="{{ event.link }}" style="flex:0 0 auto;">{{ event.linkLabel|default:"Details" }}</a>{% endif %}
</li>{% endif %}{% endfor %}
</ul>
{% endif %}
</div></section>

View File

@ -1,6 +0,0 @@
{# faq override — native hairline-divided disclosure list. #}
<section class="cm-section {{ class }}"><div class="cm-content-well cm-measure">
{% if heading %}<h2 class="cm-heading" style="font-size: clamp(1.75rem, 3vw, 2.25rem); margin: 0 0 0.75rem 0;"><span class="cm-heading-h2-accent">{{ heading }}</span></h2>{% endif %}
{% if intro %}<p class="cm-lede" style="max-width: 60ch; margin: 0 0 2rem 0; color: hsl(var(--muted-foreground));">{{ intro }}</p>{% endif %}
{% if items %}<div class="cm-hairline-top" style="margin-top: 1.5rem;">{% for item in items %}<details class="cm-faq-item cm-hairline-bottom"{% if variant == "open-list" %} open{% endif %}><summary class="cm-heading" style="display: flex; justify-content: space-between; align-items: center; gap: 1rem; cursor: pointer; padding: 1rem 0; font-size: 1.125rem; list-style: none;">{{ item.question }}<span class="cm-faq-chevron" aria-hidden="true" style="color: hsl(var(--muted-foreground)); flex: none;">::lucide:chevron-down:sm::</span></summary><div class="cm-faq-answer cm-text" style="padding-bottom: 1rem; color: hsl(var(--muted-foreground));">{{ item.answer|safe }}</div></details>{% endfor %}</div>{% endif %}
</div></section>

View File

@ -1,7 +0,0 @@
{# feature-grid override — Swiss cells, each led by a Bauhaus square mark. #}
<section class="cm-section {{ class }}"><div class="cm-content-well">
{% if eyebrow %}<p class="cm-eyebrow">{{ eyebrow }}</p>{% endif %}
{% if heading %}<h2 class="cm-heading" style="font-size: clamp(1.75rem, 3vw, 2.25rem); margin: 0 0 0.75rem 0;"><span class="cm-heading-h2-accent">{{ heading }}</span></h2>{% endif %}
{% if intro %}<p class="cm-lede" style="max-width: 60ch; margin: 0 0 2.5rem 0; color: hsl(var(--muted-foreground));">{{ intro }}</p>{% endif %}
{% if items %}<div class="cm-grid {% if columns == 2 %}cm-grid-2{% elif columns == 4 %}cm-grid-4{% else %}cm-grid-3{% endif %}">{% for item in items %}<div class="cm-feature">{% if item.icon %}<span class="cm-mark cm-mark-square" aria-hidden="true">::{{ item.icon }}:sm::</span>{% else %}<span class="cm-mark cm-mark-square" aria-hidden="true"></span>{% endif %}{% if item.title %}<h3 class="cm-feature-title">{% if item.linkUrl %}<a href="{{ item.linkUrl }}" style="color: inherit; text-decoration: none;">{{ item.title }}</a>{% else %}{{ item.title }}{% endif %}</h3>{% endif %}{% if item.text %}<p class="cm-text" style="font-size: 0.9375rem; line-height: 1.6; color: hsl(var(--muted-foreground));">{{ item.text }}</p>{% endif %}{% if item.linkUrl and item.linkLabel %}<a class="cm-btn-ghost" href="{{ item.linkUrl }}" style="margin-top: 0.75rem;">{{ item.linkLabel }}::lucide:arrow-right:sm::</a>{% endif %}</div>{% endfor %}</div>{% endif %}
</div></section>

View File

@ -1 +0,0 @@
<section class="cm-section {{ class }}" data-block="corporate-modernist:featured-posts"><div class="cm-content-well">{% if heading %}<h2 class="cm-heading" style="font-size: clamp(1.75rem, 3vw, 2.25rem); margin: 0 0 2rem 0;"><span class="cm-heading-h2-accent">{{ heading }}</span></h2>{% endif %}<div class="cm-post-grid {% if columns == 2 %}cm-post-grid-2{% else %}cm-post-grid-3{% endif %}">{% for post in posts %}<article class="cm-post-card">{% if showFeaturedImage and post.featured_image_url %}<a href="{{ post.url }}" class="cm-post-thumb cm-media-frame" style="display: block; aspect-ratio: 16 / 9;"><img src="{{ post.featured_image_url }}" alt="{{ post.title }}" style="width: 100%; height: 100%; object-fit: cover;"></a>{% endif %}<h3 class="cm-post-title"><a href="{{ post.url }}">{{ post.title }}</a></h3>{% if showExcerpt and post.excerpt %}<p class="cm-post-excerpt">{{ post.excerpt }}</p>{% endif %}<div class="cm-post-meta">{% if showAuthor and post.author_name %}<span>{{ post.author_name }}</span>{% endif %}{% if showDate and post.published_at %}<span class="cm-mono numeric-tabular">{{ post.published_at|date:"M j, Y" }}</span>{% endif %}{% if post.reading_time %}<span class="cm-mono numeric-tabular">{{ post.reading_time }} min</span>{% endif %}</div></article>{% endfor %}</div></div></section>

View File

@ -1,17 +0,0 @@
{# footer override — Swiss-grid footer chrome from CONTENT fields (brand, contact, newsletter, legal); menu columns / social / legal rows render from the footer providers (empty today, forward-correct). #}
<footer class="cm-footer {{ class }}"><div class="cm-content-well">
<div class="cm-footer-cols" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:2.5rem;align-items:start;">
<div class="cm-stack" style="grid-column:span 2;min-width:220px;display:flex;flex-direction:column;gap:1rem;">
<a href="/" style="display:inline-flex;align-items:center;gap:0.5rem;text-decoration:none;color:hsl(var(--foreground));">{% if logo %}{% img logo alt=companyName|default:"Home" class="cm-footer-logo" %}{% else %}<span class="cm-mark cm-mark-square" aria-hidden="true"></span><span class="cm-display" style="font-size:1.125rem;">{{ companyName|default:context.site.title|default:"Studio" }}</span>{% endif %}</a>
{% if tagline %}<p class="cm-text" style="font-size:0.9375rem;max-width:34ch;margin:0;color:hsl(var(--muted-foreground));">{{ tagline }}</p>{% endif %}
{% if address or email or phone %}<address class="cm-mono numeric-tabular" style="font-style:normal;font-size:0.8125rem;line-height:1.8;color:hsl(var(--muted-foreground));">{% if address %}<div>{{ address }}</div>{% endif %}{% if email %}<div><a href="mailto:{{ email }}" class="cm-footer-link" style="color:inherit;text-decoration:none;">{{ email }}</a></div>{% endif %}{% if phone %}<div><a href="tel:{{ phone }}" class="cm-footer-link" style="color:inherit;text-decoration:none;">{{ phone }}</a></div>{% endif %}</address>{% endif %}
{% if showNewsletter %}<div class="cm-stack-sm" style="margin-top:0.5rem;display:flex;flex-direction:column;gap:0.5rem;"><p class="cm-footer-head" style="margin:0;font-size:0.75rem;text-transform:uppercase;letter-spacing:0.08em;color:hsl(var(--foreground));">{{ newsletterTitle|default:"Stay in the loop" }}</p>{% if newsletterDescription %}<p class="cm-text" style="margin:0;font-size:0.8125rem;color:hsl(var(--muted-foreground));">{{ newsletterDescription }}</p>{% endif %}{% signup_form list=newsletterListSlug|default:"main" source="footer" submit=newsletterButtonText|default:"Subscribe" %}</div>{% endif %}
</div>
{% for col in columns %}<div class="cm-stack-sm"><p class="cm-footer-head" style="margin:0 0 0.75rem;font-size:0.75rem;text-transform:uppercase;letter-spacing:0.08em;color:hsl(var(--foreground));">{{ col.header }}</p><ul style="list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:0.5rem;">{% for item in col.items %}<li><a class="cm-footer-link" href="{{ item.url|default:'#' }}"{% if item.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} style="font-size:0.875rem;color:hsl(var(--muted-foreground));text-decoration:none;">{{ item.label }}</a></li>{% endfor %}</ul></div>{% endfor %}
</div>
<div class="cm-footer-legal" style="display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:1rem;">
<p style="margin:0;">{% if copyright %}&copy; {{ copyright }}{% else %}&copy; {{ companyName }}{% endif %}</p>
{% if legal_items %}<nav style="display:flex;flex-wrap:wrap;gap:1.25rem;">{% for l in legal_items %}<a href="{{ l.url|default:'#' }}"{% if l.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="cm-footer-link" style="font-size:0.8125rem;color:hsl(var(--muted-foreground));text-decoration:none;">{{ l.label }}</a>{% endfor %}</nav>{% endif %}
{% if showSocial and social_items %}<div class="cm-social-row" style="display:flex;align-items:center;gap:0.75rem;">{% for s in social_items %}<a href="{{ s.url }}" target="_blank" rel="noopener" title="{{ s.label }}" class="cm-social-link" style="display:inline-flex;color:hsl(var(--muted-foreground));">{% if s.icon %}::{{ s.icon }}:sm::{% endif %}</a>{% endfor %}</div>{% endif %}
</div>
</div><style>.cm-footer-logo{max-height:2rem;width:auto;display:block;}.cm-footer-link:hover{color:hsl(var(--foreground));}</style></footer>

View File

@ -1,6 +0,0 @@
{# gallery override — Swiss grid of hairline-framed images; carries data-bn-gallery / data-lightbox / data-bn-gallery-item so the host lightbox JS binds. #}
{% if columns == 4 %}{% set gcols = "cm-gallery-4" %}{% set gn = 4 %}{% elif columns == 2 %}{% set gcols = "" %}{% set gn = 2 %}{% else %}{% set gcols = "cm-gallery-3" %}{% set gn = 3 %}{% endif %}
{% if gap == "sm" %}{% set ggap = "0.5rem" %}{% elif gap == "lg" %}{% set ggap = "1.5rem" %}{% else %}{% set ggap = "1rem" %}{% endif %}
<div class="cm-gallery-grid {{ gcols }} {{ class }}" data-bn-gallery data-lightbox="{% if lightbox %}true{% else %}false{% endif %}" style="margin:1.5rem 0;display:grid;grid-template-columns:repeat({{ gn }}, minmax(0, 1fr));gap:{{ ggap }};">
{% for image in images %}<a href="{{ image.src|mediaURL }}" data-bn-gallery-item data-caption="{{ image.caption|default:'' }}" class="cm-media-frame" style="display:block;overflow:hidden;border:1px solid hsl(var(--border));border-radius:4px;background:hsl(var(--muted));">{% img image.src alt=image.alt|default:"" class="cm-gallery-img" %}</a>{% endfor %}
</div><style>.cm-gallery-grid .cm-gallery-img{display:block;width:100%;height:100%;aspect-ratio:1;object-fit:cover;}</style>

View File

@ -1,24 +0,0 @@
{# hero override — Swiss grid, oversized display headline, Bauhaus restraint. #}
{# styled-text fields accept a {text,color} object OR a bare string (.text|default:field). #}
{% 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" %}
{% macro herobody() %}
{% if eyebrowText %}<p class="cm-eyebrow">{{ eyebrowText }}</p>{% endif %}
{% if headlineText %}<h1 class="cm-hero-headline cm-display" style="font-size: clamp(2.5rem, 6vw, 4.5rem); line-height: 1.02; letter-spacing: -0.03em; margin: 0; color: hsl(var(--foreground));">{{ headlineText }}</h1>{% endif %}
{% if subText %}<p class="cm-hero-sub cm-lede" style="margin: 1.25rem 0 0 0; font-size: 1.25rem; line-height: 1.5; color: hsl(var(--muted-foreground)); max-width: 60ch;">{{ subText }}</p>{% endif %}
{% if descText %}<p class="cm-text" style="margin: 1rem 0 0 0; color: hsl(var(--muted-foreground)); max-width: 60ch;">{{ descText }}</p>{% endif %}
{% if pText or sText %}<div class="cm-btn-row" style="display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 2rem;">{% if pText and pUrl %}<a class="cm-btn-primary cm-btn-lg" href="{{ pUrl }}">{{ pText }}</a>{% endif %}{% if sText and sUrl %}<a class="cm-btn-outline cm-btn-lg" href="{{ sUrl }}">{{ sText }}</a>{% endif %}</div>{% endif %}
{% if trustLogos %}<div class="cm-hairline-top" style="margin-top: 3rem; padding-top: 1.5rem;"><p class="cm-kicker cm-eyebrow" style="margin-bottom: 1rem; color: hsl(var(--muted-foreground));">Trusted by</p><div class="cm-logo-row" style="display: flex; flex-wrap: wrap; align-items: center; gap: 2rem;">{% for logo in trustLogos %}{% if logo.url %}<a href="{{ logo.url }}">{% img logo.image alt=logo.alt class="cm-logo" %}</a>{% else %}{% img logo.image alt=logo.alt class="cm-logo" %}{% endif %}{% endfor %}</div></div>{% endif %}
{% endmacro %}
{% if variant == "split" and backgroundImage %}
<section class="cm-hero cm-hero-split {{ class }}" style="padding-top: 3.5rem; padding-bottom: 3.5rem;"><div class="cm-content-well"><div class="cm-grid cm-grid-2" style="align-items: center;"><div>{{ herobody() }}</div><div class="cm-media-frame" style="aspect-ratio: 4 / 3; background: hsl(var(--muted)); border: 1px solid hsl(var(--border)); overflow: hidden; {% if mediaPosition == "left" %}order: -1;{% endif %}">{% img backgroundImage alt=headlineText class="cm-hero-media" %}</div></div></div></section>
{% else %}
<section class="cm-hero {{ class }}" style="padding-top: 3.5rem; padding-bottom: 3.5rem; {% if variant == "left-aligned" %}text-align: left;{% else %}text-align: center;{% endif %}"><div class="cm-content-well" style="{% if variant != "left-aligned" %}display: flex; flex-direction: column; align-items: center;{% endif %}"><div style="max-width: 60ch; width: 100%;">{{ herobody() }}</div></div></section>
{% endif %}

View File

@ -1,18 +0,0 @@
{# hours-location override — hairline hours table + address; click-to-load map preserves the builtin data-map-src / onclick wiring. #}
<section class="cm-section {{ class }}"><div class="cm-content-well">
{% if heading %}<h2 class="cm-heading" style="font-size:clamp(1.75rem, 3vw, 2.25rem);margin:0 0 0.75rem 0;"><span class="cm-heading-h2-accent">{{ heading }}</span></h2>{% endif %}
<div class="cm-grid cm-grid-2" style="display:grid;grid-template-columns:repeat(auto-fit, minmax(280px, 1fr));gap:2.5rem;margin-top:2.5rem;">
<div>
{% if hours %}<table style="width:100%;border-collapse:collapse;font-size:0.9375rem;"><tbody>{% for row in hours %}<tr class="cm-hours-row cm-hairline-bottom"><th scope="row" style="text-align:left;padding:0.5rem 1rem 0.5rem 0;font-weight:500;">{{ row.day }}</th><td class="numeric-tabular" style="text-align:right;padding:0.5rem 0;color:{% if row.closed %}hsl(var(--muted-foreground)){% else %}hsl(var(--foreground)){% endif %};">{% if row.closed %}Closed{% else %}{{ row.hours }}{% endif %}</td></tr>{% endfor %}</tbody></table>{% endif %}
{% if specialHoursNote %}<p class="cm-surface-muted" style="margin-top:1rem;border:1px solid hsl(var(--border));background:hsl(var(--muted));padding:0.5rem 0.75rem;font-size:0.875rem;color:hsl(var(--muted-foreground));">{{ specialHoursNote }}</p>{% endif %}
{% if address %}<div style="margin-top:1.5rem;"><div class="cm-stat-label" style="margin:0;">Address</div><address style="margin-top:0.25rem;white-space:pre-line;font-style:normal;color:hsl(var(--foreground));">{{ address }}</address></div>{% endif %}
{% if phone %}<div style="margin-top:1rem;"><div class="cm-stat-label" style="margin:0;">Phone</div><a href="tel:{{ phone }}" style="display:inline-block;margin-top:0.25rem;color:hsl(var(--foreground));text-decoration:none;">{{ phone }}</a></div>{% endif %}
{% if directionsUrl %}<a href="{{ directionsUrl }}" target="_blank" rel="noopener noreferrer" class="cm-btn-outline" style="margin-top:1.5rem;display:inline-flex;align-items:center;gap:0.5rem;">::lucide:navigation:sm::Get directions</a>{% endif %}
</div>
{% if mapEmbedUrl %}
<div class="cm-media-frame" style="width:100%;min-height:16rem;aspect-ratio:4 / 3;overflow:hidden;border:1px solid hsl(var(--border));border-radius:4px;background:hsl(var(--muted));">
<button type="button" data-map-src="{{ mapEmbedUrl }}" onclick="var f=document.createElement('iframe');f.src=this.getAttribute('data-map-src');f.setAttribute('style','width:100%;height:100%;border:0;');f.setAttribute('loading','lazy');f.setAttribute('referrerpolicy','no-referrer-when-downgrade');f.setAttribute('title','Map');this.parentNode.replaceChild(f,this);" style="display:flex;align-items:center;justify-content:center;gap:0.5rem;width:100%;height:100%;min-height:16rem;border:0;background:none;cursor:pointer;color:hsl(var(--foreground));font-family:var(--font-heading, 'Inter Tight', 'Inter', system-ui, sans-serif);font-weight:500;">::lucide:map-pin:md::{{ mapButtonLabel|default:"Load map" }}</button>
</div>
{% endif %}
</div>
</div></section>

View File

@ -1,5 +0,0 @@
{# image override — hairline-framed figure with alignment and an optional caption. #}
{% if src %}{% if align == "left" %}{% set ialign = "flex-start" %}{% elif align == "right" %}{% set ialign = "flex-end" %}{% else %}{% set ialign = "center" %}{% endif %}<figure class="{{ class }}" style="margin:1.5rem 0;display:flex;flex-direction:column;align-items:{{ ialign }};">
<div class="cm-media-frame" style="max-width:100%;overflow:hidden;border:1px solid hsl(var(--border));border-radius:4px;line-height:0;">{% img src alt=alt|default:"" class="cm-image-el" %}</div>
{% if caption %}<figcaption class="cm-stat-label" style="margin-top:0.5rem;text-align:{% if align == "center" %}center{% else %}left{% endif %};">{{ caption }}</figcaption>{% endif %}
</figure><style>.cm-media-frame > .cm-image-el,.cm-media-frame > img{display:block;max-width:100%;height:auto;}</style>{% endif %}

View File

@ -1,5 +0,0 @@
{# logo-strip override — quiet greyscale client row under a kicker. #}
<section class="cm-section-tight {{ class }}" style="padding-top: 3rem; padding-bottom: 3rem;"><div class="cm-content-well">
{% if heading %}<p class="cm-kicker cm-eyebrow" style="text-align: center; color: hsl(var(--muted-foreground)); margin-bottom: 0;">{{ heading }}</p>{% endif %}
<div class="cm-logo-row" style="display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 2.5rem; margin-top: 1.5rem;">{% for logo in logos %}{% if logo.url %}<a href="{{ logo.url }}">{% img logo.image alt=logo.alt class="cm-logo" %}</a>{% else %}{% img logo.image alt=logo.alt class="cm-logo" %}{% endif %}{% endfor %}</div>
</div></section>

View File

@ -1,21 +0,0 @@
{# menu-list override — sectioned price list; tabular-numeral prices right-aligned, dietary badges as hairline pills. #}
<section class="cm-section {{ class }}"><div class="cm-content-well">
{% if heading %}<h2 class="cm-heading" style="font-size:clamp(1.75rem, 3vw, 2.25rem);margin:0 0 0.75rem 0;"><span class="cm-heading-h2-accent">{{ heading }}</span></h2>{% endif %}
{% if intro %}<p class="cm-lede" style="max-width:60ch;margin:0 0 2.5rem 0;color:hsl(var(--muted-foreground));">{{ intro }}</p>{% endif %}
<div class="cm-grid {% if columns == 2 %}cm-grid-2{% endif %}" style="display:grid;gap:2.5rem 3rem;{% if columns == 2 %}grid-template-columns:repeat(2, minmax(0, 1fr));{% endif %}">
{% for section in sections %}<div class="cm-menu-section" style="break-inside:avoid;">
{% if section.title %}<h3 class="cm-display" style="font-size:1.25rem;border-bottom:1px solid hsl(var(--border));padding-bottom:0.5rem;margin:0 0 1rem 0;">{{ section.title }}</h3>{% endif %}
{% if section.note %}<p style="margin:0 0 1rem 0;font-size:0.875rem;color:hsl(var(--muted-foreground));">{{ section.note }}</p>{% endif %}
<ul style="list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:1.25rem;">
{% for item in section.items %}<li class="cm-menu-item" style="display:flex;align-items:baseline;gap:1rem;">
<div style="min-width:0;flex:1;">
<div style="display:flex;flex-wrap:wrap;align-items:baseline;gap:0.5rem;"><span class="cm-display" style="font-size:1rem;color:hsl(var(--foreground));">{{ item.name }}</span>{% for badge in item.badges %}{% if badge.label %}<span style="display:inline-block;border:1px solid hsl(var(--border));border-radius:4px;padding:0.05rem 0.4rem;font-size:0.6875rem;text-transform:uppercase;letter-spacing:0.05em;color:hsl(var(--muted-foreground));">{{ badge.label }}</span>{% endif %}{% endfor %}</div>
{% if item.description %}<p style="margin:0.25rem 0 0 0;font-size:0.875rem;color:hsl(var(--muted-foreground));">{{ item.description }}</p>{% endif %}
</div>
{% if item.price %}<span class="cm-menu-price cm-mono numeric-tabular" style="flex:0 0 auto;color:hsl(var(--foreground));">{% if currency %}{{ currency }}{% endif %}{{ item.price }}</span>{% endif %}
</li>{% endfor %}
</ul>
</div>{% endfor %}
</div>
{% if footnote %}<p class="cm-stat-label" style="margin-top:2.5rem;">{{ footnote }}</p>{% endif %}
</div></section>

View File

@ -1 +0,0 @@
<nav class="cm-navbar {{ class }}" aria-label="Primary"><a class="cm-brand" href="/"><span class="cm-mark cm-mark-square" aria-hidden="true"></span>{% if logoType == "image" and logo %}{% img logo alt=alt|default:"Home" class="cm-brand-logo" %}{% else %}<span>{{ logoText.text|default:logoText|default:context.site.title|default:"Studio" }}</span>{% endif %}</a><div class="cm-nav-links">{% for item in menu.items %}<a class="cm-nav-link" href="{{ item.url }}"{% if item.open_in_new_tab %} target="_blank" rel="noopener"{% endif %}>{{ item.label|default:item.title }}</a>{% endfor %}{% if cta.text and cta.url %}<a class="cm-btn-primary cm-btn-sm" href="{{ cta.url }}">{{ cta.text }}</a>{% endif %}</div><input type="checkbox" id="cm-nav-toggle-cb" class="cm-nav-cb" style="display:none;"><label for="cm-nav-toggle-cb" class="cm-nav-toggle cm-btn-outline cm-btn-sm" aria-label="Toggle menu">::lucide:menu:sm::</label><div class="cm-nav-drawer">{% for item in menu.items %}<a href="{{ item.url }}"{% if item.open_in_new_tab %} target="_blank" rel="noopener"{% endif %}>{{ item.label|default:item.title }}</a>{% endfor %}{% if cta.text and cta.url %}<a href="{{ cta.url }}" class="cm-btn-primary cm-btn-sm" style="margin-top:0.75rem;">{{ cta.text }}</a>{% endif %}</div><style>.cm-nav-cb:checked ~ .cm-nav-drawer{display:flex;}</style></nav>

View File

@ -1 +0,0 @@
<section class="cm-section {{ class }}" data-block="corporate-modernist:newsletter-signup"><div class="cm-content-well"><div class="{% if layout == "inline" %}cm-panel{% else %}cm-card cm-card-accent{% endif %}" style="text-align: center;">{% if title %}<h2 class="cm-heading" style="font-size: clamp(1.5rem, 2.5vw, 2rem); margin: 0;"><span class="cm-heading-h2-accent">{{ title }}</span></h2>{% endif %}{% if description %}<p class="cm-lede" style="max-width: 52ch; margin: 0.75rem auto 0 auto;">{{ description }}</p>{% endif %}<div class="cm-form" style="max-width: 28rem; margin: 1.5rem auto 0 auto;">{% signup_form list=listSlug source=source submit=submitText success_message=successMessage placeholder=placeholder %}</div></div></div></section>

View File

@ -1 +0,0 @@
<section class="cm-section {{ class }}" data-block="corporate-modernist:page-suggestions"><div class="cm-content-well"><div class="cm-measure cm-stack"><span class="cm-mark cm-mark-triangle" aria-hidden="true"></span><h2 class="cm-heading" style="font-size: clamp(1.5rem, 2.5vw, 2rem); margin: 0;"><span class="cm-heading-h2-accent">{{ title|default:"Were you looking for..." }}</span></h2>{% if suggestions %}<ul class="cm-stack-sm" style="list-style: none; margin: 0; padding: 0;">{% for s in suggestions %}<li><a class="cm-sidebar-link" href="/{{ s.slug }}" style="display: block;"><span class="cm-post-title" style="font-size: 1rem;">{{ s.title }}</span>{% if showDescription and s.excerpt %}<span class="cm-text" style="display: block; font-size: 0.875rem; color: hsl(var(--muted-foreground));">{{ s.excerpt }}</span>{% endif %}</a></li>{% endfor %}</ul>{% else %}{% if emptyMessage %}<p class="cm-text" style="margin: 0; color: hsl(var(--muted-foreground));">{{ emptyMessage }}</p>{% endif %}<div class="cm-btn-row"><a class="cm-btn-outline cm-btn-sm" href="/">::lucide:home:sm::Home</a><a class="cm-btn-outline cm-btn-sm" href="/blog">::lucide:newspaper:sm::Blog</a></div>{% endif %}</div></div></section>

View File

@ -1,11 +0,0 @@
<div class="cm-stack {{ class }}" data-block="corporate-modernist:password-reset-form">
<span class="cm-mark cm-mark-ring" aria-hidden="true"></span>
<h2 class="cm-heading" style="font-size: 1.5rem; margin: 0;">Reset password</h2>
<p class="cm-text" style="margin: 0; font-size: 0.9375rem; color: hsl(var(--muted-foreground));">Enter your email and we will send a link to reset your password.</p>
<div id="reset-message" role="status" aria-live="polite"></div>
<form class="cm-form" hx-post="/api/auth/request-password-reset" hx-target="#reset-message" hx-swap="innerHTML">
<div class="cm-field cm-field--full"><label class="cm-label" for="cm-reset-email">Email</label><input class="cm-input" type="email" name="email" id="cm-reset-email" required></div>
<button type="submit" class="cm-btn-primary" hx-disabled-elt="this" style="width:100%; justify-content:center;">Send reset link</button>
</form>
<a class="cm-btn-ghost cm-btn-sm" href="/login" style="align-self: flex-start;">::lucide:arrow-left:sm::Back to sign in</a>
</div>

View File

@ -1 +0,0 @@
<section class="cm-section-tight {{ class }}" data-block="corporate-modernist:popular-posts">{% if heading %}<h2 class="cm-kicker">{{ heading }}</h2>{% endif %}<ol class="cm-stack-sm" style="list-style: none; margin: 0; padding: 0;">{% for post in posts %}<li style="display: flex; gap: 0.75rem; align-items: flex-start;">{% if showRank %}<span class="cm-rank numeric-tabular">{{ forloop.Counter }}</span>{% endif %}{% if showThumbnail and post.featured_image_url %}<a href="{{ post.url }}" class="cm-post-thumb cm-media-frame" style="width: 3rem; height: 3rem; flex-shrink: 0;"><img src="{{ post.featured_image_url }}" alt="{{ post.title }}" style="width: 100%; height: 100%; object-fit: cover;"></a>{% endif %}<div style="min-width: 0;"><h3 class="cm-post-title" style="font-size: 0.9375rem;"><a href="{{ post.url }}">{{ post.title }}</a></h3>{% if post.published_at %}<p class="cm-post-meta"><span class="cm-mono numeric-tabular">{{ post.published_at|date:"M j, Y" }}</span></p>{% endif %}</div></li>{% endfor %}</ol></section>

View File

@ -1 +0,0 @@
<section class="cm-section {{ class }}" data-block="corporate-modernist:post-hero"><div class="cm-content-well">{% if context.currentPost.featuredImageUrl %}<div class="cm-media-frame" style="aspect-ratio: 21 / 9; margin-bottom: 2.5rem;"><img src="{{ context.currentPost.featuredImageUrl }}" alt="{{ context.currentPost.featuredImageAlt|default:context.currentPost.title }}" style="width: 100%; height: 100%; object-fit: cover;"></div>{% endif %}<div class="cm-measure cm-stack" style="{% if align == "left" %}text-align: left;{% else %}margin-left: auto; margin-right: auto; text-align: center;{% endif %}"><span class="cm-mark cm-mark-square" aria-hidden="true"{% if align != "left" %} style="margin-left: auto; margin-right: auto;"{% endif %}></span><h1 class="cm-display" style="margin: 0;">{{ context.currentPost.title }}</h1>{% if showExcerpt and context.currentPost.excerpt %}<p class="cm-lede" style="margin: 0;">{{ context.currentPost.excerpt }}</p>{% endif %}{% if showMeta %}<div class="cm-post-meta"{% if align != "left" %} style="justify-content: center;"{% endif %}>{% if context.currentPost.authorName %}<span>{{ context.currentPost.authorName }}</span>{% endif %}{% if context.currentPost.publishedAt %}<span class="cm-mono numeric-tabular">{{ context.currentPost.publishedAt }}</span>{% endif %}{% if context.currentPost.readingTimeMinutes %}<span class="cm-mono numeric-tabular">{{ context.currentPost.readingTimeMinutes }} min read</span>{% endif %}</div>{% endif %}</div></div></section>

View File

@ -1 +0,0 @@
<div class="cm-post-meta {{ class }}" data-block="corporate-modernist:post-metadata">{% if showAuthor and context.currentPost.authorName %}<span style="display: inline-flex; align-items: center; gap: 0.5rem;">{% if showAvatar and context.currentPost.authorAvatarUrl %}<img class="cm-avatar" src="{{ context.currentPost.authorAvatarUrl }}" alt="{{ context.currentPost.authorName }}" style="width: 1.75rem; height: 1.75rem; border-radius: 9999px; object-fit: cover;">{% endif %}<span style="color: hsl(var(--foreground)); font-weight: 500;">{{ context.currentPost.authorName }}</span></span>{% endif %}{% if showDate and context.currentPost.publishedAt %}<time class="cm-mono numeric-tabular" datetime="{{ context.currentPost.publishedAtIso }}">{{ context.currentPost.publishedAt }}</time>{% endif %}{% if showReadingTime and context.currentPost.readingTimeMinutes %}<span class="cm-mono numeric-tabular">{{ context.currentPost.readingTimeMinutes }} min read</span>{% endif %}</div>

View File

@ -1,14 +0,0 @@
{# pricing override — flat hairline tiers, mono price figures, accent-top featured tier. #}
<section class="cm-section {{ class }}"><div class="cm-content-well">
{% if heading %}<h2 class="cm-heading" style="font-size: clamp(1.75rem, 3vw, 2.25rem); margin: 0 0 0.75rem 0;"><span class="cm-heading-h2-accent">{{ heading }}</span></h2>{% endif %}
{% if intro %}<p class="cm-lede" style="max-width: 60ch; margin: 0 0 2.5rem 0; color: hsl(var(--muted-foreground));">{{ intro }}</p>{% endif %}
{% if tiers %}<div class="cm-grid cm-grid-3">{% for tier in tiers %}<div class="cm-card cm-price-tier {% if tier.highlighted %}cm-price-tier--featured cm-card-accent{% endif %}" style="display: flex; flex-direction: column;">
{% if tier.badge %}<span class="cm-badge" style="display: inline-block; align-self: flex-start; background: hsl(var(--accent)); color: hsl(var(--accent-foreground)); font-size: 0.6875rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; padding: 0.25rem 0.5rem; border-radius: 4px; margin-bottom: 0.75rem;">{{ tier.badge }}</span>{% endif %}
{% if tier.name %}<h3 class="cm-feature-title" style="margin: 0;">{{ tier.name }}</h3>{% endif %}
<p style="display: flex; align-items: baseline; gap: 0.25rem; margin: 1rem 0 0 0;"><span class="cm-price numeric-tabular" style="font-family: var(--font-mono, ui-monospace, monospace); font-size: clamp(2rem, 4vw, 2.75rem); font-weight: 500; line-height: 1; color: hsl(var(--foreground));">{% if currency %}{{ currency }}{% else %}${% endif %}{{ tier.price }}</span>{% if tier.period %}<span class="cm-price-period cm-stat-label" style="margin-top: 0;">{{ tier.period }}</span>{% endif %}</p>
{% if tier.description %}<p class="cm-text" style="font-size: 0.9375rem; color: hsl(var(--muted-foreground)); margin-top: 0.75rem;">{{ tier.description }}</p>{% endif %}
{% if tier.features %}<ul class="cm-stack-sm" style="list-style: none; padding: 0; margin: 1.5rem 0 0 0; display: flex; flex-direction: column; gap: 0.625rem; flex: 1;">{% for feature in tier.features %}<li class="cm-check" style="display: flex; gap: 0.5rem; align-items: flex-start; font-size: 0.9375rem; color: hsl(var(--foreground));"><span style="color: hsl(var(--accent)); flex: none;">::lucide:check:sm::</span><span>{{ feature }}</span></li>{% endfor %}</ul>{% endif %}
{% if tier.ctaUrl and tier.ctaLabel %}{% if tier.highlighted %}<a class="cm-btn-primary" href="{{ tier.ctaUrl }}" style="margin-top: 1.5rem; width: 100%; box-sizing: border-box; text-align: center;">{{ tier.ctaLabel }}</a>{% else %}<a class="cm-btn-outline" href="{{ tier.ctaUrl }}" style="margin-top: 1.5rem; width: 100%; box-sizing: border-box; text-align: center;">{{ tier.ctaLabel }}</a>{% endif %}{% endif %}
</div>{% endfor %}</div>{% endif %}
{% if billingNote %}<p class="cm-stat-label" style="text-align: center; margin-top: 2rem;">{{ billingNote }}</p>{% endif %}
</div></section>

View File

@ -1,6 +0,0 @@
{# quote override — pull quote (accent top rule) / block quote (accent left rule) / inline card. #}
{% if style == "pull" %}{% set qcls = "cm-pull-quote" %}{% set qsty = "margin:2rem 0;" %}{% elif style == "inline-card" %}{% set qcls = "cm-quote-block cm-card cm-card-accent" %}{% set qsty = "margin:2rem 0;" %}{% else %}{% set qcls = "cm-quote-block" %}{% set qsty = "margin:2rem 0;border-left:2px solid hsl(var(--accent));padding-left:1.25rem;" %}{% endif %}
<figure class="{{ qcls }} {{ class }}" style="{{ qsty }}{% if align == "center" %}text-align:center;{% endif %}">
<blockquote{% if cite %} cite="{{ cite }}"{% endif %} style="margin:0;border:0;padding:0;{% if style != "pull" %}font-size:1.25rem;line-height:1.5;color:hsl(var(--foreground));{% endif %}">{{ quote|safe }}</blockquote>
{% if attribution or role or cite %}<figcaption class="cm-quote-cite" style="margin-top:1rem;font-size:0.875rem;color:hsl(var(--muted-foreground));">{% if attribution %}<span class="cm-display" style="font-size:0.9375rem;color:hsl(var(--foreground));">{{ attribution }}</span>{% endif %}{% if role %}<span style="display:block;">{{ role }}</span>{% endif %}{% if cite %}<a href="{{ cite }}" style="display:inline-block;margin-top:0.25rem;color:inherit;text-decoration:underline;text-underline-offset:2px;">Source</a>{% endif %}</figcaption>{% endif %}
</figure>

View File

@ -1 +0,0 @@
<section class="cm-section cm-hairline-top {{ class }}" data-block="corporate-modernist:related-posts"><div class="cm-content-well"><h2 class="cm-heading" style="font-size: clamp(1.5rem, 2.5vw, 2rem); margin: 0 0 2rem 0;"><span class="cm-heading-h2-accent">{{ heading|default:"Related posts" }}</span></h2><div class="cm-post-grid {% if columns == 2 %}cm-post-grid-2{% else %}cm-post-grid-3{% endif %}">{% for post in posts %}<article class="cm-post-card">{% if post.featured_image_url %}<a href="{{ post.url }}" class="cm-post-thumb cm-media-frame" style="display: block; aspect-ratio: 16 / 9;"><img src="{{ post.featured_image_url }}" alt="{{ post.title }}" style="width: 100%; height: 100%; object-fit: cover;"></a>{% endif %}<h3 class="cm-post-title"><a href="{{ post.url }}">{{ post.title }}</a></h3>{% if post.published_at %}<div class="cm-post-meta"><span class="cm-mono numeric-tabular">{{ post.published_at|date:"M j, Y" }}</span></div>{% endif %}</article>{% endfor %}</div></div></section>

View File

@ -1,10 +0,0 @@
{# rich-section override — two-column media + rich text, hairline surface options. #}
<section class="cm-section {% if background == "muted" %}cm-surface-muted{% elif background == "card" %}cm-surface-card{% endif %} {{ class }}"><div class="cm-content-well"><div class="cm-grid cm-grid-2" style="align-items: center;">
<div>
{% if eyebrow %}<p class="cm-eyebrow">{{ eyebrow }}</p>{% endif %}
{% if heading %}<h2 class="cm-heading" style="font-size: clamp(1.75rem, 3vw, 2.25rem); margin: 0 0 1rem 0;"><span class="cm-heading-h2-accent">{{ heading }}</span></h2>{% endif %}
{% if body %}<div class="cm-text" style="color: hsl(var(--muted-foreground)); line-height: 1.7;">{{ body|safe }}</div>{% endif %}
{% if ctaUrl and ctaLabel %}<a class="cm-btn-ghost" href="{{ ctaUrl }}" style="margin-top: 1.5rem;">{{ ctaLabel }}::lucide:arrow-right:sm::</a>{% endif %}
</div>
<div class="cm-media-frame" style="aspect-ratio: 4 / 3; background: hsl(var(--muted)); border: 1px solid hsl(var(--border)); overflow: hidden; {% if mediaPosition == "left" %}order: -1;{% endif %}">{% if media %}{% img media alt=mediaAlt class="cm-rich-media" %}{% endif %}</div>
</div></div></section>

View File

@ -1,2 +0,0 @@
{# rich-text override — raw imported HTML inside the Swiss reading measure. #}
{% if html %}<div class="cm-text cm-measure {{ class }}" style="max-width:70ch;margin-left:auto;margin-right:auto;">{{ html|safe }}</div>{% endif %}

View File

@ -1,3 +0,0 @@
<nav class="{{ class }}" data-block="corporate-modernist:sidebar-nav" style="{% if showBorder %}border-right: 1px solid hsl(var(--border)); padding-right: 1rem;{% endif %}">{% if title %}<h3 class="cm-kicker" style="margin-top: 0;">{{ title }}</h3>{% endif %}<ul class="cm-stack-sm" style="list-style: none; margin: 0; padding: 0;">{% for item in menu.items %}<li><a class="cm-sidebar-link" 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>{% if item.icon %}<svg class="cm-icon" aria-hidden="true"><use href="/icons/{{ item.icon|split:":"|first }}.svg#{{ item.icon|split:":"|last }}"/></svg>{% endif %}{{ item.label }}</a></li>{% endfor %}</ul>{% if highlightCurrent %}<script>
(function(){var p=window.location.pathname;document.querySelectorAll('[data-bn-sidebar-link]').forEach(function(a){if(a.getAttribute('href')===p){a.setAttribute('aria-current','page');}});})();
</script>{% endif %}</nav>

View File

@ -1,7 +0,0 @@
{# social-links override — icon row or labelled stack; each link a hairline cm-social-link (rendered only when it has a URL). #}
<div class="{{ class }}">
{% if heading %}<p class="cm-stat-label" style="margin:0 0 0.75rem 0;">{{ heading }}</p>{% endif %}
<ul class="cm-social-row" style="list-style:none;margin:0;padding:0;{% if style == "stack" %}display:flex;flex-direction:column;gap:0.5rem;{% else %}display:flex;flex-wrap:wrap;align-items:center;gap:0.75rem;{% 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="cm-social-link" style="display:inline-flex;align-items:center;gap:0.5rem;border:1px solid hsl(var(--border));border-radius:4px;color:hsl(var(--foreground));text-decoration:none;{% if style == "stack" %}padding:0.5rem 1rem;{% else %}padding:0.5rem;{% endif %}">{% if link.network %}::{{ link.network }}:sm::{% endif %}{% if style == "stack" and link.label %}<span style="font-size:0.875rem;font-weight:500;">{{ link.label }}</span>{% endif %}</a></li>{% endif %}{% endfor %}
</ul>
</div>

View File

@ -1,6 +0,0 @@
{# stats override — oversized tabular figures over a hairline, no count-up JS. #}
<section class="cm-section {{ class }}"><div class="cm-content-well">
{% if heading %}<h2 class="cm-heading" style="font-size: clamp(1.75rem, 3vw, 2.25rem); margin: 0 0 0.75rem 0;"><span class="cm-heading-h2-accent">{{ heading }}</span></h2>{% endif %}
{% if intro %}<p class="cm-lede" style="max-width: 60ch; margin: 0 0 2.5rem 0; color: hsl(var(--muted-foreground));">{{ intro }}</p>{% endif %}
{% if items %}<div class="cm-grid {% if columns == 2 %}cm-grid-2{% elif columns == 3 %}cm-grid-3{% else %}cm-grid-4{% endif %}">{% for item in items %}<div class="cm-hairline-top" style="padding-top: 1.25rem;"><div class="cm-stat-figure numeric-tabular" style="font-size: clamp(2.5rem, 5vw, 3.5rem);">{% if item.prefix %}{{ item.prefix }}{% endif %}{{ item.value }}{% if item.suffix %}{{ item.suffix }}{% endif %}</div>{% if item.label %}<div class="cm-stat-label">{{ item.label }}</div>{% endif %}</div>{% endfor %}</div>{% endif %}
</div></section>

View File

@ -1,12 +0,0 @@
{# team override — leadership grid, square-framed portraits, restrained social row. #}
<section class="cm-section {{ class }}"><div class="cm-content-well">
{% if heading %}<h2 class="cm-heading" style="font-size: clamp(1.75rem, 3vw, 2.25rem); margin: 0 0 0.75rem 0;"><span class="cm-heading-h2-accent">{{ heading }}</span></h2>{% endif %}
{% if intro %}<p class="cm-lede" style="max-width: 60ch; margin: 0 0 2.5rem 0; color: hsl(var(--muted-foreground));">{{ intro }}</p>{% endif %}
{% if members %}<div class="{% if layout == "list" %}cm-stack{% elif layout == "single" %}cm-measure{% else %}cm-leadership-grid{% endif %}">{% for member in members %}<div class="cm-feature">
{% if member.photo %}<div class="cm-media-frame" style="aspect-ratio: 1 / 1; background: hsl(var(--muted)); border: 1px solid hsl(var(--border)); overflow: hidden; margin-bottom: 1rem;">{% img member.photo alt=member.name class="cm-team-photo" %}</div>{% endif %}
{% if member.name %}<h3 class="cm-feature-title" style="margin: 0;">{{ member.name }}</h3>{% endif %}
{% if member.role %}<div class="cm-stat-label" style="margin-top: 0.25rem;">{{ member.role }}</div>{% endif %}
{% if member.bio %}<p class="cm-text" style="font-size: 0.9375rem; line-height: 1.6; color: hsl(var(--muted-foreground)); margin-top: 0.75rem;">{{ member.bio }}</p>{% endif %}
{% if member.socials %}<div class="cm-btn-row" style="display: flex; gap: 0.75rem; margin-top: 1rem;">{% for social in member.socials %}{% if social.url %}<a href="{{ social.url }}" aria-label="Profile link" style="color: hsl(var(--muted-foreground));">::{{ social.icon }}:sm::</a>{% endif %}{% endfor %}</div>{% endif %}
</div>{% endfor %}</div>{% endif %}
</div></section>

View File

@ -1,9 +0,0 @@
{# testimonial override — accent-ruled pull quotes, optional 5-star scale. #}
<section class="cm-section {{ class }}"><div class="cm-content-well">
{% if heading %}<h2 class="cm-heading" style="font-size: clamp(1.75rem, 3vw, 2.25rem); margin: 0 0 2.5rem 0;"><span class="cm-heading-h2-accent">{{ heading }}</span></h2>{% endif %}
<div class="{% if layout == "single" %}cm-measure{% else %}cm-grid cm-grid-3{% endif %}">{% for q in quotes %}<figure class="cm-quote-block cm-card" style="margin: 0; display: flex; flex-direction: column;">
{% if q.rating %}<div class="cm-rating" aria-label="Rated {{ q.rating }} out of 5" style="display: flex; gap: 0.125rem; margin-bottom: 0.75rem;"><span style="color: {% if q.rating >= 1 %}hsl(var(--accent)){% else %}hsl(var(--muted-foreground) / 0.35){% endif %};">::lucide:star:sm::</span><span style="color: {% if q.rating >= 2 %}hsl(var(--accent)){% else %}hsl(var(--muted-foreground) / 0.35){% endif %};">::lucide:star:sm::</span><span style="color: {% if q.rating >= 3 %}hsl(var(--accent)){% else %}hsl(var(--muted-foreground) / 0.35){% endif %};">::lucide:star:sm::</span><span style="color: {% if q.rating >= 4 %}hsl(var(--accent)){% else %}hsl(var(--muted-foreground) / 0.35){% endif %};">::lucide:star:sm::</span><span style="color: {% if q.rating >= 5 %}hsl(var(--accent)){% else %}hsl(var(--muted-foreground) / 0.35){% endif %};">::lucide:star:sm::</span></div>{% endif %}
{% if q.quote %}<blockquote class="cm-pull-quote" style="flex: 1; margin: 0;">{{ q.quote }}</blockquote>{% endif %}
<figcaption class="cm-quote-cite" style="display: flex; align-items: center; gap: 0.75rem; margin-top: 1.25rem;">{% if q.avatar %}<span class="cm-avatar" style="width: 2.75rem; height: 2.75rem; border-radius: 9999px; overflow: hidden; background: hsl(var(--muted)); display: inline-flex; flex: none;">{% img q.avatar alt=q.name class="cm-avatar-img" %}</span>{% endif %}<span>{% if q.name %}<span class="cm-feature-title" style="display: block; font-size: 0.9375rem;">{{ q.name }}</span>{% endif %}{% if q.role %}<span class="cm-stat-label" style="margin-top: 0;">{{ q.role }}</span>{% endif %}</span></figcaption>
</figure>{% endfor %}</div>
</div></section>

View File

@ -1,6 +0,0 @@
{# timeline override — numbered Swiss rail (mirrors engagement_process). #}
<section class="cm-section {{ class }}"><div class="cm-content-well">
{% if heading %}<h2 class="cm-heading" style="font-size: clamp(1.75rem, 3vw, 2.25rem); margin: 0 0 0.75rem 0;"><span class="cm-heading-h2-accent">{{ heading }}</span></h2>{% endif %}
{% if intro %}<p class="cm-lede" style="max-width: 60ch; margin: 0 0 2.5rem 0; color: hsl(var(--muted-foreground));">{{ intro }}</p>{% endif %}
{% if steps %}<div>{% for step in steps %}<div class="cm-timeline-step"><div class="cm-timeline-rail"><span class="cm-timeline-node numeric-tabular">{{ forloop.Counter }}</span>{% if not forloop.Last %}<span class="cm-timeline-line" aria-hidden="true"></span>{% endif %}</div><div style="padding-top: 0.375rem;">{% if step.label %}<p class="cm-eyebrow" style="margin-bottom: 0.25rem;">{{ step.label }}</p>{% endif %}{% if step.title %}<h3 class="cm-feature-title" style="margin-top: 0;">{{ step.title }}</h3>{% endif %}{% if step.text %}<p class="cm-text" style="font-size: 0.9375rem; line-height: 1.6; color: hsl(var(--muted-foreground));">{{ step.text }}</p>{% endif %}{% if step.media %}<div class="cm-media-frame" style="aspect-ratio: 16 / 9; border: 1px solid hsl(var(--border)); overflow: hidden; margin-top: 1rem;">{% img step.media alt=step.title class="cm-timeline-media" %}</div>{% endif %}</div></div>{% endfor %}</div>{% else %}<p class="cm-stat-label">No steps configured.</p>{% endif %}
</div></section>

View File

@ -1,9 +0,0 @@
{# utility-bar override — thin Swiss top strip; left/right item groups; icon-picker fields render via ::pack:name:: shorthand. #}
{% if variant == "primary" %}{% set ubvar = "cm-utility-bar--primary" %}{% set ubsty = "background:hsl(var(--primary));color:hsl(var(--primary-foreground));" %}{% elif variant == "dark" %}{% set ubvar = "cm-utility-bar--dark" %}{% set ubsty = "background:hsl(var(--foreground));color:hsl(var(--background));" %}{% else %}{% set ubvar = "cm-utility-bar--muted" %}{% set ubsty = "background:hsl(var(--muted));color:hsl(var(--muted-foreground));border-bottom:1px solid hsl(var(--border));" %}{% endif %}
{% macro ubitem(it) %}{% if it.link %}<a href="{{ it.link }}" class="cm-utility-item" style="display:inline-flex;align-items:center;gap:0.375rem;color:inherit;text-decoration:none;">{% if it.icon %}::{{ it.icon }}:sm::{% endif %}{% if it.label %}<span>{{ it.label }}</span>{% endif %}</a>{% else %}<span class="cm-utility-item" style="display:inline-flex;align-items:center;gap:0.375rem;">{% if it.icon %}::{{ it.icon }}:sm::{% endif %}{% if it.label %}<span>{{ it.label }}</span>{% endif %}</span>{% endif %}{% endmacro %}
<div class="cm-utility-bar {{ ubvar }} {{ class }}" style="{{ ubsty }}font-size:0.75rem;font-family:var(--font-body, 'Inter', system-ui, sans-serif);">
<div class="cm-content-well cm-utility-row" style="display:flex;align-items:center;justify-content:space-between;gap:1rem;min-height:2.25rem;">
<div class="cm-utility-group" style="display:flex;align-items:center;gap:1.25rem;flex-wrap:wrap;">{% for it in leftItems %}{{ ubitem(it) }}{% endfor %}</div>
<div class="cm-utility-group" style="display:flex;align-items:center;gap:1.25rem;flex-wrap:wrap;justify-content:flex-end;">{% for it in rightItems %}{{ ubitem(it) }}{% endfor %}</div>
</div>
</div>

View File

@ -1,11 +0,0 @@
{# video-embed override — click-to-load facade; carries data-bn-video / data-video-url / data-video-media so the host facade JS binds. #}
{% if aspect == "4:3" %}{% set vratio = "4 / 3" %}{% elif aspect == "1:1" %}{% set vratio = "1 / 1" %}{% elif aspect == "21:9" %}{% set vratio = "21 / 9" %}{% else %}{% set vratio = "16 / 9" %}{% endif %}
<figure class="{{ class }}" style="margin:2rem 0;">
<div class="cm-video-frame" data-bn-video data-video-url="{{ url|default:'' }}" data-video-media="{% if media %}{{ media|mediaURL }}{% endif %}" style="position:relative;width:100%;overflow:hidden;border:1px solid hsl(var(--border));border-radius:4px;background:hsl(var(--muted));aspect-ratio:{{ vratio }};">
<button type="button" data-bn-video-play class="cm-video-play" aria-label="Play video{% if title %}: {{ title }}{% endif %}" style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;width:100%;height:100%;border:0;background:none;cursor:pointer;">
{% if poster %}{% img poster alt=title|default:"" class="cm-video-poster" %}{% endif %}
<span class="cm-video-play-badge" style="position:relative;display:inline-flex;align-items:center;justify-content:center;width:4rem;height:4rem;border-radius:4px;background:hsl(var(--accent));color:hsl(var(--accent-foreground));">::lucide:play:lg::</span>
</button>
</div>
{% if caption %}<figcaption class="cm-stat-label" style="margin-top:0.5rem;text-align:center;">{{ caption }}</figcaption>{% endif %}
</figure><style>.cm-video-frame .cm-video-poster{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}</style>