{#- mgf-brand-web · the shared operator macro pack (M1 P4.1)
    =========================================================================
    The Jinja macros fed-web and test-web used to copy-paste, now shipped by
    the kit (G3's duplication list): the sync/test state chips, the workflow
    chip, the origin badge, the derived tag, honest absence, the inline empty
    state — and the sprite `icon()` helper they build on.

    Import (with the kit templates dir on your loader path — see console.html):

        {% import "mgf/macros.html" as mgf %}
        {{ mgf.sync_chip(project.state) }}
        {{ mgf.status_chip('pass', icon='success') }}
        {{ mgf.icon('proof', label='Verified') }}

    Icons resolve against the vendored sprite at
    /static/brand/icons/icons-sprite.svg (same-origin external <use> — the
    canon usage from mgf-brand's icon sheet); pass `sprite=` if your brand
    mount differs. Everything renders kit classes themed by --mgf-* tokens.
    ========================================================================= -#}

{#- A sprite icon. Decorative by default (aria-hidden, per the icon law);
    pass `label` to make it meaningful (role="img" + aria-label). Sizes in
    text-flow at 1em via .mgf-icon; pass `size` (px) to fix it. -#}
{%- macro icon(name, size=none, label=none, sprite="/static/brand/icons/icons-sprite.svg") -%}
<svg class="mgf-icon"{% if size %} style="width:{{ size }}px;height:{{ size }}px"{% endif %}
     {%- if label %} role="img" aria-label="{{ label }}"{% else %} aria-hidden="true"{% endif %}><use
    href="{{ sprite }}#mgf-icon-{{ name }}"/></svg>
{%- endmacro -%}

{#- SYNC-language state chip (IN_SYNC, STALE, TAMPERED, UNKNOWN, UNREADABLE,
    CHECKING, SYNCING, ERROR …) — the brand state-chip; the in-flight states
    swap the dot for a spinner. `icon` (optional) adds a sprite glyph beside
    the dot — decoration on top of dot + label, never instead of them. -#}
{%- macro sync_chip(state, icon_name=none) -%}
<span class="status-chip status-chip--{{ state|lower|replace('_','-') }}">
{%- if state in ('CHECKING', 'SYNCING') %}<span class="spinner"></span>{% else %}<span class="status-chip__dot"></span>{% endif -%}
{%- if icon_name %}{{ icon(icon_name) }}{% endif -%}
{{ state }}</span>
{%- endmacro -%}

{#- TEST-language state chip (pass, fail, error, skipped, running, …);
    `running=True` overrides the committed last status. -#}
{%- macro status_chip(value, running=false, icon_name=none) -%}
{%- set v = 'running' if running else (value or 'unknown') -%}
<span class="status-chip status-chip--{{ v|lower|replace('_','-') }}"><span class="status-chip__dot"></span>
{%- if icon_name %}{{ icon(icon_name) }}{% endif -%}
{{ v }}</span>
{%- endmacro -%}

{#- Task / stage WORKFLOW-state chip (done, in_progress, next, postponed,
    blocked, todo, …) — kebab-cased modifier, de-underscored label. -#}
{%- macro tstate(state) -%}
<span class="tstate tstate--{{ state|lower|replace('_','-') }}"><span class="tstate__dot"></span>{{ state|replace('_', ' ') }}</span>
{%- endmacro -%}

{#- Actor-origin badge — who submitted the run / produced the record
    (web · cli · agent · ci). -#}
{%- macro origin_badge(origin) -%}
<span class="origin-badge origin-badge--{{ origin }}">{{ origin }}</span>
{%- endmacro -%}

{#- The violet "derived" tag — inferred by the web layer, never a
    project-reported fact. Name the inferring surface. -#}
{%- macro derived_tag(source="the web layer") -%}
<span class="derived-tag" title="inferred by {{ source }} — not a project-reported fact">derived</span>
{%- endmacro -%}

{#- Honest-absence block — every absence is an explicit state + reason, with
    the state chip restated and (optionally) the raw errors. UNREADABLE reads
    warning ("!"), UNKNOWN reads neutral ("?"), anything else is no-data. -#}
{%- macro absence(state, title, reason, action=none, errors=none) -%}
{%- set kind = 'unreadable' if state == 'UNREADABLE' else ('unknown' if state == 'UNKNOWN' else 'no-data') -%}
<div class="empty empty--{{ kind }} absence">
  <div class="empty__icon">{{ '!' if state == 'UNREADABLE' else '?' }}</div>
  <p class="empty__title">{{ title }}</p>
  <p class="empty__desc">{{ reason }}</p>
  <p class="absence__state">{{ sync_chip(state) }}{% if action %} <span class="muted">{{ action }}</span>{% endif %}</p>
  {% if errors %}<ul class="absence__errs mono">{% for e in errors %}<li>{{ e }}</li>{% endfor %}</ul>{% endif %}
</div>
{%- endmacro -%}

{#- Inline "nothing here" empty card — data present, slice empty. -#}
{%- macro empty_state(title, reason, icon_glyph="∅") -%}
<div class="empty empty--no-data absence">
  <div class="empty__icon">{{ icon_glyph }}</div>
  <p class="empty__title">{{ title }}</p>
  <p class="empty__desc">{{ reason }}</p>
</div>
{%- endmacro -%}

{#- The living Magogi mark as INLINE, addressable SVG so its geometry can
    animate — an <img>-referenced SVG has no animatable internals. Renders the
    class-targeted parts that logo-motion.css drives (idle heartbeat; hover
    pulse via a `.lm-host` ancestor; success/error moods via `.is-success` /
    `.is-error`). Load `/static/brand/logo-motion.css` for the motion; without
    it the mark is a correct static drawing. Decorative by default (aria-hidden);
    pass `title` for a meaningful name. `on_dark=true` forces mist ink on indigo
    grounds (the console top bar). `mode="lockup"` adds the wordmark. `class` is
    added to the <svg> (e.g. a sizing class like mgf-console__mark-glyph). -#}
{%- macro logo_mark(mode="mark", on_dark=false, title=none, class="", id="lm") -%}
<svg class="logo-mark{% if on_dark %} on-dark{% endif %}{% if class %} {{ class }}{% endif %}"
     viewBox="{% if mode == 'lockup' %}0 0 306 96{% else %}0 0 96 96{% endif %}"
     {% if title %}role="img" aria-labelledby="{{ id }}-title"{% else %}aria-hidden="true"{% endif %}
     focusable="false" xmlns="http://www.w3.org/2000/svg">
  {%- if title %}<title id="{{ id }}-title">{{ title }}</title>{%- endif %}
  <circle class="lm-ring lm-ring-outer" cx="48" cy="48" r="33"></circle>
  <circle class="lm-ring lm-ring-primary" cx="48" cy="48" r="21"></circle>
  <circle class="lm-emit" cx="48" cy="48" r="15"></circle>
  <g class="lm-orbit">
    <circle class="lm-station" cx="29.81" cy="58.5" r="5"></circle>
    <circle class="lm-station" cx="66.19" cy="58.5" r="5"></circle>
    <circle class="lm-gold lm-keystone" cx="48" cy="27" r="5"></circle>
  </g>
  <circle class="lm-gold lm-core" cx="48" cy="48" r="8.5"></circle>
  {%- if mode == 'lockup' %}
  <g class="lm-word">
    <path transform="matrix(0.04800 0 0 -0.04800 112.000 63.000)" d="M69 0V700H294L430 81H447L584 700H808V0H692V613H675L539 0H339L203 613H186V0Z"></path>
    <path transform="matrix(0.04800 0 0 -0.04800 154.644 63.000)" d="M226 -14Q174 -14 132.0 4.5Q90 23 65.5 58.0Q41 93 41 144Q41 194 65.5 228.0Q90 262 133.0 279.5Q176 297 231 297H374V327Q374 366 350.0 390.5Q326 415 275 415Q225 415 199.5 391.5Q174 368 166 331L60 366Q72 405 98.5 437.0Q125 469 169.0 488.5Q213 508 277 508Q374 508 429.5 459.5Q485 411 485 319V125Q485 95 513 95H555V0H474Q438 0 415.0 18.0Q392 36 392 67V69H375Q369 55 354.0 35.0Q339 15 308.5 0.5Q278 -14 226 -14ZM245 80Q302 80 338.0 112.5Q374 145 374 200V210H238Q201 210 178.0 194.0Q155 178 155 147Q155 117 179.0 98.5Q203 80 245 80Z"></path>
    <path transform="matrix(0.04800 0 0 -0.04800 182.840 63.000)" d="M49 245V260Q49 338 80.0 393.5Q111 449 163.0 478.5Q215 508 277 508Q347 508 383.5 483.0Q420 458 437 429H454V494H566V-94Q566 -143 538.0 -171.5Q510 -200 462 -200H130V-100H423Q452 -100 452 -70V73H435Q424 56 405.0 38.5Q386 21 355.0 9.0Q324 -3 277 -3Q215 -3 163.0 26.5Q111 56 80.0 111.5Q49 167 49 245ZM309 98Q371 98 412.0 137.5Q453 177 453 248V258Q453 330 412.5 369.0Q372 408 309 408Q247 408 205.5 369.0Q164 330 164 258V248Q164 177 205.5 137.5Q247 98 309 98Z"></path>
    <path transform="matrix(0.04800 0 0 -0.04800 214.012 63.000)" d="M307 -14Q233 -14 174.5 16.5Q116 47 82.5 103.5Q49 160 49 239V255Q49 334 82.5 391.0Q116 448 174.5 478.0Q233 508 307 508Q381 508 439.0 478.0Q497 448 530.5 391.0Q564 334 564 255V239Q564 160 530.5 103.5Q497 47 439.0 16.5Q381 -14 307 -14ZM307 88Q370 88 410.0 128.5Q450 169 450 242V252Q450 325 410.0 365.5Q370 406 307 406Q244 406 204.0 365.5Q164 325 164 252V242Q164 169 204.0 128.5Q244 88 307 88Z"></path>
    <path transform="matrix(0.04800 0 0 -0.04800 243.936 63.000)" d="M49 245V260Q49 338 80.0 393.5Q111 449 163.0 478.5Q215 508 277 508Q347 508 383.5 483.0Q420 458 437 429H454V494H566V-94Q566 -143 538.0 -171.5Q510 -200 462 -200H130V-100H423Q452 -100 452 -70V73H435Q424 56 405.0 38.5Q386 21 355.0 9.0Q324 -3 277 -3Q215 -3 163.0 26.5Q111 56 80.0 111.5Q49 167 49 245ZM309 98Q371 98 412.0 137.5Q453 177 453 248V258Q453 330 412.5 369.0Q372 408 309 408Q247 408 205.5 369.0Q164 330 164 258V248Q164 177 205.5 137.5Q247 98 309 98Z"></path>
    <path transform="matrix(0.04800 0 0 -0.04800 275.108 63.000)" d="M73 0V494H188V0ZM130 557Q99 557 76.5 577.5Q54 598 54 632Q54 666 76.5 686.5Q99 707 130 707Q163 707 184.5 686.5Q206 666 206 632Q206 598 184.5 577.5Q163 557 130 557Z"></path>
  </g>
  {%- endif %}
</svg>
{%- endmacro -%}
