{% extends "base.html" %}
{% block title %}{{ person.name }}{% endblock %}
{#- Unlike the other pages, this one is meant to be found. -#}
{% block meta %}
{%- if !person.summary.is_empty() %}
<meta name="description" content="{{ person.summary }}">
{%- endif %}
{% endblock %}
{% block header_search %}{% include "work-search.html" %}{% endblock %}
{% block header_actions %}
<form method="get" action="/{{ orcid }}" class="flex items-center gap-1">
{%- include "filter-fields.html" %}
{%- if !q.is_empty() %}
<input type="hidden" name="q" value="{{ q }}">
{%- endif %}
<select name="lang" data-language aria-label="{{ t.language }}" class="rounded-md border bg-background px-2 py-1 text-sm">
{%- for option in languages %}
<option value="{{ option.code() }}"{% if option.code() == lang.code() %} selected{% endif %}>{{ option.name() }}</option>
{%- endfor %}
</select>
<noscript><button type="submit" class="rounded-md border px-2 py-1 text-sm hover:bg-muted">{{ t.apply }}</button></noscript>
</form>
{% endblock %}
{#- Laid out as a person's page on commonmeta.org (dragoman's App.svelte and
PersonCard.svelte): the person, their affiliations, then the works with
their facets beside them. A facet value is a link and a facet a <details>,
so all of it works without JavaScript; with it, htmx replaces only the list
of works. Buttons that need JavaScript show only with it (`.js`). -#}
{% block content %}
<div class="space-y-2">
<div class="border border-border rounded-md overflow-hidden">
<div class="px-5 py-3 border-b border-border bg-muted/50">
<h2 class="text-lg font-semibold text-primary">{{ t.person }}</h2>
</div>
<div class="flex items-start gap-3 px-5 py-4 bg-background">
<div class="flex-1 min-w-0 space-y-0.5">
<h1 class="text-lg font-semibold leading-snug">{{ person.name }}</h1>
<dl class="mt-2 space-y-3 text-sm">
{%- if !person.given_name.is_empty() %}
<div>
<dt class="font-semibold text-foreground">{{ t.given_name }}</dt>
<dd class="text-muted-foreground">{{ person.given_name }}</dd>
</div>
{%- endif %}
{%- if !person.family_name.is_empty() %}
<div>
<dt class="font-semibold text-foreground">{{ t.family_name }}</dt>
<dd class="text-muted-foreground">{{ person.family_name }}</dd>
</div>
{%- endif %}
{%- if !person.additional_names.is_empty() %}
<div>
<dt class="font-semibold text-foreground">{{ t.additional_names }}</dt>
<dd class="text-muted-foreground">{{ person.additional_names }}</dd>
</div>
{%- endif %}
{%- if !person.description.is_empty() %}
<div>
<dd class="text-muted-foreground line-clamp-6">{{ person.description }}</dd>
</div>
{%- endif %}
{%- if !person.urls.is_empty() %}
<div>
<dt class="font-semibold text-foreground">{{ t.websites }}</dt>
<dd class="text-muted-foreground">
{%- for link in person.urls %}
{%- if !loop.first %} · {% endif -%}
<a href="{{ link.url }}" target="_blank" rel="noreferrer" class="hover:underline">{{ link.name }}</a>
{%- endfor -%}
</dd>
</div>
{%- endif %}
{%- if !person.country.is_empty() %}
<div>
<dt class="font-semibold text-foreground">{{ t.country }}</dt>
<dd class="text-muted-foreground">{{ person.country }}</dd>
</div>
{%- endif %}
</dl>
<p class="text-sm text-muted-foreground/60 mt-2">{{ works_count }}</p>
<a href="{{ person.orcid_url }}" target="_blank" rel="noreferrer" class="text-sm text-primary hover:underline inline-flex items-center gap-1.5 pt-0.5 wrap-anywhere max-w-full">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="14" height="14" role="img" aria-label="ORCID" class="shrink-0"><path fill="#A6CE39" d="M256 128c0 70.7-57.3 128-128 128C57.3 256 0 198.7 0 128 0 57.3 57.3 0 128 0c70.7 0 128 57.3 128 128z"/><path fill="#fff" d="M86.3 186.2H70.9V79.1h15.4v48.4zM108.9 79.1h41.6c39.6 0 57 28.3 57 53.6 0 27.5-21.5 53.6-56.8 53.6h-41.8zm15.4 93.3h24.5c34.9 0 42.9-26.5 42.9-39.7 0-21.5-13.7-39.7-43.7-39.7h-23.7zM88.7 56.8c0 5.5-4.5 10.1-10.1 10.1s-10.1-4.6-10.1-10.1c0-5.6 4.5-10.1 10.1-10.1s10.1 4.6 10.1 10.1z"/></svg>
{{- person.orcid_url -}}
</a>
</div>
<div class="flex flex-col gap-0.5 shrink-0">
<button type="button" data-copy="{{ person.json }}" aria-label="{{ t.copy_citation }}" title="{{ t.copy_citation }}" class="hidden [.js_&]:inline-flex items-center justify-center h-7 w-7 rounded-md text-muted-foreground hover:bg-muted hover:text-foreground">
{% include "copy-icons.html" %}
</button>
</div>
</div>
</div>
{%- if !person.affiliations.is_empty() %}
<div class="border border-border rounded-md overflow-hidden">
<div class="px-5 py-3 border-b border-border bg-muted/50">
<h2 class="text-base font-semibold">{{ t.affiliations }}</h2>
</div>
<div class="bg-background">
{%- for affiliation in person.affiliations %}
<div class="px-5 py-4 space-y-0.5">
<p class="text-sm font-medium inline-flex items-center gap-0.5">
{%- if let Some(ror) = affiliation.ror %}
<a href="{{ ror }}" target="_blank" rel="noreferrer" class="hover:underline">{{ affiliation.name }}</a>{% include "ror-icon.html" %}
{%- else %}
{{ affiliation.name }}
{%- endif %}
</p>
{%- if !affiliation.role.is_empty() %}
<p class="text-sm text-muted-foreground">{{ affiliation.role }}</p>
{%- endif %}
{%- if !affiliation.dates.is_empty() %}
<div class="text-sm text-muted-foreground">{{ affiliation.dates }}</div>
{%- endif %}
</div>
{%- endfor %}
</div>
</div>
{%- endif %}
{#- Rendered alone for htmx (see ui::profile_page), which swaps it in place of
itself. It stays where it is on the page, as the list does on
commonmeta.org: `scrollIntoViewOnBoost` is off in base.html. -#}
{% block works %}
<section id="works" hx-boost="true" hx-target="#works" hx-swap="outerHTML" hx-indicator="#works"
class="pt-4 transition-opacity [&.htmx-request]:opacity-50 {% if !facets.is_empty() %}lg:grid lg:grid-cols-[13rem_1fr] lg:gap-x-8 lg:items-start{% endif %}">
{%- if !chips.is_empty() && !facets.is_empty() %}
<div class="hidden lg:block" aria-hidden="true"></div>
{%- endif %}
{%- if !chips.is_empty() %}
{#- What the list is narrowed by, at every width: the sidebar is gone below
lg, and this is how a filter is seen and cleared on a phone. -#}
<div class="flex flex-wrap items-center gap-1.5 mb-3">
<span class="text-xs font-semibold uppercase tracking-wide text-muted-foreground">{{ t.filters }}</span>
{%- for chip in chips %}
<a href="{{ chip.href }}" aria-label="{{ chip.label }} — {{ t.clear_filters }}" class="inline-flex items-center gap-1.5 rounded-full border border-primary/30 bg-primary/10 px-2.5 py-1 text-xs font-medium text-foreground transition-colors hover:border-primary/60">
{%- if !chip.heading.is_empty() %}
<span class="text-muted-foreground">{{ chip.heading }}</span>
{%- endif %}
{{ chip.label }}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" class="w-3 h-3 opacity-60"><path d="M18 6 6 18M6 6l12 12"/></svg>
</a>
{%- endfor %}
<a href="{{ clear_href }}" class="ml-1 text-xs text-primary hover:underline">{{ t.clear_filters }}</a>
</div>
{%- endif %}
{%- if !facets.is_empty() %}
<aside class="hidden lg:block sticky top-20 self-start" aria-label="{{ t.filters }}">
{%- if let Some(value) = featured %}
<a href="{{ value.href }}"{% if value.picked %} aria-current="true"{% endif %}
class="flex items-start gap-2 rounded px-1.5 py-1 -mx-1.5 mb-3 text-sm transition-colors {% if value.picked %}bg-muted font-medium text-foreground{% else %}text-muted-foreground hover:bg-muted hover:text-foreground{% endif %}">
<span aria-hidden="true" class="mt-[3px] shrink-0 w-3.5 h-3.5 rounded-sm border flex items-center justify-center {% if value.picked %}border-primary bg-primary text-primary-foreground{% else %}border-muted-foreground/40{% endif %}">
{%- if value.picked %}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" class="w-2.5 h-2.5"><path d="M20 6 9 17l-5-5"/></svg>
{%- endif %}
</span>
<span class="flex-1 min-w-0 break-words">{{ value.label }}</span>
<span class="shrink-0 text-xs tabular-nums opacity-70 mt-px">{{ value.count }}</span>
</a>
{%- endif %}
{%- if facets.len() > 1 %}
<div class="hidden [.js_&]:flex justify-end mb-3">
<button type="button" data-facets-toggle data-open-label="{{ t.open_all }}" data-close-label="{{ t.close_all }}" class="text-xs text-muted-foreground hover:text-foreground transition-colors">{{ t.open_all }}</button>
</div>
{%- endif %}
{%- for facet in facets %}
<details data-facet="{{ facet.key }}" class="group mb-5"{% if facet.picked %} open{% endif %}>
<summary class="w-full flex items-center gap-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground mb-2 hover:text-foreground transition-colors cursor-pointer list-none [&::-webkit-details-marker]:hidden">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true" class="w-3 h-3 shrink-0 transition-transform -rotate-90 group-open:rotate-0"><path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"/></svg>
<span class="flex-1 text-left">{{ facet.heading }}</span>
</summary>
<ul class="space-y-0.5 text-sm">
{%- for value in facet.values %}
<li>
<a href="{{ value.href }}"{% if value.picked %} aria-current="true"{% endif %}
class="flex items-start gap-2 rounded px-1.5 py-1 -mx-1.5 transition-colors {% if value.picked %}bg-muted font-medium text-foreground{% else %}text-muted-foreground hover:bg-muted hover:text-foreground{% endif %}">
<span aria-hidden="true" class="mt-[3px] shrink-0 w-3.5 h-3.5 rounded-sm border flex items-center justify-center {% if value.picked %}border-primary bg-primary text-primary-foreground{% else %}border-muted-foreground/40{% endif %}">
{%- if value.picked %}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" class="w-2.5 h-2.5"><path d="M20 6 9 17l-5-5"/></svg>
{%- endif %}
</span>
<span class="flex-1 min-w-0 break-words">{{ value.label }}</span>
<span class="shrink-0 text-xs tabular-nums opacity-70 mt-px">{{ value.count }}</span>
</a>
</li>
{%- endfor %}
</ul>
</details>
{%- endfor %}
</aside>
{%- endif %}
<div class="min-w-0">
<div class="border border-border rounded-md overflow-hidden">
<div class="px-5 py-3 border-b border-border bg-muted/50 flex items-baseline gap-3 flex-wrap">
<h2 class="text-base font-semibold">{{ total }}</h2>
{%- if let Some(page_of) = page_of %}
<span class="ml-auto text-sm text-muted-foreground">{{ page_of }}</span>
{%- endif %}
</div>
{%- if works.is_empty() %}
<p class="px-5 py-4 bg-background text-sm text-muted-foreground">
{%- if chips.is_empty() %}{{ t.no_works }}{% else %}{{ t.no_results }}{% endif -%}
</p>
{%- endif %}
<ol class="bg-background">
{%- for work in works %}
<li class="flex items-start gap-3 px-5 py-4 border-b border-border last:border-b-0">
<span class="text-xs text-muted-foreground min-w-5 shrink-0 pt-0.5 text-right tabular-nums">{{ lang.count(start + loop.index) }}.</span>
<div class="flex-1 min-w-0 space-y-0.5">
<p class="text-base font-semibold leading-snug">
{{- work.title -}}
</p>
<p class="text-sm text-muted-foreground">
{{- work.kind }}
{%- if !work.date.is_empty() %} {{ t.published }} {{ work.date }}{% endif %}
{%- if !work.container.is_empty() %} {{ t.in_container }} <em>{{ work.container }}</em>{% endif %}
{%- if !work.language.is_empty() %} ({{ work.language }}){% endif -%}
</p>
{%- if !work.authors.is_empty() %}
<p class="text-sm text-muted-foreground leading-relaxed">{{ work.authors }}</p>
{%- endif %}
{%- if !work.description.is_empty() %}
<p class="text-sm text-muted-foreground line-clamp-3 mt-3 mb-3">{{ work.description }}</p>
{%- endif %}
{%- if !work.license.is_empty() %}
<p class="text-sm text-muted-foreground/60">{% if work.is_license %}{{ t.license }} {% endif %}{% if let Some(url) = work.license_url %}<a href="{{ url }}" target="_blank" rel="noreferrer" class="hover:underline">{{ work.license }}</a>{% else %}{{ work.license }}{% endif %}.
{%- if !work.license_icons.is_empty() %}
{%- if let Some(url) = work.license_url %}
<a href="{{ url }}" target="_blank" rel="noreferrer" aria-label="{{ work.license }}" class="inline-flex items-center gap-0.5 align-text-bottom hover:opacity-70 transition-opacity">
{%- for icon in work.license_icons %}{% include "cc-icon.html" %}{% endfor -%}
</a>
{%- else %}
<span class="inline-flex items-center gap-0.5 align-text-bottom">
{%- for icon in work.license_icons %}{% include "cc-icon.html" %}{% endfor -%}
</span>
{%- endif %}
{%- endif -%}
</p>
{%- endif %}
{%- if let Some(link) = work.link %}
<a href="{{ link }}" target="_blank" rel="noreferrer" class="text-sm text-primary hover:underline flex items-center gap-1.5 pt-0.5 wrap-anywhere max-w-full">
{%- if work.has_doi %}{% include "doi-icon.html" %}{% endif %}{{ link -}}
</a>
{%- endif %}
</div>
<div class="flex flex-col gap-0.5 shrink-0">
<button type="button" data-copy="{{ work.citation }}" aria-label="{{ t.copy_citation }}" title="{{ t.copy_citation }}" class="hidden [.js_&]:inline-flex items-center justify-center h-7 w-7 rounded-md text-muted-foreground hover:bg-muted hover:text-foreground">
{% include "copy-icons.html" %}
</button>
</div>
</li>
{%- endfor %}
</ol>
{%- if pager.len() > 1 %}
<div class="px-5 pb-4">
<nav aria-label="{{ t.pagination }}" class="mt-4 overflow-x-auto">
<div class="w-max mx-auto flex items-center border border-border rounded-lg bg-background overflow-hidden divide-x divide-border shadow-sm">
{%- if let Some(href) = previous_href %}
<a href="{{ href }}" aria-label="{{ t.previous_page }}" class="inline-flex items-center justify-center h-9 min-w-10 px-2 text-xs transition-colors hover:bg-muted"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5"/></svg></a>
{%- endif %}
{%- for cell in pager %}
{%- if let Some(href) = cell.href %}
<a href="{{ href }}"{% if cell.current %} aria-current="page"{% endif %} class="inline-flex items-center justify-center h-9 min-w-10 px-2 text-xs transition-colors tabular-nums {% if cell.current %}bg-muted font-medium{% else %}hover:bg-muted{% endif %}">{{ cell.label }}</a>
{%- else %}
<span class="inline-flex items-center justify-center h-9 min-w-10 px-2 text-xs text-muted-foreground select-none">{{ cell.label }}</span>
{%- endif %}
{%- endfor %}
{%- if let Some(href) = next_href %}
<a href="{{ href }}" aria-label="{{ t.next_page }}" class="inline-flex items-center justify-center h-9 min-w-10 px-2 text-xs transition-colors hover:bg-muted"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5"/></svg></a>
{%- endif %}
</div>
</nav>
</div>
{%- endif %}
{#- A plain form, not htmx's: it downloads a file. -#}
<form method="get" action="/{{ orcid }}/export" hx-boost="false" data-export class="border-t border-border px-4 py-3 bg-muted flex items-center gap-2 flex-wrap">
{%- include "filter-fields.html" %}
{%- if !q.is_empty() %}
<input type="hidden" name="q" value="{{ q }}">
{%- endif %}
<input type="hidden" name="lang" value="{{ lang.code() }}">
<select name="format" data-export-format aria-label="{{ t.export_format }}" class="w-40 h-8 rounded-md border bg-background px-2 text-xs">
{%- for format in formats %}
<option value="{{ format.value }}"{% if format.value == default_format %} selected{% endif %}>
{%- if let Some(label) = format.label %}{{ label }}{% else %}{{ t.formatted_citation }}{% endif -%}
</option>
{%- endfor %}
</select>
<select name="style" data-export-style aria-label="{{ t.citation_style }}" class="flex-1 min-w-36 h-8 rounded-md border bg-background px-2 text-xs">
{%- for style in styles %}
<option value="{{ style.0 }}">{{ style.1 }}</option>
{%- endfor %}
</select>
<button type="submit" class="inline-flex items-center h-8 gap-1.5 rounded-md bg-primary px-3 text-xs font-medium text-primary-foreground hover:opacity-90">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true" class="w-3.5 h-3.5 shrink-0"><path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3"/></svg>
{{- t.export -}}
</button>
<button type="button" data-export-copy data-copied-label="{{ t.copied }}" class="hidden [.js_&]:inline-flex items-center h-8 gap-1.5 rounded-md border bg-background px-3 text-xs font-medium hover:bg-muted">
{% include "copy-icons.html" %}<span>{{ t.copy }}</span>
</button>
</form>
</div>
</div>
</section>
{%- if fragment %}
{% include "work-search.html" %}
{%- endif %}
{% endblock %}
</div>
{% endblock %}