{# The tab reads the brand too: the page first, so it survives a narrow tab, and the name from the one place it is written. poll.js prefixes "(stale) " to whatever this says, which is why the page belongs at the front. #} {% if page_title is defined %}{{ page_title }} · {% endif %}{{ brand_name | default('little sister') }} {# The SVG tab icon re-inks itself for a dark tab strip (the media query sits inside the file); the .ico carries 16/32/48 for everything that ignores SVG, including the places that ask for it by name — a bookmark, a pinned tab — and the PNGs stay for browsers that prefer them. #} {#- What a dependent package ships for its slot fillers (ADR-0037). The **library** emits these tags, never the extension: an extension writing a tag into a page it does not own would be deciding load order and caching for a document it cannot see. After our own stylesheet, so a filler builds on the design tokens rather than fights them; `defer` so a script runs once the document is parsed and after Bootstrap's end-of-body bundle, in registration order. Each URL carries the asset's content hash, so a changed file is a changed URL. Emitted here rather than per page, so no page can forget them — and not at all in the kiosk, which renders no slot for them to serve. -#} {%- for kind, href in extension_assets() %} {%- if kind == 'css' %} {%- else %} {%- endif %} {%- endfor %} {#- Submit-on-change for the presentation controls. Emitted from the header, for the reason the extension assets above are: the settings menu ships from this file, so the behavior that makes it work ships with it and no page can forget it — and the dashboard's filter switches, which carry the same marker, are covered by the same one delegated listener. `defer` because a delegated listener has nothing to do before the document is parsed. -#} {#- The layout the server renders (ADR-0029). `rail` is the default and `topbar` is the arrangement this surface had before it — the same markup either way, with CSS showing the sections in the rail or in the bar. Which of them arrives here is the viewer's own choice, read from the presentation cookie; `kiosk` is the third, and the one this file branches on rather than leaving to CSS. -#} {#- Resolving the `auto` theme before first paint. Why it exists and why it has to be inline are in the partial; it is shared with the login page, which includes no header, so the two documents spend one hash between them rather than two. -#} {% include '_theme_bootstrap.html' %} {#- The kiosk takes the bar and the rail away entirely rather than restyling them — a wall display has nothing to click — so this is the one layout the server branches on instead of leaving to CSS. Its own line is emitted inside the page below, and _kiosk_line.html carries the freshness stamp the bar has no room for. -#} {% if layout != 'kiosk' %} {#- No `navbar-light`: it pins Bootstrap's navbar variables to light values, which would survive `data-bs-theme: dark` and leave a dark theme with a light bar. Without it the navbar follows the theme, and the light themes are unchanged. -#} {% endif %} {# A failed engine start must not hide in the log: while the engine is down the tree is empty, so this persistent alert is the page's only truthful signal. It reaches the kiosk too — an unattended screen is where it matters most. #} {% if engine_error %}
{% endif %} {#- The shell: the rail beside the page. Closed by _page_end.html, which every page including this file must carry. -#}
{% if layout != 'kiosk' %} {% endif %}
{%- if layout == 'kiosk' %}
{% include '_kiosk_line.html' %}
{%- endif %}