{# Pre-paint theme resolver (#94). MUST stay before every stylesheet AND the blocking htmx script below: a slow script fetch must not delay theme resolution, or the first painted frame flashes the wrong theme. Guarded: unreadable storage or an unknown value falls back to System (no attribute). All other theme wiring lives at the end of . #} {% block title %}Voxint review{% endblock %} {# Island stylesheet (issue #48). Placed BEFORE the inline {% block nav %} {% endblock %}
{# Guided-tutorial banner: present only when the API layer resolved a real tutorial-run/step for this page. Injected here (outside every page's body block, and outside the workbench htmx swap target) so fragment swaps never remove it. #} {% if tutorial %}{% include "fragments/tutorial_banner.html" %}{% endif %} {% block body %}{% endblock %}
{# Theme wiring (#94). The resolver owns first paint; this owns the rest. Split on purpose (multi-model plan review): applyDom never persists and the storage handler never writes back, so cross-tab events cannot echo; persistence failures degrade to a working same-page toggle. The Settings radios are wired by document-level delegation (inert on pages without them, survives htmx swaps — no script lives in the partial); pageshow re-reads for bfcache restores. OS flips in System mode need no JS: the guarded @media block re-themes chrome, and WaveformStrip has its own matchMedia listener. #} {# Shared island loader (issue #48): the ONLY script every page pulls in. It no-ops when a page has no [data-island] nodes and dynamically imports only the island bundles present, so adding an island never touches base.html. The guard keeps dev/source-run (no build) working — no broken script tag. #} {% if asset_url('main') %}{% endif %}