{# The one layout. Everything the console draws is inside this. The app bar carries the icon at the far left and the name beside it; on the right, right to left, the profile, the notifications and the theme toggle. That order is the specification and it is expressed in the DOM rather than by float, so a screen reader hears it in the same order a sighted reader sees it. Navigation lives in the left drawer and nowhere else. On a narrow viewport the drawer becomes a disclosure driven by a checkbox and a label -- no script is involved, because navigation that needs JavaScript to appear is navigation that is missing when a proxy strips a script. See `03-chrome.css`. The language chooser sits in that right-hand cluster too, between the theme toggle and the bell. It is six ordinary links back to the page the operator is on, so it needs no route, no form and no script: the `?lang=` on the link is what `painfree.ui.rendering` reads and answers with a cookie. Every string on every page comes through `t(...)`, which is the console's whole localisation surface -- and it stops there, because nothing on the wire is allowed to know a locale exists. #} {% import "_icons.html" as icons %} {% block title %}painfree{% endblock %} {# FIRST, and inline. It sets `data-theme` on before any of is parsed, so a reload of a page whose theme was chosen shows that theme in its first paint rather than flashing the default one. #} {# The mark of `_icons.html`, as the tab icon. Inline, so the browser does not ask for /favicon.ico -- a route this console does not have, which would be a 404 on every page in the developer console and a public path if it did. The same three shapes as `icons.logo()`, and shapes are the point: an SVG favicon is rendered by a code path that is under no obligation to have any font at all, so the `` element this replaces showed a blank blue tile wherever `system-ui` did not resolve. Geometry always resolves. #} {% block head %}{% endblock %} {{ t('chrome.skip') }} {# The drawer's switch. Off screen, still focusable, still in the accessibility tree: a keyboard reaches it with one Tab and opens it with Space. #}
{% if principal %} {% endif %} {{ icons.logo() }} painfree {{ t('chrome.subtitle') }}
{# Right to left from the edge: profile, notifications, theme. So in the document, left to right: theme, notifications, profile. #}
{# Six links, not a form and not a `` needs a script to act on a choice. Each one carries the page's own query string, so switching language on a filtered list keeps the filter. #}
{{ icons.language() }} {{ locale | upper }}

{{ t('lang.label') }}

{{ t('lang.note') }}

{# `name` makes the two menus an exclusive pair: opening one closes the other, in HTML, with no script and no Escape handler to forget. A browser that does not know the attribute lets both stand open, which is what they did before it existed. #}
{{ icons.bell() }} {% if alert_total %} {{ alert_total if alert_total < 100 else '99+' }} {% endif %}

{{ t('alerts.heading') }}

{% if alerts %} {% else %}

{{ t('alerts.none') }}

{% endif %}
{% if principal %}{{ (principal.display_name or principal.subject) | initials }}{% else %}?{% endif %}
{% if principal %}

{{ principal.display_name or principal.subject }}

{% if principal.display_name and principal.display_name != principal.subject %}

{{ principal.subject }}

{% endif %}

{% if principal.admin %} admin {{ t('profile.admin') }} {% elif principal.oversight %} oversight {{ t('profile.oversight') }} {% else %} member {% if principal.grants %} {{ t('profile.granted', count=principal.grants | length) }} {% else %} {{ t('profile.granted_none') }} {% endif %} {% endif %}

{% else %}

{{ t('profile.signed_out') }}

{% endif %}
{# No drawer before there is an identity. The sign-in and signed-out pages are the two that render without one, and a navigation column whose every entry answers `401` is not navigation. #} {% if principal %} {% endif %}
{# Shown on every page until somebody says a copy of the custody secret exists, because the alternative is saying it once, at provisioning, to an operator who may not be the one still here. #} {% if custody_unacknowledged %}
{{ t('recovery.banner_title') }}

{{ t('recovery.banner_body') }} {{ t('recovery.banner_link') }}

{% endif %} {% block body %}{% endblock %}