---
layout: layouts/base.njk
withBlogSidebar: true
---
{# AI metadata compatibility: support nested ai object plus legacy top-level keys #}
{% set aiMeta = ai or {} %}
{% set aiTextLevel = aiTextLevel or ai_text_level or aiMeta.textLevel or aiMeta.aiTextLevel or "0" %}
{% set aiCodeLevel = aiCodeLevel or ai_code_level or aiMeta.codeLevel or aiMeta.aiCodeLevel or "0" %}
{% set aiTools = aiTools or ai_tools or aiMeta.aiTools or aiMeta.tools %}
{% set aiDescription = aiDescription or ai_description or aiMeta.aiDescription or aiMeta.description %}
{% set aiUsed = (aiTextLevel and aiTextLevel !== "0") or (aiCodeLevel and aiCodeLevel !== "0") %}

<article class="h-entry post{% if aiUsed %} h-ai-usage{% endif %}" x-data="lightbox" @keydown.window="onKeydown($event)" data-ai-text-level="{{ aiTextLevel }}" data-ai-code-level="{{ aiCodeLevel or '0' }}" data-ai-used="{% if aiUsed %}true{% else %}false{% endif %}"{% if aiTools %} data-ai-tools="{{ aiTools }}"{% endif %}>
  {# Support both camelCase (Indiekit Eleventy preset) and underscore (legacy) property names #}
  {% set bookmarkedUrl = bookmarkOf or bookmark_of %}
  {% set likedUrl = likeOf or like_of %}
  {% set replyTo = inReplyTo or in_reply_to %}
  {% set repostedUrl = repostOf or repost_of %}

  {% if title and not repostedUrl %}
  <h1 class="p-name text-2xl sm:text-3xl font-bold text-surface-900 dark:text-surface-100 mb-3 sm:mb-4">{{ title }}</h1>
  {% else %}
  <div class="flex items-center gap-2 mb-1">
    <span class="text-base font-medium text-surface-600 dark:text-surface-400">
      {% if replyTo %}&#8617; Reply{% elif likedUrl %}&#9829; Like{% elif repostedUrl %}&#9851; Repost{% elif bookmarkedUrl %}&#128278; Bookmark{% else %}&#9998; Note{% endif %}
    </span>
  </div>
  {% endif %}

  <div class="post-meta mb-4 sm:mb-6 flex flex-col gap-1 items-start">
    <div class="flex flex-wrap items-center gap-2">
      <span class="font-mono text-sm">Planted: </span><time-difference><time class="dt-published font-mono text-sm" datetime="{{ date.toISOString() }}">
        {{ date | dateDisplay }}
      </time></time-difference>
      {% if category | withoutGardenTags %}
      <ul class="post-categories flex flex-wrap gap-2 list-none p-0 m-0" role="list" aria-label="Categories">
        {# Handle both string and array categories #}
        {% if category is string %}
        <li><a href="/categories/{{ category | nestedSlugify }}/" class="p-category">{{ category }}</a></li>
        {% else %}
        {% for cat in (category | withoutGardenTags) %}
        <li><a href="/categories/{{ cat | nestedSlugify }}/" class="p-category">{{ cat }}</a></li>
        {% endfor %}
        {% endif %}
      </ul>
      {% endif %}
      {# Digital Garden stage badge #}
      {% include "components/garden-badge.njk" %}
    </div>
    {% if updated %}
    <div class="flex items-center gap-2">
      <span class="font-mono text-sm">Tended: </span><time-difference><time class="dt-updated font-mono text-sm" datetime="{{ updated | isoDate }}">
        {{ updated | dateDisplay }}
      </time></time-difference>
    </div>
    {% endif %}
  </div>

  {# Bridgy syndication content - controls what gets posted to social networks #}
  {# For interaction types (bookmarks, likes, replies, reposts), include the target URL #}
  {% set bridgySummary = description or summary or (content | ogDescription(280)) %}
  {% set interactionUrl = bookmarkedUrl or likedUrl or replyTo or repostedUrl %}

  {% if bridgySummary or interactionUrl %}
  <p class="p-summary e-bridgy-mastodon-content e-bridgy-bluesky-content hidden">{% if bookmarkedUrl %}🔖 {{ bookmarkedUrl }}{% if bridgySummary %} - {{ bridgySummary }}{% endif %}{% elif likedUrl %}❤️ {{ likedUrl }}{% if bridgySummary %} - {{ bridgySummary }}{% endif %}{% elif replyTo %}↩️ {{ replyTo }}{% if bridgySummary %} - {{ bridgySummary }}{% endif %}{% elif repostedUrl %}🔁 {{ repostedUrl }}{% if bridgySummary %} - {{ bridgySummary }}{% endif %}{% else %}{{ bridgySummary }}{% endif %}</p>
  {% endif %}

  {# Render photo(s) from frontmatter for photo posts - use eleventy:ignore to skip image transform #}
  {% if photo %}
  <div class="photo-gallery mb-6">
    {% for img in photo %}
      {% set photoUrl = img.url %}
      {% if photoUrl and photoUrl[0] != '/' and 'http' not in photoUrl %}
        {% set photoUrl = '/' + photoUrl %}
      {% endif %}
      <img src="{{ photoUrl }}" alt="{{ img.alt | default('Photo from: ' + title) }}" class="u-photo rounded-lg max-w-full" loading="lazy" eleventy:ignore>
    {% endfor %}
  </div>
  {% endif %}

  {% set isInteraction = replyTo or likedUrl or repostedUrl or bookmarkedUrl %}
  {% set hasContent = content and content | striptags | trim %}
  {% set contentWarning = contentWarning or content_warning %}
  {% if contentWarning %}
  <details class="content-warning mb-4">
    <summary class="cursor-pointer inline-flex items-center gap-2 px-3 py-2 rounded-lg bg-amber-100 dark:bg-amber-900/30 text-amber-800 dark:text-amber-200 text-sm font-medium">
      <span>&#9888; {{ contentWarning }}</span>
      <span class="text-xs text-amber-600 dark:text-amber-400">(click to show)</span>
    </summary>
    <div class="e-content prose prose-lg prose-surface dark:prose-invert max-w-none mt-4{% if isInteraction and hasContent %} border-l-[3px] border-l-accent-500 dark:border-l-accent-400 pl-4{% endif %}">
      {{ content | safe }}
    </div>
  </details>
  {% else %}
  <div class="e-content prose prose-lg prose-surface dark:prose-invert max-w-none{% if isInteraction and hasContent %} border-l-[3px] border-l-accent-500 dark:border-l-accent-400 pl-4{% endif %}">
    {{ content | safe }}
  </div>
  {% endif %}

  {# Rich reply context with h-cite microformat #}
  {% include "components/reply-context.njk" %}

  {# AI usage disclosure — articles and notes only #}
  {% if '/articles/' in page.url or '/notes/' in page.url %}
  <details class="mt-4 text-xs text-surface-700 dark:text-surface-300">
    <summary class="cursor-pointer hover:text-surface-700 dark:hover:text-surface-200 list-none flex items-center gap-1.5 [&::-webkit-details-marker]:hidden">
      <svg class="w-3.5 h-3.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 3.104v5.714a2.25 2.25 0 01-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 014.5 0m0 0v5.714a2.25 2.25 0 00.659 1.591L19 14.5M14.25 3.104c.251.023.501.05.75.082M19 14.5l-2.47 2.47a2.25 2.25 0 01-1.59.659H9.06a2.25 2.25 0 01-1.591-.659L5 14.5m14 0V17a2 2 0 01-2 2H7a2 2 0 01-2-2v-2.5"/>
      </svg>
      <span>AI:
        Text {% if aiTextLevel === "0" %}None{% elif aiTextLevel === "1" %}Editorial{% elif aiTextLevel === "2" %}Co-drafted{% elif aiTextLevel === "3" %}AI-generated{% endif %}{% if aiCodeLevel %} · Code {% if aiCodeLevel === "0" %}Human{% elif aiCodeLevel === "1" %}AI-assisted{% elif aiCodeLevel === "2" %}AI-generated{% endif %}{% endif %}{% if aiTools %} · {{ aiTools }}{% endif %}
      </span>
    </summary>
    {% if aiDescription %}
    <p class="mt-1 pl-5">{{ aiDescription }}</p>
    {% endif %}
    <p class="mt-1 pl-5"><a href="/ai/" class="hover:text-accent-700 dark:hover:text-accent-300 underline">Learn more about AI usage on this site</a></p>
    <div class="hidden">
      <span class="p-ai-text-level">{{ aiTextLevel }}</span>
      <span class="p-ai-code-level">{{ aiCodeLevel or "0" }}</span>
      {% if aiTools %}
      <span class="p-ai-tools">{{ aiTools }}</span>
      {% endif %}
      {% if aiDescription %}
      <span class="e-ai-description">{{ aiDescription }}</span>
      {% endif %}
    </div>
  </details>
  {% endif %}

  {# See Also — explicit related: frontmatter + in-text links to other blog posts #}
  {% set _seeAlso = page.inputPath | seeAlsoLinks(related) %}
  {% if _seeAlso.length > 0 %}
  <section class="post-related mt-6 pt-5 border-t border-surface-200 dark:border-surface-700">
    <h2 class="text-xs font-semibold text-surface-500 dark:text-surface-400 uppercase tracking-widest mb-3">See Also</h2>
    <ul class="space-y-1.5 list-none p-0 m-0">
      {% for relUrl in _seeAlso %}
      {% set _relPost = collections.posts | postByUrl(relUrl) %}
      <li>
        <a href="{{ relUrl }}" class="text-sm text-accent-700 dark:text-accent-300 hover:underline">
          {{ _relPost.data.title if _relPost else relUrl }}
        </a>
      </li>
      {% endfor %}
    </ul>
  </section>
  {% endif %}

  {# Linked From — posts that link to this one, computed from raw source files #}
  {% set _backlinks = collections.posts | backlinksWith(page.url) %}
  {% if _backlinks and _backlinks.length > 0 %}
  <section class="post-backlinks mt-6 pt-5 border-t border-surface-200 dark:border-surface-700">
    <h2 class="text-xs font-semibold text-surface-500 dark:text-surface-400 uppercase tracking-widest mb-3">Linked From</h2>
    <ul class="space-y-1.5 list-none p-0 m-0">
      {% for post in _backlinks %}
      <li>
        <a href="{{ post.url }}" class="text-sm text-accent-700 dark:text-accent-300 hover:underline">
          {{ post.data.title or "Untitled" }}
        </a>
      </li>
      {% endfor %}
    </ul>
  </section>
  {% endif %}

  {# Pending syndication targets (for services like IndieNews that require u-syndication before webmention) #}
  {% if mpSyndicateTo %}
  <div class="hidden">
    {% for url in mpSyndicateTo %}
      {% if "news.indieweb.org" in url %}
      <a href="{{ url }}" class="u-syndication" rel="syndication">IndieNews</a>
      {% endif %}
    {% endfor %}
  </div>
  {% endif %}

  {# Syndication Footer - shows where this post was also published #}
  {# Separate self-hosted AP URLs from external syndication targets #}
  {% set externalSyndication = [] %}
  {% set selfHostedApUrl = "" %}
  {% if syndication %}
    {% for url in syndication %}
      {% if url.indexOf(site.url) == 0 %}
        {% set selfHostedApUrl = url %}
      {% else %}
        {% set externalSyndication = (externalSyndication.push(url), externalSyndication) %}
      {% endif %}
    {% endfor %}
  {% endif %}
  {% if externalSyndication.length or selfHostedApUrl %}
  <footer class="post-footer mt-8 pt-6 border-t border-surface-200 dark:border-surface-700">
    <div class="flex flex-wrap items-center gap-4">
      <span class="text-sm text-surface-700 dark:text-surface-300">Also on:</span>
      <div class="flex flex-wrap gap-3">
        {# Fediverse remote interaction button (self-hosted ActivityPub) #}
        {% if selfHostedApUrl %}
        <span x-data="fediverseInteract('{{ selfHostedApUrl }}', 'interact')" class="inline-flex">
          <a class="u-syndication inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-[#a730b8]/10 text-[#a730b8] hover:bg-[#a730b8]/20 transition-colors text-sm font-medium cursor-pointer"
             href="{{ selfHostedApUrl }}"
             rel="syndication"
             title="Interact from your fediverse instance (Shift+click to change)"
             @click="handleClick($event)">
            <svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M13.09 4.43L24 10.73v2.51L13.09 19.58v-2.51L21.83 12 13.09 6.98v-2.55zM13.09 9.49L17.44 12l-4.35 2.51V9.49z"/><path d="M10.91 4.43L0 10.73v2.51l8.74-5.03v10.09l2.18 1.28V4.43zM6.56 12L2.18 14.51l4.35 2.51V12z"/></svg>
            <span>Fediverse</span>
          </a>
          {% set modalTitle = "Fediverse Interaction" %}
          {% set modalDescription = "Choose your instance to like, boost, or reply." %}
          {% include "components/fediverse-modal.njk" %}
        </span>
        {% endif %}

        {# External syndication buttons #}
        {% for url in externalSyndication %}
          {% if "bsky.app" in url or "bluesky" in url %}
          <a class="u-syndication inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-[#0085ff]/10 text-[#0085ff] hover:bg-[#0085ff]/20 transition-colors text-sm font-medium" href="{{ url }}" target="_blank" rel="noopener syndication" title="View on Bluesky">
            <svg class="w-4 h-4" viewBox="0 0 568 501" fill="currentColor" aria-hidden="true">
              <path d="M123.121 33.664C188.241 82.553 258.281 181.68 284 234.873c25.719-53.192 95.759-152.32 160.879-201.21C491.866-1.611 568-28.906 568 57.947c0 17.346-9.945 145.713-15.778 166.555-20.275 72.453-94.155 90.933-159.875 79.748C507.222 323.8 536.444 388.56 473.333 453.32c-119.86 122.992-172.272-30.859-185.702-70.281-2.462-7.227-3.614-10.608-3.631-7.733-.017-2.875-1.169.506-3.631 7.733-13.43 39.422-65.842 193.273-185.702 70.281-63.111-64.76-33.89-129.52 80.986-149.071-65.72 11.185-139.6-7.295-159.875-79.748C9.945 203.659 0 75.291 0 57.946 0-28.906 76.135-1.612 123.121 33.664Z"/>
            </svg>
            <span>Bluesky</span>
          </a>
          {% elif site.feeds.mastodon.instance and site.feeds.mastodon.instance in url %}
          <a class="u-syndication inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-[#6364ff]/10 text-[#6364ff] hover:bg-[#6364ff]/20 transition-colors text-sm font-medium" href="{{ url }}" target="_blank" rel="noopener syndication" title="View on Mastodon">
            <svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
              <path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.668 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/>
            </svg>
            <span>Mastodon</span>
          </a>
          {% elif "linkedin.com" in url %}
          <a class="u-syndication inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-[#0a66c2]/10 text-[#0a66c2] hover:bg-[#0a66c2]/20 transition-colors text-sm font-medium" href="{{ url }}" target="_blank" rel="noopener syndication" title="View on LinkedIn">
            <svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
              <path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
            </svg>
            <span>LinkedIn</span>
          </a>
          {% elif "news.indieweb.org" in url %}
          <a class="u-syndication inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-[#ff5c00]/10 text-[#ff5c00] hover:bg-[#ff5c00]/20 transition-colors text-sm font-medium" href="{{ url }}" target="_blank" rel="noopener syndication" title="View on IndieNews">
            <svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
              <path d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 1-2 2zm0 0a2 2 0 0 1-2-2v-9c0-1.1.9-2 2-2h2"/><line x1="10" y1="6" x2="18" y2="6"/><line x1="10" y1="10" x2="18" y2="10"/><line x1="10" y1="14" x2="14" y2="14"/>
            </svg>
            <span>IndieNews</span>
          </a>
          {% elif "/@" in url %}
          {# Mastodon-compatible instance (any URL with /@username pattern) #}
          {% set mastoHandle = url | replace("https://", "") | replace("http://", "") %}
          {% set mastoHandle = mastoHandle.split("/")[0] + "/" + mastoHandle.split("/")[1] %}
          <a class="u-syndication inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-[#6364ff]/10 text-[#6364ff] hover:bg-[#6364ff]/20 transition-colors text-sm font-medium" href="{{ url }}" target="_blank" rel="noopener syndication" title="View on Mastodon">
            <svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
              <path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.668 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/>
            </svg>
            <span>{{ mastoHandle }}</span>
          </a>
          {% else %}
          <a class="u-syndication inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-surface-100 dark:bg-surface-800 text-surface-600 dark:text-surface-300 hover:bg-surface-200 dark:hover:bg-surface-700 transition-colors text-sm font-medium" href="{{ url }}" target="_blank" rel="noopener syndication">
            <svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
              <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
              <polyline points="15 3 21 3 21 9"/>
              <line x1="10" y1="14" x2="21" y2="3"/>
            </svg>
            <span>{{ url | replace("https://", "") | truncate(20) }}</span>
          </a>
          {% endif %}
        {% endfor %}
      </div>
    </div>
  </footer>
  {% endif %}

  <a class="u-url" href="{{ page.url }}" hidden>Permalink</a>

  {# Author h-card for IndieWeb authorship #}
  <span class="p-author h-card hidden">
    <a class="p-name u-url" href="{{ site.author.url }}">{{ site.author.name }}</a>
    <img class="u-photo" src="{{ site.author.avatar }}" alt="{{ site.author.name }}" hidden>
  </span>

  {# Pagefind filter metadata — hidden elements for search filtering #}
  <div hidden>
    {% if replyTo %}<span data-pagefind-filter="type">Reply</span>
    {% elif likedUrl %}<span data-pagefind-filter="type">Like</span>
    {% elif repostedUrl %}<span data-pagefind-filter="type">Repost</span>
    {% elif bookmarkedUrl %}<span data-pagefind-filter="type">Bookmark</span>
    {% elif photo and photo.length %}<span data-pagefind-filter="type">Photo</span>
    {% elif title %}<span data-pagefind-filter="type">Article</span>
    {% else %}<span data-pagefind-filter="type">Note</span>
    {% endif %}
    <span data-pagefind-filter="year">{{ date | date("yyyy") }}</span>
    <span data-pagefind-filter="ai-text-level">{{ aiTextLevel }}</span>
    <span data-pagefind-filter="ai-code-level">{{ aiCodeLevel or "0" }}</span>
    <span data-pagefind-filter="ai-used">{% if aiUsed %}yes{% else %}no{% endif %}</span>
    {% if gardenStage %}
    <span data-pagefind-filter="garden-stage">{{ gardenStage }}</span>
    {% endif %}
    {% if category | withoutGardenTags %}
      {% if category is string %}
      <span data-pagefind-filter="category">{{ category }}</span>
      {% else %}
      {% for cat in (category | withoutGardenTags) %}
      <span data-pagefind-filter="category">{{ cat }}</span>
      {% endfor %}
      {% endif %}
    {% endif %}
  </div>

  {# JSON-LD Structured Data for SEO #}
  {# Handle photo as potentially an array #}
  {% set postImage = photo %}
  {% if postImage %}
    {# If photo is an array, use first element (check if first element looks like a URL) #}
    {% if postImage[0] and (postImage[0] | length) > 10 %}
      {% set postImage = postImage[0] %}
    {% endif %}
  {% endif %}
  {% if not postImage or postImage == "" %}
    {% set postImage = image or (content | extractFirstImage) %}
  {% endif %}
  {% set postDesc = description | default(content | ogDescription(160)) %}
  <script type="application/ld+json">
  {
    "@context": "https://schema.org",
    "@type": "Article",
    "headline": {{ (title or "Untitled") | dump | safe }},
    "url": "{{ site.url }}{{ page.url }}",
    "mainEntityOfPage": {
      "@type": "WebPage",
      "@id": "{{ site.url }}{{ page.url }}"
    },
    "datePublished": "{{ date.toISOString() }}",
    "dateModified": "{{ (updated | isoDate) if updated else date.toISOString() }}",
    "author": {
      "@type": "Person",
      "name": "{{ site.author.name }}",
      "url": "{{ site.author.url }}"
    },
    "publisher": {
      "@type": "Organization",
      "name": "{{ site.name }}",
      "url": "{{ site.url }}",
      "logo": {
        "@type": "ImageObject",
        "url": "{{ site.url }}/images/og-default.png"
      }
    },
    "description": {{ postDesc | dump | safe }}{% if postImage and postImage != "" and (postImage | length) > 10 %},
    "image": ["{% if postImage.startsWith('http') %}{{ postImage }}{% elif '/' in postImage and postImage[0] == '/' %}{{ site.url }}{{ postImage }}{% else %}{{ site.url }}/{{ postImage }}{% endif %}"]{% endif %},
    "usageInfo": "{{ site.url }}/ai"{% set _aiParts = [] %}{% if aiTextLevel %}{% set _textLabel %}{% if aiTextLevel === "0" %}None{% elif aiTextLevel === "1" %}Editorial assistance{% elif aiTextLevel === "2" %}Co-drafting{% elif aiTextLevel === "3" %}AI-generated{% endif %}{% endset %}{% set _aiParts = (_aiParts.push("Text: " + _textLabel), _aiParts) %}{% endif %}{% if aiCodeLevel %}{% set _codeLabel %}{% if aiCodeLevel === "0" %}Human-written{% elif aiCodeLevel === "1" %}AI-assisted{% elif aiCodeLevel === "2" %}AI-generated{% endif %}{% endset %}{% set _aiParts = (_aiParts.push("Code: " + _codeLabel), _aiParts) %}{% endif %}{% if aiTools %}{% set _aiParts = (_aiParts.push("Tools: " + aiTools), _aiParts) %}{% endif %},
    "creativeWorkStatus": "{{ _aiParts | join(', ') }}",
    "additionalProperty": [
      { "@type": "PropertyValue", "name": "aiTextLevel", "value": "{{ aiTextLevel }}" },
      { "@type": "PropertyValue", "name": "aiCodeLevel", "value": "{{ aiCodeLevel or '0' }}" }{% if aiTools %},
      { "@type": "PropertyValue", "name": "aiTools", "value": {{ aiTools | dump | safe }} }{% endif %}
    ]{% if aiDescription %},
    "abstract": {{ aiDescription | dump | safe }}{% endif %}
  }
  </script>

  {# Lightbox overlay for article images #}
  <template x-teleport="body">
    <div x-show="open" x-transition.opacity.duration.200ms
         role="dialog" aria-modal="true" aria-label="Image viewer"
         class="fixed inset-0 z-[9999] flex items-center justify-center bg-black/90 backdrop-blur-sm"
         @click.self="close()">
      <button x-ref="closeBtn" @click="close()" class="absolute top-4 right-4 text-white/70 hover:text-white text-3xl leading-none p-2 z-10" aria-label="Close lightbox">&times;</button>
      <template x-if="images.length > 1">
        <button @click="prev()" class="absolute left-4 top-1/2 -translate-y-1/2 text-white/70 hover:text-white text-4xl leading-none p-2 z-10" aria-label="Previous">&lsaquo;</button>
      </template>
      <template x-if="images.length > 1">
        <button @click="next()" class="absolute right-4 top-1/2 -translate-y-1/2 text-white/70 hover:text-white text-4xl leading-none p-2 z-10" aria-label="Next">&rsaquo;</button>
      </template>
      <img :src="src" :alt="alt" class="max-h-[90vh] max-w-[90vw] object-contain" @click.stop>
      <div x-show="alt" x-text="alt" class="absolute bottom-4 left-1/2 -translate-x-1/2 text-white/80 text-sm max-w-2xl text-center px-4 py-2 bg-black/50 rounded-lg"></div>
    </div>
  </template>
</article>


{# Comments section #}
{% include "components/comments.njk" %}

{# Webmentions display - likes, reposts, replies #}
{% include "components/webmentions.njk" %}

{# Post Navigation - Previous/Next #}
{% include "components/post-navigation.njk" %}
