{# Reusable UI macros adapted from vendored Penguin UI components.
   These are OUR adaptation layer; the byte-for-byte upstream sources live under
   penguinui-components/. Tailwind sees the full literal class strings here
   (assets/css/app.css has @source "../views"), so every branch must spell its
   classes out in full — never build class names by concatenation.

   Usage:
     {% import "macros/ui.html" as ui %}
     {{ ui::button(label=t(key="save", lang=lang)) }}                         {# default primary #}
     {{ ui::button(label="Add", attrs='hx-post="/x"' | safe) }}
     {{ ui::button(label="Cancel", variant="outline-secondary", href="/back") }}
     {{ ui::button(label="Send", size="px-6 py-2.5 text-sm") }}                        {# keep a non-default size #}
     {{ ui::badge(label="Published", variant="success") }}

   Notes:
   - Macros can't see template context vars (e.g. `lang`); pass already-translated
     strings as `label`.
   - `attrs` is injected raw (caller must pass it through `| safe`); use it for
     htmx / name / value / @click / :disabled etc. For buttons whose attrs carry
     nested quotes (e.g. hx-on with toast(...)), keep them inline instead.
   - `pad` is the size (default Penguin "px-4 py-2"); override to preserve an
     existing size rather than normalizing it.
   - The button class strings are the **verbatim** Penguin variants from
     penguinui/buttons/{default,outline,ghost}-button.html (only `inline-flex
     items-center justify-center` is added so <a> and w-full render correctly,
     and the upstream `text-onDanger`/`text-onSuccess`… token typos are fixed to
     our real `text-on-*` tokens). `variant` selects a Penguin variant:
       solid   : primary (default) | secondary | danger | success | warning | info
       outline : outline-primary | outline-secondary | outline-alternate | outline-danger
       ghost   : ghost-primary | ghost-secondary | ghost-danger #}

{# CSRF hidden field for native (non-htmx) <form method="post"> submits. htmx
   requests instead inherit the X-CSRF-Token header from <body hx-headers>.
   `csrf_token()` is a global Tera function bound per-request by shared::csrf. #}
{% macro csrf_field() -%}
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
{%- endmacro %}

{% macro button(label, variant="primary", type="button", href="", attrs="", extra="", icon="", size="px-4 py-2 text-sm", nowrap=true) -%}
{%- if variant == "secondary" -%}{% set cls = "border border-secondary bg-secondary text-on-secondary focus-visible:outline-secondary dark:border-secondary-dark dark:bg-secondary-dark dark:text-on-secondary-dark dark:focus-visible:outline-secondary-dark" -%}
{%- elif variant == "danger" -%}{% set cls = "border border-danger bg-danger text-on-danger focus-visible:outline-danger dark:bg-danger dark:border-danger dark:text-on-danger dark:focus-visible:outline-danger" -%}
{%- elif variant == "success" -%}{% set cls = "border border-success bg-success text-on-success focus-visible:outline-success dark:bg-success dark:border-success dark:text-on-success dark:focus-visible:outline-success" -%}
{%- elif variant == "warning" -%}{% set cls = "border border-warning bg-warning text-on-warning focus-visible:outline-warning dark:bg-warning dark:border-warning dark:text-on-warning dark:focus-visible:outline-warning" -%}
{%- elif variant == "info" -%}{% set cls = "border border-info bg-info text-on-info focus-visible:outline-info dark:bg-info dark:border-info dark:text-on-info dark:focus-visible:outline-info" -%}
{%- elif variant == "outline-primary" -%}{% set cls = "border border-primary bg-transparent text-primary focus-visible:outline-primary dark:border-primary-dark dark:text-primary-dark dark:focus-visible:outline-primary-dark" -%}
{%- elif variant == "outline-secondary" -%}{% set cls = "border border-secondary bg-transparent text-secondary focus-visible:outline-secondary dark:border-secondary-dark dark:text-secondary-dark dark:focus-visible:outline-secondary-dark" -%}
{%- elif variant == "outline-alternate" -%}{% set cls = "border border-outline bg-transparent text-outline focus-visible:outline-outline dark:border-outline-dark dark:text-outline-dark dark:focus-visible:outline-outline-dark" -%}
{%- elif variant == "outline-danger" -%}{% set cls = "border border-danger bg-transparent text-danger focus-visible:outline-danger dark:border-danger dark:text-danger dark:focus-visible:outline-danger" -%}
{%- elif variant == "ghost-primary" -%}{% set cls = "bg-transparent text-primary focus-visible:outline-primary dark:text-primary-dark dark:focus-visible:outline-primary-dark" -%}
{%- elif variant == "ghost-secondary" -%}{% set cls = "bg-transparent text-secondary focus-visible:outline-secondary dark:text-secondary-dark dark:focus-visible:outline-secondary-dark" -%}
{%- elif variant == "ghost-danger" -%}{% set cls = "bg-transparent text-danger focus-visible:outline-danger dark:text-danger dark:focus-visible:outline-danger" -%}
{%- else -%}{% set cls = "border border-cta bg-cta text-on-cta focus-visible:outline-cta dark:border-cta-dark dark:bg-cta-dark dark:text-on-cta-dark dark:focus-visible:outline-cta-dark" -%}
{%- endif -%}
{% if nowrap %}{% set wrap = "whitespace-nowrap" %}{% else %}{% set wrap = "text-balance" %}{% endif %}{% if href %}<a href="{{ href }}"{% else %}<button type="{{ type }}"{% endif %} class="inline-flex items-center justify-center gap-2 {{ wrap }} rounded-radius {{ size }} text-center font-medium tracking-wide transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 active:opacity-100 active:outline-offset-0 disabled:cursor-not-allowed disabled:opacity-75 {{ cls }} {{ extra }}" {{ attrs | safe }}>{{ icon | safe }}{{ label }}</{% if href %}a{% else %}button{% endif %}>
{%- endmacro button %}

{# Icon-only button (square). Penguin ghost treatment (bg-transparent,
   hover:opacity-75); pass the raw <svg> as `icon`, an accessible name via
   `aria_label`/`sr`, and any Alpine/htmx via `attrs` (raw). variant ∈
   ghost-secondary (default) | ghost-primary | ghost-danger | ghost-alternate. #}
{% macro icon_button(icon, variant="ghost-secondary", type="button", href="", attrs="", extra="", aria_label="", sr="", size="size-9") -%}
{%- if variant == "ghost-primary" -%}{% set cls = "text-primary focus-visible:outline-primary dark:text-primary-dark dark:focus-visible:outline-primary-dark" -%}
{%- elif variant == "ghost-danger" -%}{% set cls = "text-danger focus-visible:outline-danger dark:text-danger dark:focus-visible:outline-danger" -%}
{%- elif variant == "ghost-alternate" -%}{% set cls = "text-outline focus-visible:outline-outline dark:text-outline-dark dark:focus-visible:outline-outline-dark" -%}
{%- else -%}{% set cls = "text-secondary focus-visible:outline-secondary dark:text-secondary-dark dark:focus-visible:outline-secondary-dark" -%}
{%- endif -%}
{% if href %}<a href="{{ href }}"{% else %}<button type="{{ type }}"{% endif %}{% if aria_label %} aria-label="{{ aria_label }}" title="{{ aria_label }}"{% endif %} class="inline-flex shrink-0 items-center justify-center rounded-radius bg-transparent {{ size }} transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 active:opacity-100 active:outline-offset-0 disabled:cursor-not-allowed disabled:opacity-75 {{ cls }} {{ extra }}" {{ attrs | safe }}>{{ icon | safe }}{% if sr %}<span class="sr-only">{{ sr }}</span>{% endif %}</{% if href %}a{% else %}button{% endif %}>
{%- endmacro icon_button %}

{# Inline icon set — the Heroicons-style SVGs that were duplicated across
   base.html / admin/base.html (hamburger, close, cart). Penguin ships no icon
   library, so this is pure dedup, not a port. `size` sets the box (default
   size-5), `extra` adds classes, `attrs` is raw (x-show / x-cloak etc.). Icons
   are decorative: aria-hidden is baked in — put the accessible name on the
   enclosing button/link. The chevron dropdown arrows (checkout, _sidebar) stay
   inline at their call sites because they carry nested-quote Alpine :class
   bindings (see the attrs note at the top of this file). name ∈
   hamburger (default) | close | cart. #}
{% macro icon(name, size="size-5", extra="", attrs="") -%}
{%- if name == "cart" -%}
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="{{ size }}{% if extra %} {{ extra }}{% endif %}" aria-hidden="true" {{ attrs | safe }}><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 3h1.386c.51 0 .955.343 1.087.835l.383 1.437M7.5 14.25a3 3 0 0 0-3 3h15.75m-12.75-3h11.218c1.121-2.3 2.1-4.684 2.924-7.138a60.114 60.114 0 0 0-16.536-1.84M7.5 14.25 5.106 5.272M6 20.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Zm12.75 0a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z" /></svg>
{%- elif name == "close" -%}
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="{{ size }}{% if extra %} {{ extra }}{% endif %}" aria-hidden="true" {{ attrs | safe }}><path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" /></svg>
{%- elif name == "chevron-double-left" -%}
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="{{ size }}{% if extra %} {{ extra }}{% endif %}" aria-hidden="true" {{ attrs | safe }}><path stroke-linecap="round" stroke-linejoin="round" d="m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5" /></svg>
{%- elif name == "search" -%}
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="{{ size }}{% if extra %} {{ extra }}{% endif %}" aria-hidden="true" {{ attrs | safe }}><path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" /></svg>
{%- else -%}
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="{{ size }}{% if extra %} {{ extra }}{% endif %}" aria-hidden="true" {{ attrs | safe }}><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" /></svg>
{%- endif -%}
{%- endmacro icon %}

{# Compact danger alert (form/inline errors). Adapted from
   penguinui/alert/default-alert.html (danger variant), trimmed to a single line
   with the danger icon. #}
{# Required-field marker: a red asterisk appended to a field label. #}
{% macro req() -%}
<span class="ml-0.5 text-danger" aria-hidden="true">*</span>
{%- endmacro req %}

{% macro alert_danger(message, extra="") -%}
<div class="flex w-full items-center gap-2 overflow-hidden rounded-radius border border-danger bg-danger/10 px-3 py-2 text-sm text-danger {{ extra }}" role="alert">
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5 shrink-0" aria-hidden="true">
    <path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM8.28 7.22a.75.75 0 0 0-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 1 0 1.06 1.06L10 11.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L11.06 10l1.72-1.72a.75.75 0 0 0-1.06-1.06L10 8.94 8.28 7.22Z" clip-rule="evenodd" />
  </svg>
  <span>{{ message }}</span>
</div>
{%- endmacro alert_danger %}

{# Soft-color badge. variant ∈ success | danger | warning | info | primary | neutral #}
{% macro badge(label, variant="neutral") -%}
{% if variant == "success" -%}
<span class="inline-flex w-fit overflow-hidden rounded-radius border border-success bg-surface text-xs font-medium text-success dark:bg-surface-dark"><span class="bg-success/10 px-2 py-1">{{ label }}</span></span>
{%- elif variant == "danger" -%}
<span class="inline-flex w-fit overflow-hidden rounded-radius border border-danger bg-surface text-xs font-medium text-danger dark:bg-surface-dark"><span class="bg-danger/10 px-2 py-1">{{ label }}</span></span>
{%- elif variant == "warning" -%}
<span class="inline-flex w-fit overflow-hidden rounded-radius border border-warning bg-surface text-xs font-medium text-warning dark:bg-surface-dark"><span class="bg-warning/10 px-2 py-1">{{ label }}</span></span>
{%- elif variant == "info" -%}
<span class="inline-flex w-fit overflow-hidden rounded-radius border border-info bg-surface text-xs font-medium text-info dark:bg-surface-dark"><span class="bg-info/10 px-2 py-1">{{ label }}</span></span>
{%- elif variant == "primary" -%}
<span class="inline-flex w-fit overflow-hidden rounded-radius border border-primary bg-surface text-xs font-medium text-primary dark:border-primary-dark dark:bg-surface-dark dark:text-primary-dark"><span class="bg-primary/10 px-2 py-1 dark:bg-primary-dark/10">{{ label }}</span></span>
{%- else -%}
<span class="inline-flex w-fit overflow-hidden rounded-radius border border-outline bg-surface text-xs font-medium text-on-surface dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark"><span class="bg-surface-alt/40 px-2 py-1 dark:bg-surface-dark-alt/40">{{ label }}</span></span>
{%- endif %}
{%- endmacro badge %}

{# Effective-price cell content for the admin products table. The value is
   colored only when it differs from the regular price (effective_reduced);
   when equal it renders in the plain text color, unified with the Price column.
   `preview=true` uses the info color (an unsaved profile-toggle preview) instead
   of the saved primary color. No t() calls, so it is safe inside a macro. #}
{% macro eff_price(p, preview=false) -%}
{%- if preview -%}{% set strong = "text-info" %}{%- else -%}{% set strong = "text-primary dark:text-primary-dark" %}{%- endif -%}
{% if p.effective_reduced %}
<span class="font-medium {{ strong }}">{{ p.effective_price }} €</span>
<span class="ml-1 text-xs text-on-surface/60 dark:text-on-surface-dark/60">(−{{ p.effective_percent_off }}%)</span>
{% else %}
{{ p.effective_price }} €
{% endif %}
{%- endmacro eff_price %}

{# ---- Form controls. Verbatim Penguin classes from
   penguinui/{text-input,text-area,select,checkbox,file-input}/default-*.html.
   These macros emit only the control (callers keep their own <label>/layout), so
   text-color utilities are added here (upstream sets them on the wrapper div). #}

{# Text/email/number/password input. #}
{% macro input(name, type="text", id="", value="", placeholder="", required=false, autocomplete="", attrs="", extra="", width="w-full") -%}
<input {% if id %}id="{{ id }}" {% endif %}name="{{ name }}" type="{{ type }}"{% if value is number or value != "" %} value="{{ value }}"{% endif %}{% if placeholder %} placeholder="{{ placeholder }}"{% endif %}{% if required %} required{% endif %}{% if autocomplete %} autocomplete="{{ autocomplete }}"{% endif %} class="{{ width }} rounded-radius border border-outline bg-surface-alt px-2 py-2 text-sm text-on-surface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:cursor-not-allowed disabled:opacity-75 dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:text-on-surface-dark dark:focus-visible:outline-primary-dark {{ extra }}" {{ attrs | safe }}/>
{%- endmacro input %}

{% macro textarea(name, id="", value="", rows="3", placeholder="", required=false, attrs="", extra="") -%}
<textarea {% if id %}id="{{ id }}" {% endif %}name="{{ name }}" rows="{{ rows }}"{% if placeholder %} placeholder="{{ placeholder }}"{% endif %}{% if required %} required{% endif %} class="w-full rounded-radius border border-outline bg-surface-alt px-2.5 py-2 text-sm text-on-surface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:cursor-not-allowed disabled:opacity-75 dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:text-on-surface-dark dark:focus-visible:outline-primary-dark {{ extra }}" {{ attrs | safe }}>{{ value }}</textarea>
{%- endmacro textarea %}

{# Quill rich-text editor (see /static/js/rich-editor.js + /static/vendor/quill).
   The real value rides in a hidden <textarea> the editor keeps in sync, so it
   submits like any other field. `value` pre-fills (HTML or plain text). Several
   editors may share one form — each is scoped to its own [data-rich-field].
   Requires the page to load quill.js + quill.snow.css + rich-editor.js (the
   product form does so) and a _csrf field in the form for image uploads. #}
{% macro rich_editor(name, lang, value="", placeholder="", min_height="12rem") -%}
<div data-rich-field>
  <textarea name="{{ name }}" data-rich-content class="hidden">{{ value }}</textarea>
  <div data-rich-editor class="rich-editor" style="--rich-min-height: {{ min_height }};"{% if placeholder %} data-placeholder="{{ placeholder }}"{% endif %}></div>
  <div data-image-size-controls class="rich-image-size-controls mt-2 hidden">
    <span class="text-xs font-medium text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="image-size", lang=lang) }}</span>
    <button type="button" data-image-size="small">{{ t(key="image-size-small", lang=lang) }}</button>
    <button type="button" data-image-size="medium">{{ t(key="image-size-medium", lang=lang) }}</button>
    <button type="button" data-image-size="full">{{ t(key="image-size-full", lang=lang) }}</button>
    <label class="inline-flex items-center gap-1.5">
      <span class="text-xs text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="image-width-px", lang=lang) }}</span>
      <input type="number" min="40" max="1200" step="10" data-image-width
        class="w-20 rounded-radius border border-outline bg-surface-alt px-2 py-1 text-sm text-on-surface dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:text-on-surface-dark">
    </label>
  </div>
  <p class="mt-1 text-xs text-on-surface/60 dark:text-on-surface-dark/60" data-rich-status
    data-uploading="{{ t(key='image-uploading', lang=lang) }}"
    data-uploaded="{{ t(key='image-uploaded', lang=lang) }}"
    data-error="{{ t(key='image-upload-error', lang=lang) }}"></p>
</div>
{%- endmacro rich_editor %}

{# File input. #}
{% macro file_input(name, id="", accept="", attrs="", extra="") -%}
<input {% if id %}id="{{ id }}" {% endif %}name="{{ name }}" type="file"{% if accept %} accept="{{ accept }}"{% endif %} class="w-full overflow-clip rounded-radius border border-outline bg-surface-alt/50 text-sm text-on-surface file:mr-4 file:border-none file:bg-surface-alt file:px-4 file:py-2 file:font-medium file:text-on-surface-strong focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:cursor-not-allowed disabled:opacity-75 dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:text-on-surface-dark dark:file:bg-surface-dark-alt dark:file:text-on-surface-dark-strong dark:focus-visible:outline-primary-dark {{ extra }}" {{ attrs | safe }}/>
{%- endmacro file_input %}

{# Checkbox (full Penguin control: custom box + check icon + label text). #}
{% macro checkbox(name, label, id="", value="on", checked=false, attrs="", extra="") -%}
<label {% if id %}for="{{ id }}" {% endif %}class="flex items-center gap-2 text-sm font-medium text-on-surface dark:text-on-surface-dark has-checked:text-on-surface-strong dark:has-checked:text-on-surface-dark-strong has-disabled:cursor-not-allowed has-disabled:opacity-75 {{ extra }}">
  <span class="relative flex items-center">
    <input {% if id %}id="{{ id }}" {% endif %}name="{{ name }}" value="{{ value }}" type="checkbox"{% if checked %} checked{% endif %} class="before:content[''] peer relative size-4 appearance-none overflow-hidden rounded-sm border border-outline bg-surface-alt before:absolute before:inset-0 checked:border-primary checked:before:bg-primary focus:outline-2 focus:outline-offset-2 focus:outline-outline-strong checked:focus:outline-primary active:outline-offset-0 disabled:cursor-not-allowed dark:border-outline-dark dark:bg-surface-dark-alt dark:checked:border-primary-dark dark:checked:before:bg-primary-dark dark:focus:outline-outline-dark-strong dark:checked:focus:outline-primary-dark" {{ attrs | safe }}/>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" stroke="currentColor" fill="none" stroke-width="4" class="pointer-events-none invisible absolute left-1/2 top-1/2 size-3 -translate-x-1/2 -translate-y-1/2 text-on-primary peer-checked:visible dark:text-on-primary-dark">
      <path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/>
    </svg>
  </span>
  <span>{{ label }}</span>
</label>
{%- endmacro checkbox %}

{# Radio dot (verbatim Penguin custom radio from penguinui/radio/radio-with-container.html).
   Emits ONLY the <input> (the styled dot) — callers keep their own card-style
   <label> wrapper (e.g. checkout's has-[:checked]:border-primary cards). Use
   `attrs` for x-model / required etc.; callers whose @change mixes nested
   single+double quotes (carrier loop) spell this class out inline instead. #}
{% macro radio(name, value="", id="", checked=false, attrs="", extra="") -%}
<input {% if id %}id="{{ id }}" {% endif %}name="{{ name }}" type="radio" value="{{ value }}"{% if checked %} checked{% endif %} class="before:content[''] relative h-4 w-4 appearance-none rounded-full border border-outline bg-surface before:invisible before:absolute before:left-1/2 before:top-1/2 before:h-1.5 before:w-1.5 before:-translate-x-1/2 before:-translate-y-1/2 before:rounded-full before:bg-on-primary checked:border-primary checked:bg-primary checked:before:visible focus:outline-2 focus:outline-offset-2 focus:outline-outline-strong checked:focus:outline-primary disabled:cursor-not-allowed dark:border-outline-dark dark:bg-surface-dark dark:before:bg-on-primary-dark dark:checked:border-primary-dark dark:checked:bg-primary-dark dark:focus:outline-outline-dark-strong dark:checked:focus:outline-primary-dark {{ extra }}" {{ attrs | safe }}/>
{%- endmacro radio %}

{# ---- Table chrome. The same wrapper/thead/tbody/row/tfoot class strings were
   copy-pasted across 5 admin/shop tables; centralized here so the styling has a
   single source of truth. Tera has no slot/{% raw %}{% call %}{% endraw %} mechanism, so cells stay
   inline (their content varies too much to macro-ize: images, htmx forms, Alpine
   inputs, badges) and these macros expose just the shared CLASS STRINGS used as
   `class="{{ ui::thead_cls() }}"`. Adopts Penguin default-table.html's
   `w-full overflow-x-auto` wrapper so wide tables scroll on mobile.

   Skeleton:
     <div class="{{ ui::table_wrap_cls() }}">
       <table class="{{ ui::table_cls() }}">
         <thead class="{{ ui::thead_cls() }}"><tr>{{ ui::th(label="Name") }}{{ ui::th(label="Total", align="text-right") }}</tr></thead>
         <tbody class="{{ ui::tbody_cls() }}">
           <tr class="{{ ui::row_cls() }}"><td class="px-4 py-3">…</td></tr>   {# row_cls = hover; omit for non-interactive rows #}
         </tbody>
       </table>
     </div>  #}
{% macro table_wrap_cls() -%}
overflow-hidden w-full overflow-x-auto rounded-radius border border-outline dark:border-outline-dark
{%- endmacro table_wrap_cls %}

{% macro table_cls() -%}
w-full text-left text-sm
{%- endmacro table_cls %}

{% macro thead_cls() -%}
border-b border-outline bg-surface-alt text-xs uppercase tracking-wide text-on-surface/70 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark/70
{%- endmacro thead_cls %}

{% macro tbody_cls() -%}
divide-y divide-outline dark:divide-outline-dark
{%- endmacro tbody_cls %}

{% macro row_cls() -%}
hover:bg-surface-alt dark:hover:bg-surface-dark-alt
{%- endmacro row_cls %}

{% macro tfoot_cls() -%}
border-t border-outline dark:border-outline-dark
{%- endmacro tfoot_cls %}

{# Header cell. align ∈ "" (left, default) | "text-right". Pass label="" for the
   empty actions column. #}
{% macro th(label, align="") -%}
<th class="px-4 py-3 font-semibold{% if align %} {{ align }}{% endif %}">{{ label }}</th>
{%- endmacro th %}

{# Top-nav link. Penguin navbar/default-navbar.html link treatment: text-only,
   underline on focus, hover:text-primary, active (aria-current=page, set by
   markActiveNav() via data-nav) = font-semibold + primary. Matches the ported
   sidebars. variant ∈ default | warning (admin) | danger (logout-style links).
   Logout itself stays an inline <form><button> (not an <a>, so not this macro). #}
{% macro nav_link(label, href, data_nav="", variant="default", attrs="") -%}
{%- if variant == "warning" -%}{% set c = "text-warning hover:opacity-75 dark:text-warning" -%}
{%- elif variant == "danger" -%}{% set c = "text-danger hover:opacity-75 dark:text-danger" -%}
{%- else -%}{% set c = "text-on-surface hover:text-primary aria-[current=page]:font-semibold aria-[current=page]:text-primary dark:text-on-surface-dark dark:hover:text-primary-dark dark:aria-[current=page]:text-primary-dark" -%}
{%- endif -%}
<a href="{{ href }}"{% if data_nav %} data-nav="{{ data_nav }}"{% endif %} class="text-sm font-medium underline-offset-2 transition focus:outline-hidden focus-visible:underline {{ c }}" {{ attrs | safe }}>{{ label }}</a>
{%- endmacro nav_link %}

{# Breadcrumbs (Kompress design: chevron separators). Build a trail by emitting
   one ui::crumb(label, href) per ancestor and a final ui::crumb_current(label)
   for the active page, all inside <nav><ol>…</ol></nav>:

     <nav aria-label="breadcrumb" class="text-sm">
       <ol class="flex flex-wrap items-center gap-1.5 text-on-surface/60 dark:text-on-surface-dark/60">
         {{ ui::crumb(label="Domov", href="/") }}
         {{ ui::crumb(label="Obchod", href="/shop") }}
         {{ ui::crumb_current(label=category.name) }}
       </ol>
     </nav>

   Adapted from penguinui/breadcrumbs/breadcrumb-with-chevron.html. #}
{% macro crumb(label, href) -%}
<li class="flex items-center gap-1.5">
  <a href="{{ href }}" class="transition hover:text-primary dark:hover:text-primary-dark">{{ label }}</a>
  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="size-3.5 shrink-0 text-on-surface/30 dark:text-on-surface-dark/30" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" /></svg>
</li>
{%- endmacro crumb %}

{% macro crumb_current(label) -%}
<li class="font-semibold text-on-surface-strong dark:text-on-surface-dark-strong" aria-current="page">{{ label }}</li>
{%- endmacro crumb_current %}

{# Checkout step indicator: Basket › Info › Payment & transport, with chevrons
   only *between* steps (no dangling trailing chevron) and the active step bold.
   `active` is one of "info" | "payment"; the basket is always a link to /cart. #}
{% macro checkout_steps(active, lang) -%}
<nav aria-label="breadcrumb" class="mb-5 text-sm">
  <ol class="flex flex-wrap items-center gap-1.5 text-on-surface/60 dark:text-on-surface-dark/60">
    <li><a href="/cart" class="transition hover:text-primary dark:hover:text-primary-dark">{{ t(key="checkout-step-basket", lang=lang) }}</a></li>
    <li class="flex items-center gap-1.5"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="size-3.5 shrink-0 text-on-surface/30 dark:text-on-surface-dark/30" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" /></svg>
      {% if active == "info" %}<span class="font-semibold text-on-surface-strong dark:text-on-surface-dark-strong" aria-current="page">{{ t(key="checkout-step-info", lang=lang) }}</span>
      {% else %}<a href="/checkout/info" class="transition hover:text-primary dark:hover:text-primary-dark">{{ t(key="checkout-step-info", lang=lang) }}</a>{% endif %}
    </li>
    <li class="flex items-center gap-1.5"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="size-3.5 shrink-0 text-on-surface/30 dark:text-on-surface-dark/30" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" /></svg>
      {% if active == "payment" %}<span class="font-semibold text-on-surface-strong dark:text-on-surface-dark-strong" aria-current="page">{{ t(key="checkout-step-payment", lang=lang) }}</span>
      {% else %}<span>{{ t(key="checkout-step-payment", lang=lang) }}</span>{% endif %}
    </li>
  </ol>
</nav>
{%- endmacro checkout_steps %}

{# Title for the static info pages (controllers/pages.rs → pages/info.html),
   resolved from the `page` slug. Lives in a macro because a child template's
   top-level {% set %} isn't visible inside its {% block %}s under `extends`;
   the macro can be called from both the title and content blocks. #}
{% macro page_title(page, lang) -%}
{%- if page == "contact" -%}{{ t(key="top-contact", lang=lang) }}
{%- elif page == "sitemap" -%}{{ t(key="top-sitemap", lang=lang) }}
{%- elif page == "terms" -%}{{ t(key="footer-terms", lang=lang) }}
{%- elif page == "about" -%}{{ t(key="footer-about", lang=lang) }}
{%- elif page == "stores" -%}{{ t(key="footer-stores", lang=lang) }}
{%- elif page == "shipping" -%}{{ t(key="footer-shipping", lang=lang) }}
{%- else -%}{{ t(key="brand", lang=lang) }}
{%- endif -%}
{%- endmacro page_title %}
