{# HyperWeave Strip Frame — Paradigm-dispatched by abstract paradigm name.

   Templates dispatch by `genome.paradigms.strip`, not by data-presence or
   genome identity.

   Built-in paradigms:
     "default" → brutalist partial (flat plate, class-based separators)
     "chrome"  → chrome partial (envelope gradient, well, url() separators)

   Zone layout: plate bg → border → accent bar → glyph →
   identity text → [divider → metric cell]* → divider → status indicator.
   Metric cells repeat at ~106px pitch.
   Extends document.svg.j2. #}
{% extends "document.svg.j2" %}

{% block defs %}
{% include "frames/strip/" ~ (paradigm | default("default")) ~ "-defs.j2" %}
<clipPath id="{{ uid }}-clip">
  <rect width="{{ width }}" height="{{ height }}"/>
</clipPath>
{% endblock %}

{% block content %}
{% set strip_h = strip_zones.core.height %}
{% set accent_w = accent_bar_width | default(6) %}
{# glyph_zone_x_offset: paradigm-supplied push-right for paradigms that
   render decoration on the left edge (cellular bifamily flanks).
   brutalist/chrome leave it 0. #}
{% set _gz_offset = strip_zones.core.glyph_zone_x_offset %}
{% set _show_icon_box = show_icon_box | default(false) %}
{% set _icon_box_size = icon_box_size | default(28) %}
{% set _icon_box_pad = icon_box_pad | default(8) %}
{# Identity-zone coordinates come from compute_strip_zones; the template only
   renders those resolved positions. #}
{% set _icon_box_x = strip_zones.core.icon_box_x %}
{% set _icon_box_y = strip_zones.core.icon_box_y %}
{% set glyph_size = strip_zones.core.glyph_size %}
{% set glyph_cx = strip_zones.core.glyph_cx %}
{% set glyph_cy = strip_zones.core.glyph_cy %}
{% set identity_x = strip_zones.core.identity_x %}
{% set metric_pitch = strip_zones.core.metric_pitch %}
{% set div_y1 = strip_divider_y1 | default(8) %}
{% set div_y2 = strip_divider_y2 | default(44) %}
{% set paradigm_strip = paradigm | default("default") %}

{# === Frame structure (paradigm-dispatched via include — polymorphism, not a branch) === #}
{% include "frames/strip/" ~ paradigm_strip ~ "-content.j2" %}

{# v0.3.2 Phase C: paradigms with brutalist strip grammar (brutalist) take full
   ownership of body composition via their content partial. The shared
   pipeline below (icon box, glyph zone, identity, metric cells, status
   indicator) only renders when paradigm_owns_strip is false — chrome /
   cellular / default continue unchanged. The flag is set unconditionally
   by resolve_strip so StrictUndefined never trips.

   Border motion overlay (after this block) stays outside the gate because
   it piggybacks on `{{ uid }}-clip` + width/height context that both code
   paths preserve. #}
{% if not paradigm_owns_strip %}
{# Card-inset wrapper: paradigms with canvas pads (primer) shift the whole
   shared pipeline into the inset card — zone geometry stays CARD-space and
   the canvas margin hosts the card's drop shadow. strip_content_offset is
   None for every other paradigm, so no group is emitted (byte-identical). #}
{% if strip_content_offset %}<g transform="translate({{ strip_content_offset.x }},{{ strip_content_offset.y }})">{% endif %}

{# === Icon box (paradigm opts in via show_icon_box) ===
   Render only when an actual glyph is provided. Without this gate, calling
   `compose("strip", ..., glyph="")` produces an EMPTY 28×28 dark box flush
   against the bifamily flank — visible structural debt on every glyphless
   strip (border-motion proofs, agent prompts that omit `glyph=`). The
   reserved layout coordinate (identity_x = _icon_box_x + box_size + 8) is
   unchanged, so the identity text still sits at the same x-position; the
   box-shaped pocket between flank and identity simply renders as void. #}
{% if _show_icon_box and glyph_path %}
<rect x="{{ _icon_box_x }}" y="{{ _icon_box_y }}" width="{{ _icon_box_size }}" height="{{ _icon_box_size }}" fill="{{ icon_box_fill | default('var(--dna-surface-deep)') }}"/>
<rect x="{{ _icon_box_x }}" y="{{ _icon_box_y }}" width="{{ _icon_box_size }}" height="{{ _icon_box_size }}" fill="none" stroke="{{ icon_box_stroke | default('var(--dna-signal)') }}" stroke-width="0.5" opacity="0.4"/>
{% endif %}

{# === Glyph zone === #}
{% if glyph_path %}
{% set gs = glyph_size %}
{% set gf = strip_glyph_fill | default('var(--dna-signal)') %}
<g data-hw-zone="glyph" transform="translate({{ glyph_cx }},{{ glyph_cy }})">
  <svg x="{{ strip_zones.core.glyph_svg_x }}" y="{{ strip_zones.core.glyph_svg_y }}"
       width="{{ gs }}" height="{{ gs }}"
       viewBox="{{ glyph_viewbox }}">
    <path d="{{ glyph_path }}"{% if glyph_fill_rule %} fill-rule="{{ glyph_fill_rule }}"{% endif %} fill="{{ glyph_fill | default(gf) }}"/>
  </svg>
</g>
{% endif %}

{# === Identity text + optional subtitle (paradigm-driven stack).
       clipped to zone before first divider. === #}
{% set first_div_x = strip_zones.core.first_divider_x %}
<defs>
  <clipPath id="{{ uid }}-id-clip">
    <rect x="{{ identity_clip_x | default(0) }}" y="{{ strip_identity_clip_y }}" width="{{ identity_clip_width | default(0) }}" height="{{ strip_h }}"/>
  </clipPath>
</defs>
<g clip-path="url(#{{ uid }}-id-clip)">
  {% if show_subtitle | default(false) and subtitle_text %}
    {# Two-line stack: identity top, subtitle bottom, centered vertically.
       The subtitle gate also checks subtitle_text non-empty so suppressed
       duplicate subtitles do not emit empty text elements. #}
    <text data-hw-zone="identity" x="{{ identity_x }}" y="{{ identity_stack_y | default(0) }}" class="{{ uid }}-identity"{% if identity_text_length and identity_text_length > 0 %} textLength="{{ identity_text_length }}" lengthAdjust="spacingAndGlyphs"{% endif %}>{{ identity | upper }}</text>
    <text data-hw-zone="subtitle" x="{{ identity_x }}" y="{{ subtitle_y | default(0) }}" font-family="'{{ subtitle_font_family }}',monospace" font-size="{{ subtitle_font_size }}" fill="{{ ink_sub | default('var(--dna-ink-sub)') }}" letter-spacing="{{ subtitle_letter_spacing_em }}em">{{ subtitle_text }}</text>
  {% else %}
    <text data-hw-zone="identity" x="{{ identity_x }}" y="{{ identity_single_y | default(0) }}" class="{{ uid }}-identity"{% if identity_text_length and identity_text_length > 0 %} textLength="{{ identity_text_length }}" lengthAdjust="spacingAndGlyphs"{% endif %}>{{ identity | upper }}</text>
  {% endif %}
</g>

{# === Metric cells with dividers === #}
{# Cell layout: every cell's group sits AT its left divider seam. Text inside
   uses paradigm-supplied x_local (cellular: 12 from start anchor; brutalist
   /chrome: cell_w//2 from middle anchor). All cells get the SAME left gutter,
   eliminating the cell-0 asymmetry that shifted "12.4k" 24px from its left
   divider while "1.2k" sat at 12. cell_widths[i] is the full seam-to-seam
   distance (resolver: content + 20 pad). #}
{% set first_div_x = strip_zones.core.first_divider_x %}
{% set _seams = strip_zones.core.seam_positions %}
{# First divider — stroke sourced from resolver-injected context:
   divider_render_mode = "gradient" → chrome-defs url(#...-sep) gradient
   divider_render_mode = "class"    → profile-declared flat color.
   Decision lives in paradigm YAML, not in a template branch on slug. #}
{# The first divider is the identity↔metrics separator — render it only when
   metrics are present, so empty strips stay identity-only with no dangling rule. #}
{% if metrics %}
{% if divider_render_mode == "paired-rect" %}
{# Paired groove+shine divider (primer): two adjacent 1px rects, colors
   resolver-supplied per substrate — a dark groove on dark substrates,
   cool-on-white on light. Geometry from strip_divider_pair. #}
<g data-hw-zone="divider" transform="translate({{ first_div_x }},0)">
  <rect x="{{ strip_divider_pair.groove_dx }}" y="{{ strip_divider_pair.y }}" width="{{ strip_divider_pair.w }}" height="{{ strip_divider_pair.h }}" fill="{{ strip_divider_groove_color }}"/>
  <rect x="{{ strip_divider_pair.shine_dx }}" y="{{ strip_divider_pair.y }}" width="{{ strip_divider_pair.w }}" height="{{ strip_divider_pair.h }}" fill="{{ strip_divider_shine_color }}"/>
</g>
{% elif divider_render_mode == "gradient" %}
<line x1="{{ first_div_x }}" y1="{{ div_y1 }}"
      x2="{{ first_div_x }}" y2="{{ div_y2 }}"
      stroke="url(#{{ uid }}-sep)" stroke-width="1"/>
{% else %}
<line x1="{{ first_div_x }}" y1="{{ div_y1 }}"
      x2="{{ first_div_x }}" y2="{{ div_y2 }}"
      stroke="{{ strip_divider_color | default('var(--dna-border)') }}"
      stroke-width="1"
      opacity="{{ strip_divider_opacity | default(1.0) }}"/>
{% endif %}
{% endif %}

{# Metric cell typography — every coordinate comes pre-resolved from the
   resolver via cell_layouts[i] (built by core.cell_layout.compute_cell_layout).
   The template renders the dict's values verbatim:
     - _layout.cell_w     → cell pitch (seam-to-seam distance)
     - _layout.label_x    → in-cell text x for the label
     - _layout.value_x    → in-cell text x for the value (== label_x today)
     - _layout.text_anchor → "start" | "middle" | "end"
   No template-side arithmetic; no paradigm-specific branching. Adding a
   new paradigm with a different alignment is a YAML edit only. #}
{% set _layouts = cell_layouts | default([]) %}
{% for metric in metrics %}
{% set _layout = _layouts[loop.index0] %}
{% set _lx = _layout.label_x %}
{% set _vx = _layout.value_x %}
{% set _mta = _layout.text_anchor %}
{% set _cell_x = _seams[loop.index0] %}
{# Leading divider before each metric EXCEPT the first — the identity↔metrics
   divider already paints that boundary. Honors the layout engine's
   show_leading_rule (the same per-cell flag brutalist-dark-content.j2 consumes),
   so NO divider renders after the last cell. The status indicator is a terminus,
   not another cell: a trailing divider there becomes the indicator's visible
   left-neighbor and pulls it off-center (automata's solid dividers exposed this;
   chrome's gradient dividers merely hid it). One divider model, every genome. #}
{% if _layout.show_leading_rule %}
{% if divider_render_mode == "paired-rect" %}
<g data-hw-zone="divider" transform="translate({{ _cell_x }},0)">
  <rect x="{{ strip_divider_pair.groove_dx }}" y="{{ strip_divider_pair.y }}" width="{{ strip_divider_pair.w }}" height="{{ strip_divider_pair.h }}" fill="{{ strip_divider_groove_color }}"/>
  <rect x="{{ strip_divider_pair.shine_dx }}" y="{{ strip_divider_pair.y }}" width="{{ strip_divider_pair.w }}" height="{{ strip_divider_pair.h }}" fill="{{ strip_divider_shine_color }}"/>
</g>
{% elif divider_render_mode == "gradient" %}
<line x1="{{ _cell_x }}" y1="{{ div_y1 }}" x2="{{ _cell_x }}" y2="{{ div_y2 }}"
      stroke="url(#{{ uid }}-sep)" stroke-width="1"/>
{% else %}
<line x1="{{ _cell_x }}" y1="{{ div_y1 }}" x2="{{ _cell_x }}" y2="{{ div_y2 }}"
      stroke="{{ strip_divider_color | default('var(--dna-border)') }}"
      stroke-width="1" opacity="{{ strip_divider_opacity | default(1.0) }}"/>
{% endif %}
{% endif %}
<g data-hw-zone="metric-{{ loop.index0 }}" transform="translate({{ _cell_x }},{{ strip_metric_local_origin_y }})">
  <text x="{{ _lx }}" y="{{ strip_metric_label_y | default(18) }}" text-anchor="{{ _mta }}"
        class="{{ uid }}-metric-label"{% if _layout.label_w and _layout.label_w > 0 %} textLength="{{ _layout.label_w | round(1) }}" lengthAdjust="spacingAndGlyphs"{% endif %}>{{ metric.label | upper }}</text>
  {# Metric value — skew applied only when strip_metric_value_skew != 0
     (was hardcoded to paradigm=chrome; now chrome.yaml sets skew=0 for badge
     parity, removing the Impact-italic treatment that diverged from badge's
     upright Orbitron). #}
  {% set _skew = strip_metric_value_skew | default(0) %}
  {% if _skew != 0 %}
  <g transform="translate({{ _vx }},{{ strip_metric_value_y | default(36) }}) skewX({{ _skew }})">
    <text x="{{ strip_metric_local_origin_x }}" y="{{ strip_metric_local_origin_y }}" text-anchor="{{ _mta }}" dominant-baseline="central"
          class="{{ uid }}-metric-value"{% if _layout.value_w and _layout.value_w > 0 %} textLength="{{ _layout.value_w | round(1) }}" lengthAdjust="spacingAndGlyphs"{% endif %}>{{ metric.value }}</text>
  </g>
  {% else %}
  <text x="{{ _vx }}" y="{{ strip_metric_value_y | default(36) }}" text-anchor="{{ _mta }}"
        class="{{ uid }}-metric-value"{% if _layout.value_w and _layout.value_w > 0 %} textLength="{{ _layout.value_w | round(1) }}" lengthAdjust="spacingAndGlyphs"{% endif %}>{{ metric.value }}</text>
  {% endif %}
{% if metric.delta %}
  <text x="{{ _vx }}" y="{{ strip_metric_delta_y }}" text-anchor="{{ _mta }}"
        class="hw-delta hw-delta--{{ metric.delta_dir | default('neutral') }}">{{ metric.delta }}</text>
{% endif %}
</g>
{% endfor %}

{# === Status indicator (paradigm-dispatched) ===
   The resolver supplies flank-aware status coordinates so the template
   renders without local position derivation. #}
{# v0.2.25: gate the status group on show_status_indicator (mode-driven).
   Stateless strips (STARS | FORKS | VERSION) get NO indicator element at
   all — same architectural posture as stateless badges. #}
{% if show_status_indicator %}
{% set _status_x = strip_zones.core.status_x %}
{% set status_x = _status_x %}
{% set status_cy = status_cy | default(0) %}
<g data-hw-zone="status" transform="translate({{ status_x }},{{ status_cy }})"
   {% if status_shape_rendering != "geometricPrecision" %}shape-rendering="{{ status_shape_rendering }}"{% endif %}>
{% include "frames/strip/" ~ paradigm_strip ~ "-status.j2" %}
</g>
{% endif %}

{% if strip_content_offset %}</g>{% endif %}{# /card-inset wrapper #}
{% endif %}{# /paradigm_owns_strip gate — end of shared zone pipeline #}

{# ── Border motion SMIL overlay (shared across all paradigms) ── #}
{% if motion_border_defs | default('') %}
<defs>{{ motion_border_defs | safe }}</defs>
{% endif %}
{% if motion_border_overlay | default('') %}
{{ motion_border_overlay | safe }}
{% endif %}
{% endblock %}
