Skip to content

feat(templates): redesign the marketing template with a photo-led demo - #3969

Merged
khoinguyenpham04 merged 33 commits into
mainfrom
noah/marketing-template-halden
Oct 11, 2026
Merged

khoinguyenpham04 merged 33 commits into
mainfrom
noah/marketing-template-halden

Conversation

@khoinguyenpham04

@khoinguyenpham04 khoinguyenpham04 commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

What does this PR do?

Redesigns the marketing template (templates/marketing, synced to templates/marketing-cloudflare) and replaces the Acme placeholder copy with a realistic demo company: Halden, a fictional energy-intelligence product for commercial buildings. The seed now downloads four Unsplash photos for the home page, so a new site starts with a finished, photo-led landing page instead of placeholder SaaS copy. Content that used to be hard-coded is now edited in the admin: every page, including contact, is built from blocks, and the header button and footer headings come from menus. To make those editable without the API, the admin's menu editor can now rename a menu and set a menu item's CSS classes. An editability audit then closed the remaining gaps: pages created in the admin get a URL, SEO text and share images come from the content, block fields are labelled for editors, and the blocks editor no longer overflows its column.

Design

  • Instrument Serif for display type over Inter, a warm neutral palette with ink as the brand colour and one amber accent, all through light-dark() tokens with the no-light-dark() fallback kept in sync.
  • Structure comes from rounded surfaces and soft shadows rather than lines: a floating glass header pill, near-full-bleed "sheets" (a sunken features panel with a warm glow, a night panel for stats, a full-photo closing call to action), and soft cards.
  • Frosted glass caption chips on photos, driven by an optional image_caption field, a logo marquee, and a giant cropped serif wordmark at the bottom of the footer.
  • Motion is CSS only: a staggered hero entrance on the text, a scroll-linked zoom as photos come into view (only where animation-timeline: view() is supported), and the marquee. All of it switches off under prefers-reduced-motion: reduce.
  • Shared classes live in Base.astro (.sheet, .section, .section-headline, .eyebrow, .btn-*, .card, .media, .media-chip, .tick and friends), so new blocks can reuse them.

Blocks

Block Change
marketing_hero Optional eyebrow and eyebrow_url (an announcement pill) and image_caption. The photo runs below the text as a near full-width sheet. Without an image the hero is text only; the isometric hero-visual.svg fallback is removed
marketing_features Optional eyebrow; nine more icon keys (building, leaf, gauge, thermometer, plug, bell, file, sun, trend). The subheadline renders as a muted second sentence of the headline
marketing_testimonials The first quote is featured in large serif type; the rest become cards. Initials replace missing avatars, and role and company are joined with a comma instead of " at "
marketing_pricing Optional plan badge. A highlighted plan sits on the night panel with its badge; plans stored without a badge value keep "Most popular", and an emptied badge shows none. Plan names become h2 when the block has no headline
marketing_faq Optional subheadline, a two-column layout, and each block is its own exclusive accordion
marketing_logos (new) Company names or logo images in a marquee with a pause button. Names without a logo render as typographic wordmarks
marketing_split (new) Eyebrow, headline, body, checklist, link, and a photo with an optional caption chip; image_first swaps the sides on desktop
marketing_stats (new) Headline numbers on the night panel
marketing_cta (new) A closing call to action over a photo with a scrim, or on the night panel without one
marketing_contact (new) Contact cards with an icon, title, description and link. It replaces the contact page's hard-coded email list, so /contact now renders only its blocks, like home and pricing
marketing_text (new) Long-form Portable Text (headings, lists, links, quotes and images) in a readable column, for pages such as About or Privacy

Every block lists headline first and anchor_id last, because the admin labels a collapsed block with its first filled-in text field (it showed "platform" and "faq" before). Block fields have no help text in EmDash, so the labels carry it: "Section ID (lowercase, for links like /#pricing)", "Primary button link (starts with /, https://, mailto:, tel: or #)", "Second sentence (shown muted after the headline)" and so on. FAQ answers, quotes and card descriptions keep the paragraphs an editor types.

Pages, SEO and site identity

  • src/pages/[slug].astro renders every page except Home, replacing pricing.astro and contact.astro (same URLs). Pages created or renamed in the admin used to return 404 even after publishing; now they render, and the automatic redirect from a renamed page's old URL lands on a working page. A page without a hero gets its title as the h1. A page with no blocks shows visitors its title and shows editors a hint with an Edit page link.
  • Meta description: the SEO panel, else the page's first hero subheadline, else the tagline. Share image: the SEO panel image, else the first hero's photo, else the default social image. These replace hard-coded Halden descriptions in each route.
  • Home is titled after the site (it used to share as "Home"). Browser titles use the Settings › SEO title separator, which the seed sets to an em dash so the admin shows the value the site uses.
  • A cleared tagline stays cleared instead of bringing back Halden's, and an empty site title shows "My Site".

Admin (@emdash-cms/admin: a minor changeset for the menu editor)

  • A pencil button next to the menu's title (named "Rename menu" for assistive technology) opens a dialog that changes the label of the locale version being edited. It trims the label and closes without a request when nothing changed. The Create menu hint now says some themes show the label on the site.
  • Add Custom Link and Edit Menu Item have a CSS classes field. Values are trimmed, clearing the field removes the classes, and an unchanged value isn't sent. Items with classes show them next to their URL in the list.
  • Both use the existing menu API routes (PUT /_emdash/api/menus/:name and PUT /_emdash/api/menus/:name/items/:id), so the server is unchanged.
  • A long repeater item title, such as a testimonial quote, used to widen the blocks editor and push Add block and each block's duplicate and delete buttons under the settings panel. Main fixed the same column in fix(admin): fit blocks field cards on phone-width screens #4012, so this PR now only adds a layout test for that repeater case, in both text directions.

Backward compatibility

  • Existing block types only gain optional fields and extra select options. Re-applying the new seed over a site seeded from main with --on-conflict update updates the five existing types in place and creates the six new ones, with no BLOCK_TYPE_VERSION_CONFLICT. --on-conflict skip also applies cleanly.
  • Field order and labels aren't part of the block type contract, so the reordered and relabelled fields apply in place. I re-applied the final seed over a database seeded from main with both --on-conflict skip and update; both completed without errors.
  • The link fields this PR adds (the hero's announcement link, the split block's link and both CTA band buttons) accept a bare / and reject paths starting with /\, which browsers read as //. The hero's two button links keep main's pattern, which needs a character after the slash, because changing an existing field's pattern is a breaking block type change.
  • Content without the new fields still renders: a hero without an image, features without an eyebrow, three equal testimonials, plans without a badge.
  • Every seeded image field stays optional in the markup, so a failed download during setup leaves a working page.

Other changes worth knowing about:

  • Header and footer: a mobile menu (closes on Escape with focus returned, outside click, link click and resize) and a skip link. The hard-coded "Get Started" button and the footer's Product/Company/Support headings are gone: a primary menu item with the CSS class button renders as the header button (the seed's Book a demo), and each footer column uses its menu's label, so both can be edited in the admin's menu editor and translated per locale. They come from the menus Base.astro already loads, so no queries are added.
  • 404: restyled; it stays in code as the one system page.
  • Queries: /, /pricing and /contact run the same queries as before. An unknown single-segment URL now looks the slug up before rendering the 404 page, one extra lookup on a miss, which is the cost of the catch-all route (the starter template has the same).
  • Contrast: muted text is #78716c (4.8:1 on white cards) and #8f8982 in dark mode (5.2:1).
  • Images: the hero, split and CTA photos pass sizes that match their crops, and the hero photo isn't faded in, so it doesn't hold back largest contentful paint.
  • Docs: AGENTS-template.md and both generated AGENTS.md files, TEMPLATES.md, the README blurb and the block map in docs/.../themes/creating-themes.mdx describe the eleven blocks, page routing and the SEO fallbacks, the menus guide covers renaming and CSS classes, and the API reference lists getContentSeo in the site-template API contract. The smoke test expects the new hero headlines.
  • Photos: all four come from Unsplash (free licence). The testimonials use initials, because the quoted people are fictional.

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes (plus astro check for templates/marketing and templates/marketing-cloudflare: 0 errors, 0 warnings)
  • pnpm lint passes (pnpm lint:json: 0 diagnostics)
  • pnpm test passes (or targeted tests for my change): the smoke suite for both marketing templates (-t "templates/marketing": 28 passed, including real photo downloads, re-seeding and boot checks), plus tests/unit/seed/validate.test.ts, tests/integration/transfer/template-targets.test.ts and tests/integration/seed-portable-text-keys.test.ts (103 passed), and the admin's MenuEditor, MenuList and BlocksField tests (40 passed)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable): three menu editor tests (rename, add a link with classes, clear classes) and a blocks editor layout test for long repeater item titles (fails without the minmax(0, 1fr) column, in both text directions); for the template, the existing seed and smoke tests cover the new seed. The smoke expectations use the new headlines, and the draft preview check now previews Pricing, because Home's admin title no longer renders
  • User-visible strings in the admin UI are wrapped for translation (if applicable)
  • I have added and reviewed the user-facing changeset (if this PR changes a published package): a minor (menu editor) for @emdash-cms/admin; templates are not published packages
  • New features link to an approved Discussion: https://github.com/emdash-cms/emdash/discussions/...
  • I have included screenshots below if this PR changes the UI

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Opus 5.5

Screenshots / test output

Seeded sample content on the local dev server. README screenshots in assets/templates/marketing/latest/ are regenerated.

Desktop (1440px), first screen

Before After
Home Home page before: Acme placeholder copy with a purple-to-pink gradient headline, gradient buttons and an isometric illustration Home page after: a floating glass header, an announcement pill, the serif headline "Every building, running at its best.", the lead text with two buttons, and the top of a full-width atrium photo
Pricing Pricing page before: centred heading and three white pricing cards with a gradient badge Pricing page after: the serif headline "Pricing that pays for itself." over three plans, with the highlighted Portfolio plan on a dark panel with a "Most popular" badge
Contact Contact page before: centred heading and three cards with gradient icon tiles and Acme addresses Contact page after: the serif headline "Talk to the Halden team", its subheadline, and three contact cards from the new contact options block with halden.example addresses

Home, section by section (after)

Features on a sunken rounded panel with a warm glow: a two-tone serif headline and six white cards with icon tiles Text and image block: eyebrow, serif headline, body, checklist and link beside a corridor photo with a glass caption chip reading "Floor 3 · 21.4 °C · CO₂ 612 ppm"
Stats on a dark rounded panel with a warm glow: four large serif figures with short labels Testimonials: a featured serif quote with an amber quote mark and an initials avatar, then two quote cards
FAQ: the serif heading "Questions, answered." beside rounded question rows with plus icons Closing call to action over a timber atrium photo with a dark scrim, a serif headline and light and glass buttons

Footer: wordmark and tagline, three link columns, the copyright, a Light/Dark/System switch, "Powered by EmDash", and a giant cropped "Halden" wordmark

Phone (390px)

Home before Home after Menu open
Home page on a 390px phone before: the Acme header with wrapped links, the gradient headline and buttons Home page on a 390px phone after: the header pill with a Book a demo button and menu button, the announcement pill, the serif headline, lead text, stacked buttons and the top of the photo The mobile menu open: an opaque rounded panel under the header listing Platform, Monitoring, Customers, Pricing and Contact

Dark mode

Features Pricing
Features in dark mode: the sunken panel with a faint warm glow and dark cards Pricing in dark mode: the serif headline and three dark plans, with the highlighted plan's light button

Admin menu editor

Menu Rename menu
The primary menu in the admin's menu editor: a pencil Rename menu button beside the title "Primary Navigation", and the Book a demo item showing its button class next to its URL The Rename menu dialog with a Label field set to "Primary Navigation", a hint that some themes also show the label on the site, and Cancel and Save buttons

The Edit Menu Item dialog for Book a demo: Label, URL, Target, a CSS classes field containing "button" with a hint about separating class names with spaces, and Parent

Admin blocks editor (Home page)

The column fit in the after shot comes from #4012 on main. The field order, labels and headline summaries come from this PR.

Before After
The Home page editor before: the content column runs under the settings panel, the hero block starts with an empty Anchor ID field, and its collapsed summary shows the announcement text The Home page editor after: the column fits beside the settings panel, the hero block starts with its Headline, labels read "Primary button text" and "Primary button link (starts with /, https://, mailto:, tel: or #)", and the summary shows the headline

Home's blocks collapsed: each card is labelled with its headline, such as "Every building, running at its best." under Hero and "One platform for every building." under Features

Replace the Acme placeholder with a photo-led demo for Halden, a
fictional energy-intelligence startup: Instrument Serif display type
over Inter, a warm neutral palette with an ink brand and one amber
accent, a floating glass header, rounded near-full-bleed sheets,
glass caption chips on photos, a logo marquee and a footer wordmark.

Add four block types (logo wall, text and image, stats, call to
action) and optional fields on the existing five (hero announcement
and image caption, features eyebrow and nine icons, pricing badge, FAQ
subheadline). Existing block contracts only gain optional fields, so
re-seeding with --on-conflict update stays compatible. The seed
downloads four Unsplash photos for the home page.
… the hero photo in

The hero photo was invisible for the first 270ms of its entrance animation, which delayed largest contentful paint. Photos also used the default 100vw sizes: the hero and call to action under-fetched for their mobile crops and the half-width split photos over-fetched on desktop.
…rim intact on small screens

Stat values kept a 56px floor in two columns and overflowed at 320px, long single words in display headings could widen the page, and the mobile CTA scrim sat in a selector list with :dir(), so browsers without :dir() dropped it and left white text on a clear photo.
The marquee loops indefinitely and only paused on hover, so keyboard and touch readers had no way to stop it. Logos now fade out before they reach the button.
…ng headline

The seeded pricing page went from the hero h1 straight to h3 plan names.
…s with reduced motion

The right-to-left mask rule outranked the reduced-motion reset, so the static logo row kept an uneven fade.
…ons block

The contact page rendered its email cards from a hard-coded list, so editors could not change them in the admin. A new marketing_contact block holds the cards (icon, title, description and link), the contact page now renders only its blocks like home and pricing, and the seeded page drops the second heading in favour of a fuller hero subheadline.
…s from menus

The header's Book a demo button and the footer's Product, Company and Support headings were hard-coded in the layout, so they could not be edited or translated. A primary menu item with the CSS class button now renders as the header button, and each footer column uses its menu's label. Both come from the menus Base already loads, so no queries are added.
The contact options link was a plain string, so a bare email address saved as a broken relative link. The url field type accepts http, https, mailto and tel links or a site-relative path, and repeater subfields support it, unlike validation patterns.
Every primary menu item with the button class stayed in the header, so a second one overflowed the bar at 320px. On narrow screens only the first stays and the rest move into the menu panel.
The admin menu editor doesn't edit CSS classes or menu labels, and re-applying the seed doesn't rename existing menus.
At 1024px and wider, the giant wordmark's text box rose over the legal row and covered the Light, Dark and System buttons, so the theme switcher couldn't be clicked.
…oto captions

The amber dot on the announcement pill and eyebrows and the pulsing green dot on photo captions read as filler. Labels and captions are now plain text, the --color-live token goes with the dot, and the amber accent is left on the testimonial quote mark.
@changeset-bot

changeset-bot Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 04054f1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@emdash-cms/admin Minor
emdash Minor
@emdash-cms/cloudflare Minor
@emdash-cms/plugin-test Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/auth Minor
@emdash-cms/blocks Minor
create-emdash Minor
@emdash-cms/gutenberg-to-portable-text Minor
@emdash-cms/x402 Minor
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
docs 04054f1 Oct 08 2026, 04:48 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://noah-marketing-template-halden.try.emdashcms.com, https://noah-marketing-template-halden-emdash-playground.emdash-cms.workers.dev (commit 04054f1)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://b26fac5d.try.emdashcms.com, https://b26fac5d-emdash-playground.emdash-cms.workers.dev 04054f1 2026-10-08T16:49:09.458Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://e483a622.try.emdashcms.com, https://e483a622-emdash-playground.emdash-cms.workers.dev cf37095 2026-10-08T13:38:13.913Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://12dffcc8.try.emdashcms.com, https://12dffcc8-emdash-playground.emdash-cms.workers.dev be2005c 2026-10-08T12:06:56.057Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://33dec2ba.try.emdashcms.com, https://33dec2ba-emdash-playground.emdash-cms.workers.dev e5b9605 2026-10-08T11:56:11.967Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://73b3d7a8.try.emdashcms.com, https://73b3d7a8-emdash-playground.emdash-cms.workers.dev 0d7b3e7 2026-10-07T17:09:21.256Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://7a677fc7.try.emdashcms.com, https://7a677fc7-emdash-playground.emdash-cms.workers.dev 77a3430 2026-10-07T16:26:44.207Z Visit the dashboard ↗

The marketing template uses each footer menu's label as its column
heading and renders a primary menu item with the `button` class as a
header button, but the admin's menu editor could change neither. A
pencil button next to the menu title now renames the locale version
being edited, and the add and edit item dialogs gain a CSS classes
field, with each item's classes shown next to its URL. Both use the
existing menu API routes.
The blocks field's root grid had one implicit auto column, so a long
truncated header (a testimonial quote as a repeater item title, or a
block summary) set its minimum width. The column grew past the editor,
pushing Add block and each block's duplicate and delete buttons under
the settings panel on desktop and off screen on phones. A
minmax(0, 1fr) column keeps it inside, and long headers end in an
ellipsis.
… from the CMS

Only Home, Pricing and Contact had routes, so a page created or renamed
in the admin returned 404 even after publishing. A [slug] route now
renders any page through a shared PageView, which adds a title heading
to pages without a hero and shows editors an empty-page hint with an
edit link.

Halden copy no longer hides in code: meta descriptions and share images
come from the page's first hero, cleared taglines stay cleared, an
empty site title falls back to "My Site", the title separator follows
Settings > SEO, and Home shares under the site title. The smoke test
previews Pricing, since Home's admin title no longer renders.
…ext block

The admin labels a collapsed block with its first filled-in text field,
so blocks read "platform" or "faq" instead of their headlines. Every
block now lists headline first and anchor_id last, and since block
fields have no help text, the labels carry it: "Section ID (lowercase,
for links like /#pricing)", button text and link labels that name the
accepted link formats, and notes on how fields render. Field order and
labels aren't part of the block type contract, so sites seeded from
main re-apply the seed cleanly.

A new marketing_text block holds Portable Text for pages such as About
or Privacy. The seed also sets the em dash title separator, so the
admin shows the value the site uses.
The admin edits plain-text fields in a multi-line box, but FAQ answers,
quotes and card descriptions rendered as one paragraph, so blank lines
an editor typed were lost. They now split into paragraphs like the
Text and image and call to action bodies. An emptied plan badge also
stays empty instead of falling back to "Most popular"; plans stored
before the badge field existed keep that fallback.
…rsed

heroSeoDefaults passed the hero image's src and previewUrl to new URL()
unguarded. Block image values written through the REST API, MCP or an
import are plain strings, so a malformed one made every request for that
page return 500. A value that can't be parsed now yields no share image,
and the site's default og:image applies.
The announcement link, split link and CTA band button fields copied the
hero buttons' pattern, which needs a character after the slash, so a link
to the home page failed validation although the labels say links start
with /. These fields are new, so their pattern can still change without a
new block type version; once released, any pattern change except removal
is breaking. The new pattern also rejects paths starting with /\, which
browsers treat as protocol-relative. The hero's two button links exist on
main and keep their pattern.
…:dir()

Browsers without :dir() support (Chrome before 120) drop a whole selector
list that contains it, so reduced-motion visitors kept the clipped
single-line marquee. :is() takes a forgiving selector list, as the CTA
block already does.
Astro decodes the path with decodeURI, which leaves reserved characters
such as #, ?, & and + percent-encoded in params. decodeSlug decodes them
before the lookup, like the starter and blog routes and the blocks guide.
@khoinguyenpham04 khoinguyenpham04 added the bot:review Trigger an emdashbot code review on this PR label Oct 8, 2026
@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Oct 8, 2026
@khoinguyenpham04 khoinguyenpham04 added the bot:review Trigger an emdashbot code review on this PR label Oct 8, 2026

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a well-scoped, substantial redesign of the marketing template plus the small admin affordances needed to make the new design fully editable. The approach is sound: it converts more hard-coded surface into blocks/menus/settings, keeps existing block types additive-only, preserves the logged-out query shape (the [slug] catch-all adds one content lookup on 404s as noted), and adds the missing menu-rename and CSS-classes UI through the existing menu API.

I reviewed the diff, the changed components, the seed schema, the page/query code, and the updated docs. The code is clean: fields are optional, unknown block data is handled, URLs are run through sanitizeHref(), light-dark() fallbacks are kept in sync, logical CSS properties are used, and the new admin strings are Lingui-wrapped. The BlocksField grid-cols-1 fix matches the regression test, and the MenuEditor tests cover rename/add/clear classes. Docs accurately describe the new blocks, routing, SEO fallbacks, and menu editor changes.

The only administrative note is that the PR template’s “New features link to an approved Discussion” checkbox is unchecked and no discussion URL is included. If this is maintainer-driven and already approved, that link is a template formality; otherwise it should be provided before merge.

@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Oct 8, 2026
@github-actions github-actions Bot added review/approved Approved; no new commits since and removed review/needs-review No maintainer or bot review yet labels Oct 8, 2026
…te-halden

# Conflicts:
#	packages/admin/src/components/BlocksField.tsx
#	packages/admin/tests/components/BlocksField.layout.test.tsx
Main fixed the same implicit-column overflow in #4012 with an identical
minmax(0, 1fr) column, and its blocks-field-phone-width changeset
describes the fix. This branch no longer changes BlocksField, so its own
changeset would add a duplicate CHANGELOG entry. The repeater layout test
stays as extra coverage.
@github-actions github-actions Bot added review/needs-rereview New commits since the last review and removed review/approved Approved; no new commits since labels Oct 8, 2026
@github-actions github-actions Bot added review/approved Approved; no new commits since and removed review/needs-rereview New commits since the last review labels Oct 9, 2026
@khoinguyenpham04
khoinguyenpham04 merged commit 01e1f12 into main Oct 11, 2026
69 of 71 checks passed
@khoinguyenpham04
khoinguyenpham04 deleted the noah/marketing-template-halden branch October 11, 2026 21:08
@emdashbot emdashbot Bot mentioned this pull request Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants