feat(publications): improve structure + awards/funding/license metadata

Restructure `publication` frontmatter from a flat Markdown string into a
structured object (name, short_name, volume, issue, pages, publisher),
resolved through a new `functions/resolve_publication.html` partial that
also emits a per-page warning pointing legacy sites at `hugoblox migrate
publications`. APA/MLA citation rendering and the publication metadata
row both consume the resolver - no parsing of conventional Markdown
strings at render time.

Add work-level metadata fields surfaced via a new reusable
`components/award_badge.html` partial (variants: pill, card-corner,
inline, row; level-driven icon + colour tokens; pagefind filter):

- `awards[]` - name, level (winner/runner_up/honorable_mention/nominee/
  selected/featured), issuer, date, note, url; skim-row pills above the
  featured image, full row in the metadata grid, corner badges on card
  and slides-gallery views, inline marker on citation views, pill on
  date-title-summary
- `funding[]` - funder + grant + url, rendered as a metadata grid row
- `license` - SPDX-friendly string, metadata grid row
- `peer_reviewed`, `open_access` - status pills, decoupled from
  the publication block so preprints can surface them

Demo content updated in academic-cv (and the same shape in research-lab).
This commit is contained in:
George Cushen committed 2026-05-27 03:53:21 +01:00
1 parent 64cdb4a81a
commit 26c336eb6d
12 files changed
+439 -27

No files matched your search

+33
View File
@@ -318,6 +318,39 @@
- id: publication_type
translation: Type
- id: awards
translation: Awards
- id: funding
translation: Funding
- id: license
translation: License
- id: peer_reviewed
translation: Peer-reviewed
- id: open_access
translation: Open access
- id: award_level_winner
translation: Winner
- id: award_level_runner_up
translation: Runner-up
- id: award_level_honorable_mention
translation: Honorable mention
- id: award_level_nominee
translation: Nominee
- id: award_level_selected
translation: Selected
- id: award_level_featured
translation: Featured
- id: date
translation: Date
@@ -0,0 +1,169 @@
{{/*
Award badge — single source of truth for rendering an award entry.
Args (dict):
award Award entry (dict with name, level, issuer, date, note, url).
Bare-string entries are coerced to {name: <string>}.
page Owning page (used to fall back issuer to publication, date to page date).
variant Visual style — one of:
"pill" horizontal pill (default; used in skim rows)
"card-corner" absolute-positioned corner pill (for card/grid views)
"row" full row with name + note (for metadata grid)
"inline" compact inline marker for citations
Falls back gracefully when fields are missing.
*/}}
{{- $award := .award -}}
{{- $page := .page -}}
{{- $variant := .variant | default "pill" -}}
{{/* Coerce bare-string entries to {name: ...} */}}
{{- if not (reflect.IsMap $award) -}}
{{- $award = dict "name" $award -}}
{{- end -}}
{{- $name := $award.name | default "" -}}
{{- $level := $award.level | default "winner" -}}
{{- $note := $award.note | default "" -}}
{{- $url := $award.url | default "" -}}
{{/* Issuer fallback: explicit → publication short_name → publication name → empty */}}
{{- $issuer := $award.issuer | default "" -}}
{{- if not $issuer -}}
{{- with $page -}}
{{- $pub := partial "functions/resolve_publication.html" . -}}
{{- $issuer = $pub.short_name | default $pub.name | default "" -}}
{{- end -}}
{{- end -}}
{{/* Date fallback: explicit → page date year */}}
{{- $date := $award.date | default "" -}}
{{- if not $date -}}
{{- with $page -}}
{{- with .Date -}}{{- $date = .Format "2006" -}}{{- end -}}
{{- end -}}
{{- end -}}
{{/* Icon + colour token per level */}}
{{- $icon := "trophy" -}}
{{- $tone := "amber" -}}
{{- if eq $level "winner" -}}{{- $icon = "trophy" -}}{{- $tone = "amber" -}}
{{- else if eq $level "runner_up" -}}{{- $icon = "medal" -}}{{- $tone = "slate" -}}
{{- else if eq $level "honorable_mention" -}}{{- $icon = "ribbon" -}}{{- $tone = "rose" -}}
{{- else if eq $level "nominee" -}}{{- $icon = "star" -}}{{- $tone = "sky" -}}
{{- else if eq $level "selected" -}}{{- $icon = "sparkles" -}}{{- $tone = "violet" -}}
{{- else if eq $level "featured" -}}{{- $icon = "bookmark-star" -}}{{- $tone = "emerald" -}}
{{- end -}}
{{/* Tailwind class shards keyed by tone (full classes for the extractor) */}}
{{- $bg := "" -}}{{- $text := "" -}}{{- $ring := "" -}}
{{- if eq $tone "amber" -}}
{{- $bg = "bg-amber-100 dark:bg-amber-900/40" -}}
{{- $text = "text-amber-900 dark:text-amber-200" -}}
{{- $ring = "ring-amber-500/30 dark:ring-amber-400/30" -}}
{{- else if eq $tone "slate" -}}
{{- $bg = "bg-slate-100 dark:bg-slate-800/60" -}}
{{- $text = "text-slate-900 dark:text-slate-200" -}}
{{- $ring = "ring-slate-500/30 dark:ring-slate-400/30" -}}
{{- else if eq $tone "rose" -}}
{{- $bg = "bg-rose-100 dark:bg-rose-900/40" -}}
{{- $text = "text-rose-900 dark:text-rose-200" -}}
{{- $ring = "ring-rose-500/30 dark:ring-rose-400/30" -}}
{{- else if eq $tone "sky" -}}
{{- $bg = "bg-sky-100 dark:bg-sky-900/40" -}}
{{- $text = "text-sky-900 dark:text-sky-200" -}}
{{- $ring = "ring-sky-500/30 dark:ring-sky-400/30" -}}
{{- else if eq $tone "violet" -}}
{{- $bg = "bg-violet-100 dark:bg-violet-900/40" -}}
{{- $text = "text-violet-900 dark:text-violet-200" -}}
{{- $ring = "ring-violet-500/30 dark:ring-violet-400/30" -}}
{{- else if eq $tone "emerald" -}}
{{- $bg = "bg-emerald-100 dark:bg-emerald-900/40" -}}
{{- $text = "text-emerald-900 dark:text-emerald-200" -}}
{{- $ring = "ring-emerald-500/30 dark:ring-emerald-400/30" -}}
{{- end -}}
{{/* Inline SVG icons (small, self-contained — no dependency on external icon system at render time) */}}
{{- $svg := "" -}}
{{- if eq $icon "trophy" -}}
{{- $svg = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 21h8M12 17v4M7 4h10v5a5 5 0 0 1-10 0V4ZM5 4H3v2a3 3 0 0 0 3 3M19 4h2v2a3 3 0 0 1-3 3"/></svg>` -}}
{{- else if eq $icon "medal" -}}
{{- $svg = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="15" r="6"/><path d="M8.5 9L6 2h12l-2.5 7M9 15l1 2.5L12 14l2 3.5L15 15"/></svg>` -}}
{{- else if eq $icon "ribbon" -}}
{{- $svg = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 2l3 7 7 1-5 5 1 7-6-3-6 3 1-7-5-5 7-1z"/></svg>` -}}
{{- else if eq $icon "star" -}}
{{- $svg = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26"/></svg>` -}}
{{- else if eq $icon "sparkles" -}}
{{- $svg = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.1 2.1M16.3 16.3l2.1 2.1M5.6 18.4l2.1-2.1M16.3 7.7l2.1-2.1"/></svg>` -}}
{{- else if eq $icon "bookmark-star" -}}
{{- $svg = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/><polygon points="12 7 13 9 15 9.3 13.5 11 14 13 12 12 10 13 10.5 11 9 9.3 11 9"/></svg>` -}}
{{- end -}}
{{/* Build subtitle: "Issuer · Year" or just whichever is present */}}
{{- $subtitle := "" -}}
{{- if and $issuer $date -}}
{{- $subtitle = printf "%s · %s" $issuer $date -}}
{{- else if $issuer -}}
{{- $subtitle = $issuer -}}
{{- else if $date -}}
{{- $subtitle = $date -}}
{{- end -}}
{{- $tooltip := $name -}}
{{- with $subtitle -}}{{- $tooltip = printf "%s — %s" $name . -}}{{- end -}}
{{- if $name -}}
{{- if eq $variant "pill" -}}
{{- if $url -}}
<a href="{{ $url }}" target="_blank" rel="noopener" class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold ring-1 {{ $bg }} {{ $text }} {{ $ring }} max-w-full" title="{{ $tooltip }}" data-pagefind-filter="award:{{ $level }}">
<span class="w-3.5 h-3.5 flex-shrink-0">{{ $svg | safeHTML }}</span>
<span class="truncate">{{ $name }}</span>
{{- with $subtitle }}<span class="opacity-70 font-normal hidden sm:inline">· {{ . }}</span>{{ end -}}
</a>
{{- else -}}
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold ring-1 {{ $bg }} {{ $text }} {{ $ring }} max-w-full" title="{{ $tooltip }}" data-pagefind-filter="award:{{ $level }}">
<span class="w-3.5 h-3.5 flex-shrink-0">{{ $svg | safeHTML }}</span>
<span class="truncate">{{ $name }}</span>
{{- with $subtitle }}<span class="opacity-70 font-normal hidden sm:inline">· {{ . }}</span>{{ end -}}
</span>
{{- end -}}
{{- else if eq $variant "card-corner" -}}
{{- if $url -}}
<a href="{{ $url }}" target="_blank" rel="noopener" class="absolute top-3 right-3 z-10 inline-flex items-center gap-1 px-2 py-1 rounded-full text-[11px] font-semibold shadow-sm backdrop-blur-sm ring-1 {{ $bg }} {{ $text }} {{ $ring }}" title="{{ $tooltip }}">
<span class="w-3 h-3 flex-shrink-0">{{ $svg | safeHTML }}</span>
<span class="truncate max-w-[140px]">{{ $name }}</span>
</a>
{{- else -}}
<span class="absolute top-3 right-3 z-10 inline-flex items-center gap-1 px-2 py-1 rounded-full text-[11px] font-semibold shadow-sm backdrop-blur-sm ring-1 {{ $bg }} {{ $text }} {{ $ring }}" title="{{ $tooltip }}">
<span class="w-3 h-3 flex-shrink-0">{{ $svg | safeHTML }}</span>
<span class="truncate max-w-[140px]">{{ $name }}</span>
</span>
{{- end -}}
{{- else if eq $variant "inline" -}}
{{- if $url -}}
<a href="{{ $url }}" target="_blank" rel="noopener" class="inline-flex items-center gap-1 text-xs font-medium {{ $text }}" title="{{ $tooltip }}">
<span class="w-3 h-3 flex-shrink-0">{{ $svg | safeHTML }}</span>
<span>{{ $name }}</span>
</a>
{{- else -}}
<span class="inline-flex items-center gap-1 text-xs font-medium {{ $text }}" title="{{ $tooltip }}">
<span class="w-3 h-3 flex-shrink-0">{{ $svg | safeHTML }}</span>
<span>{{ $name }}</span>
</span>
{{- end -}}
{{- else if eq $variant "row" -}}
<div class="flex items-start gap-3" data-pagefind-filter="award:{{ $level }}">
<span class="inline-flex items-center justify-center w-8 h-8 rounded-full {{ $bg }} {{ $text }} flex-shrink-0">
<span class="w-4 h-4">{{ $svg | safeHTML }}</span>
</span>
<div class="min-w-0">
<div class="font-semibold {{ $text }}">
{{- if $url -}}<a href="{{ $url }}" target="_blank" rel="noopener" class="hover:underline">{{ $name }}</a>
{{- else -}}{{ $name }}{{- end -}}
</div>
{{- with $subtitle -}}<div class="text-sm text-gray-600 dark:text-gray-400">{{ . }}</div>{{- end -}}
{{- with $note -}}<div class="text-sm text-gray-700 dark:text-gray-300 mt-1">{{ . | markdownify }}</div>{{- end -}}
</div>
</div>
{{- end -}}
{{- end -}}
@@ -0,0 +1,97 @@
{{/*
Resolve publication metadata from page params.
Supports the structured shape and warns on the legacy flat shape:
Structured: publication: { name, short_name, volume, issue, pages, publisher }
Legacy: publication: "*Journal Name*, 12(3)" + publication_short: "*JN*"
Legacy pages emit a per-page suppressible warning pointing users to the
`hugoblox migrate publications` CLI command.
Args:
A page context (`.`)
Returns dict:
name Full publication name (empty for legacy)
short_name Abbreviated name
volume Volume number as string
issue Issue number as string
pages Page range
publisher Publisher name
display Markdown-ready display string, e.g. "*Name*, 12(3), 100-120"
display_short Markdown-ready compact form (uses short_name when set)
is_legacy true if the page used the legacy flat string shape
is_empty true if no publication info is set
*/}}
{{- $page := . -}}
{{- $raw := $page.Params.publication -}}
{{- $legacy_short := $page.Params.publication_short -}}
{{- $name := "" -}}
{{- $short_name := "" -}}
{{- $volume := "" -}}
{{- $issue := "" -}}
{{- $pages := "" -}}
{{- $publisher := "" -}}
{{- $is_legacy := false -}}
{{- $is_empty := false -}}
{{- if reflect.IsMap $raw -}}
{{- $name = $raw.name | default "" -}}
{{- $short_name = $raw.short_name | default "" -}}
{{- with $raw.volume -}}{{- $volume = printf "%v" . -}}{{- end -}}
{{- with $raw.issue -}}{{- $issue = printf "%v" . -}}{{- end -}}
{{- $pages = $raw.pages | default "" -}}
{{- $publisher = $raw.publisher | default "" -}}
{{- else if $raw -}}
{{- $is_legacy = true -}}
{{- $name = $raw -}}
{{- $short_name = $legacy_short | default "" -}}
{{- partial "functions/logger.html" (dict
"page" $page
"level" "warn"
"id" (printf "pub-legacy-%s" $page.RelPermalink)
"message" (printf "%s uses the legacy flat `publication` string. Migrate to the structured shape `publication: {name, short_name, volume, issue, pages, publisher}` by running: hugoblox migrate publications" $page.RelPermalink)
) -}}
{{- else -}}
{{- $is_empty = true -}}
{{- end -}}
{{- $display := "" -}}
{{- $display_short := "" -}}
{{- if $is_legacy -}}
{{- $display = $name -}}
{{- $display_short = $short_name | default $name -}}
{{- else if not $is_empty -}}
{{- $core := "" -}}
{{- with $name -}}{{- $core = printf "*%s*" . -}}{{- end -}}
{{- with $volume -}}{{- $core = printf "%s, %s" $core . -}}{{- end -}}
{{- with $issue -}}{{- $core = printf "%s(%s)" $core . -}}{{- end -}}
{{- with $pages -}}{{- $core = printf "%s, %s" $core . -}}{{- end -}}
{{- $display = $core -}}
{{- $short_core := "" -}}
{{- if $short_name -}}
{{- $short_core = printf "*%s*" $short_name -}}
{{- else if $name -}}
{{- $short_core = printf "*%s*" $name -}}
{{- end -}}
{{- with $volume -}}{{- $short_core = printf "%s, %s" $short_core . -}}{{- end -}}
{{- with $issue -}}{{- $short_core = printf "%s(%s)" $short_core . -}}{{- end -}}
{{- with $pages -}}{{- $short_core = printf "%s, %s" $short_core . -}}{{- end -}}
{{- $display_short = $short_core -}}
{{- end -}}
{{- return (dict
"name" $name
"short_name" $short_name
"volume" $volume
"issue" $issue
"pages" $pages
"publisher" $publisher
"display" $display
"display_short" $display_short
"is_legacy" $is_legacy
"is_empty" $is_empty
) -}}
+13 -6
View File
@@ -25,12 +25,19 @@
<div class="group bg-white/90 dark:bg-zinc-900/90 backdrop-blur-sm rounded-2xl ring-1 ring-zinc-900/5 dark:ring-white/10 shadow-lg overflow-hidden transition-all duration-300 ease-out hover:shadow-xl hover:shadow-primary-500/10 hover:-translate-y-2 focus-within:ring-2 focus-within:ring-primary-500/50" role="article" aria-labelledby="card-title-{{ $item.File.UniqueID }}">
<!-- Image Section -->
<div class="relative overflow-hidden aspect-[16/9] bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-zinc-800 dark:to-zinc-900">
{{ with $item.Params.content_meta }}
{{ if .trending }}
<div class="absolute top-3 right-3 z-10 inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-[11px] font-semibold bg-black/55 backdrop-blur-sm text-white shadow-sm">
<span>{{ i18n "trending" | default "Trending" }}</span>
<span aria-hidden="true">🔥</span>
</div>
{{/* Award badge takes priority over trending — awards are higher-signal */}}
{{ $top_award := "" }}
{{ with $item.Params.awards }}{{ $top_award = index . 0 }}{{ end }}
{{ with $top_award }}
{{ partial "components/award_badge.html" (dict "award" . "page" $item "variant" "card-corner") }}
{{ else }}
{{ with $item.Params.content_meta }}
{{ if .trending }}
<div class="absolute top-3 right-3 z-10 inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-[11px] font-semibold bg-black/55 backdrop-blur-sm text-white shadow-sm">
<span>{{ i18n "trending" | default "Trending" }}</span>
<span aria-hidden="true">🔥</span>
</div>
{{ end }}
{{ end }}
{{ end }}
{{ with $resource }}
@@ -2,6 +2,9 @@
{{ $year_value := $item.Params.event_start | default $item.Date }}
{{ $year := (time $year_value).Format "2006" }}
{{ $has_attachments := partial "functions/has_attachments" $item }}
{{ $pub := partial "functions/resolve_publication.html" $item }}
{{ $top_award := "" }}
{{ with $item.Params.awards }}{{ $top_award = index . 0 }}{{ end }}
<div class="pub-list-item view-citation" style="margin-bottom: 1rem">
<i class="far fa-file-alt pub-icon" aria-hidden="true"></i>
@@ -14,10 +17,9 @@
</span>
({{- $year -}}).
<a href="{{ $item.RelPermalink }}" class="underline">{{ $item.Title }}</a>.
{{ if $item.Params.publication_short }}
{{- $item.Params.publication_short | markdownify -}}.
{{ else if $item.Params.publication }}
{{- $item.Params.publication | markdownify -}}.
{{ with $pub.display_short }}{{- . | markdownify -}}.{{ end }}
{{ with $top_award }}
<span class="ml-1">{{ partial "components/award_badge.html" (dict "award" . "page" $item "variant" "inline") }}</span>
{{ end }}
{{ if $has_attachments }}
@@ -33,12 +35,11 @@
{{ partial "page_metadata_authors" $item }}.
</span>
<a href="{{ $item.RelPermalink }}">{{ $item.Title }}</a>.
{{ if $item.Params.publication_short }}
{{- $item.Params.publication_short | markdownify -}},
{{ else if $item.Params.publication }}
{{- $item.Params.publication | markdownify -}},
{{ end }}
{{ with $pub.display_short }}{{- . | markdownify -}},{{ end }}
{{- $year -}}.
{{ with $top_award }}
<span class="ml-1">{{ partial "components/award_badge.html" (dict "award" . "page" $item "variant" "inline") }}</span>
{{ end }}
{{ if $has_attachments }}
<div class="flex flex-wrap space-x-3">
@@ -11,6 +11,11 @@
<a href="{{$item.RelPermalink}}"><span
class="absolute -inset-x-4 -inset-y-6 z-20 sm:-inset-x-6 sm:rounded-2xl"></span><span class="relative z-10">{{$item.Title}}</span></a>
</h2>
{{ with $item.Params.awards }}
<div class="relative z-10 mt-2">
{{ partial "components/award_badge.html" (dict "award" (index . 0) "page" $item "variant" "pill") }}
</div>
{{ end }}
<time
class="md:hidden relative z-10 order-first mb-3 flex items-center text-sm text-zinc-400 dark:text-zinc-500 pl-1"
datetime="{{ time.Format "2006-01-02" $displayDate }}">
@@ -56,6 +56,11 @@
</div>
{{ end }}
{{/* Top award (top-right corner) */}}
{{ with $item.Params.awards }}
{{ partial "components/award_badge.html" (dict "award" (index . 0) "page" $item "variant" "card-corner") }}
{{ end }}
{{/* Slide Preview Image - 16:9 aspect ratio */}}
<a href="{{ $present_link }}" target="_blank" rel="noopener" class="block relative aspect-video bg-gradient-to-br from-zinc-100 to-zinc-200 dark:from-zinc-800 dark:to-zinc-900 overflow-hidden" aria-label="{{ printf "%s %s" $present_fullscreen_label ($item.Title | plainify) }}">
{{ with $resource }}
+50 -2
View File
@@ -142,6 +142,15 @@
<div class="mt-3">
{{ partial "page_links_div.html" . }}
</div>
{{/* Awards skim row — visible badges above any featured image */}}
{{ with .Params.awards }}
<div class="mt-4 flex flex-wrap gap-2">
{{ range . }}
{{ partial "components/award_badge.html" (dict "award" . "page" $ "variant" "pill") }}
{{ end }}
</div>
{{ end }}
</div>
@@ -227,6 +236,7 @@
{{end}}
{{/* EVENT / PUBLICATION METADATA */}}
{{ $pub := partial "functions/resolve_publication.html" . }}
{{ if .Params.abstract | or (eq .Type "publications") | or (eq .Type "events") }}
<div class="max-w-prose grid grid-cols-1 md:grid-cols-[200px_auto] gap-4 my-6">
@@ -247,9 +257,47 @@
</div>
{{ end }}
{{ if .Params.publication }}
{{ if not $pub.is_empty }}
<div class="font-bold text-2xl">{{ i18n "publication" }}</div>
<div>{{ .Params.publication | markdownify }}</div>
<div>{{ $pub.display | markdownify }}{{ with $pub.publisher }}. {{ . | markdownify }}{{ end }}</div>
{{ end }}
{{/* Work-level flags — independent of whether a venue is set, so preprints can still surface OA/peer-review status. */}}
{{ $flags := slice }}
{{ if eq .Params.peer_reviewed true }}{{ $flags = $flags | append (i18n "peer_reviewed") }}{{ end }}
{{ if eq .Params.open_access true }}{{ $flags = $flags | append (i18n "open_access") }}{{ end }}
{{ with $flags }}
<div class="font-bold text-2xl">{{ i18n "status" | default "Status" }}</div>
<div class="flex flex-wrap gap-2">
{{ range . }}
<span class="inline-flex items-center px-2 py-0.5 rounded-full text-[11px] font-medium bg-emerald-100 text-emerald-900 dark:bg-emerald-900/40 dark:text-emerald-200 ring-1 ring-emerald-500/30">{{ . }}</span>
{{ end }}
</div>
{{ end }}
{{ with .Params.awards }}
<div class="font-bold text-2xl">{{ i18n "awards" }}</div>
<div class="space-y-3">
{{ range . }}
{{ partial "components/award_badge.html" (dict "award" . "page" $ "variant" "row") }}
{{ end }}
</div>
{{ end }}
{{ with .Params.funding }}
<div class="font-bold text-2xl">{{ i18n "funding" }}</div>
<div class="space-y-1">
{{ range . }}
<div>
{{ if .url }}<a href="{{ .url }}" target="_blank" rel="noopener" class="hover:underline">{{ .funder }}</a>{{ else }}{{ .funder }}{{ end }}{{ with .grant }} <span class="text-gray-500 dark:text-gray-400">({{ . }})</span>{{ end }}
</div>
{{ end }}
</div>
{{ end }}
{{ with .Params.license }}
<div class="font-bold text-2xl">{{ i18n "license" }}</div>
<div>{{ . }}</div>
{{ end }}
{{ if eq .Type "events" }}