{# Schema-discovery ERD section: legend, toolbar, interactive Cytoscape canvas
with a static-SVG fallback, and the JSON data island powering both. #}
{# Card-internal styles. Emitted once for the fallback SVG on the page and
inlined into every standalone card SVG (SVG-as-image cannot see page CSS). #}
{% set erd_card_css %}
.schema-node { fill: #fff; stroke: #b7c9c2; stroke-width: 1.4; }
.schema-node-header { fill: #123d32; }
.schema-node-header.is-hub { fill: #0d5f4a; }
.schema-node-header-rule { fill: #0d7a5f; }
.schema-node-icon { stroke: #9de0ca; stroke-width: 1.4; fill: none; }
.schema-node-title { fill: #fff; font-size: 15px; font-weight: 800; letter-spacing: -.01em; }
.schema-node-meta { fill: #b9d8ce; font-size: 10.5px; }
.schema-role { fill: #cfeee2; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; }
.schema-section-label { fill: #7a8984; font-size: 9.5px; font-weight: 800; letter-spacing: .09em; }
.schema-row-rule { stroke: #e5ece9; stroke-width: 1; }
.schema-row-label { fill: #23312c; font-size: 11.5px; font-weight: 700; }
.schema-row-detail { fill: #71817b; font-size: 10px; }
.schema-key-badge { stroke-width: 1; }
.schema-key-badge.pk { fill: #dff4ed; stroke: #b9e3d5; }
.schema-key-badge.fk { fill: #e8f0f6; stroke: #cbdce8; }
.schema-key-badge-text { font-size: 9px; font-weight: 850; text-anchor: middle; }
.schema-key-badge-text.pk { fill: #07624b; }
.schema-key-badge-text.fk { fill: #345b74; }
.schema-empty-state { fill: #7a8984; font-size: 10.5px; }
.schema-more { fill: #0d5f4a; font-size: 10.5px; font-weight: 700; }
{% endset %}
{# One table card drawn at local (0,0). Reused by the fallback SVG (wrapped in
a translate group) and by card_svg() below for the Cytoscape node image. #}
{% macro table_card(node) %}
{{ node.display_table }}{{ node.row_count | format_value }} rows · {{ node.column_count }} columns{{ node.role }}{% if node.role in ['hub', 'dimension'] and node.referenced_by %} · ref {{ node.referenced_by }}{% elif node.references %} · refs {{ node.references }}{% endif %}INFERRED KEY ROLES
{% if node.rows %}
{% for row in node.rows %}
{{ row.kind }} · {{ row.full_label }} · {{ row.detail }}
{% if not loop.first %}{% endif %}
{{ row.kind }}{{ row.label }}{{ row.detail }}
{% endfor %}
{% else %}
No candidate key roles met the evidence threshold
{% endif %}
{% if node.overflow_count %}
+ {{ node.overflow_count }} more — show all
{% endif %}
{% endmacro %}
{# Standalone SVG document for one card, used as a Cytoscape node image. #}
{% macro card_svg(node) -%}
{%- endmacro %}
{{ sections.number('erd') }}
{{ artifact.title }}
Evidence-backed candidates, not declared constraints. Scroll to zoom, drag the canvas to pan, drag a table to rearrange, click a table to focus its relationships; toggle tables to declutter.
KeysPK candidate primary keyFK inferred foreign keyTable roles hub dimension fact junctionConfidence high medium lowCardinality1 exactly one (parent side)
N can match many (child side)
{% set nodes_by_importance = artifact.data.nodes | sort(attribute='referenced_by', reverse=true) %}
{% for node in nodes_by_importance[:12] %}
{{ node.role }} {{ node.display_table }}{% if node.referenced_by %} · ref by {{ node.referenced_by }}{% endif %}
{% endfor %}
Tables
{% for node in artifact.data.nodes %}
{% endfor %}
Interactive diagram unavailable — showing the static fallback.
Drag tables to rearrange · click a table to focus · click empty space to reset
{% if cytoscape_js %}
{% endif %}
{% include "_erd_cytoscape.html" %}
{% include "_erd_fallback.html" %}