Alex Dunmow 1451f06d9f feat(noir): WO-TF-022 Wave A — detective/true-crime dossier rebuild
Re-persona noir from photography to the locked detective/true-crime
direction: case-file panels, typewriter type, redaction bars, evidence
board, film grain. High-key gallery (light) / darkroom black + amber
safelight (dark).

- 49 mandatory builtin overrides (chrome, marketing, primitives,
  commerce, blog family, auth, page-suggestions), re-skinned from the
  frozen builtin defaults with every field/provider/tag/JS preserved;
  legacy button/card field reads reconciled to the new contract.
- 7 page templates (default, full-width, landing, article, blog-index,
  contact, auth) + themed email wrapper.
- 4 persona blocks (case_file, evidence_board, redacted_quote,
  case_dossier); old photography blocks deleted (now overrides).
- 5 dual-mode presets (19 tokens, light+dark tuned).
- Bundled OFL fonts (Source Serif 4, IBM Plex Mono) + licenses;
  RECOMMENDED_FONTS/ICONS updated; required_icon_packs=lucide.
- Master pages + seed demo ("The Cold File": home/about/blog x3/contact
  with case_tips data table + row_inserted email-admins workflow/login).
- noir CSS vocabulary via manifest css.input_css_append; MID motion
  (scroll-reveal + gallery lightbox, reduced-motion honored, no canvas).

Verified: make build exit 0; check-safety 32 checks 19 ok 13 skip -> OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 12:48:44 +08:00

12 lines
1.6 KiB
Plaintext

{# image override — square evidence frame with a typed mono caption. Fields mirror builtin: src, alt, caption, size, align, aspectRatio, objectFit. Media resolved via {% img %} (the media filter is not registered in the block render path). #}
{% with size=size|default:"full" align=align|default:"center" objectFit=objectFit|default:"cover" %}
<figure data-block-override="noir:image" class="my-8 flex-1 {% if align == "left" %}flex justify-start{% elif align == "right" %}flex justify-end{% else %}flex justify-center{% endif %}{% if class %} {{ class }}{% endif %}">
<div class="noir-file p-2 {% if size == "xs" %}max-w-[200px] w-full{% elif size == "sm" %}max-w-[400px] w-full{% elif size == "md" %}max-w-[600px] w-full{% elif size == "lg" %}max-w-[800px] w-full{% else %}w-full{% endif %}">
<div class="relative overflow-hidden bg-muted {% if aspectRatio == "square" %}aspect-square{% elif aspectRatio == "video" %}aspect-video{% elif aspectRatio == "portrait" %}aspect-[3/4]{% elif aspectRatio == "wide" %}aspect-[21/9]{% endif %}">
{% if objectFit == "contain" %}{% img src alt=alt class="w-full h-full object-contain" layout=size %}{% elif objectFit == "fill" %}{% img src alt=alt class="w-full h-full object-fill" layout=size %}{% elif objectFit == "none" %}{% img src alt=alt class="w-full h-full object-none" layout=size %}{% else %}{% img src alt=alt class="w-full h-full object-cover" layout=size %}{% endif %}
</div>
</div>
{% if caption %}<figcaption class="mt-3 text-center noir-mono text-sm" style="color:hsl(var(--muted-foreground));">{{ caption }}</figcaption>{% endif %}
</figure>
{% endwith %}