{# login.html — password prompt for one roster user. Rendered by routes/login.py (Jinja2, autoescape ON) and served through nginx's public `/auth/` location. Self-contained by necessity: the sidecar is its own container with no static-asset mount and no stylesheet to link, and this page must render on a deployment whose every other URL is behind the session it exists to create. So the CSS is inline and the palette is baked in. Template variables (contract with routes/login.py): user: str, required The roster user whose card was clicked. Shown in the prompt and carried in a hidden field. On an own card there is NO username input on this page, because the page is addressed per user and typing a different name here would only produce a login the server refuses; a SHARED card is the one exception — see `shared` below. shared: bool Whether this card is open to the whole roster (`access: any`). Off by default. When true, the form gains a visible `username` input where the person opening the card types their OWN roster name, and the heading reads "Sign in to " rather than "Enter password for ". opener: str Prefill for the shared card's `username` input — the opener's own roster name on a re-render, empty on a first prompt. Unused when `shared` is false. next: str, required The validated same-origin return-to. Already checked by `_safe_return_to()`; re-checked on POST, since a hidden field is client input like any other. error: str | None A message to show above the form. One fixed string for every refusal — an unknown user and a wrong password produce the identical page. login_path: str, required Where the form posts. Comes from the route module's own constant so the path is defined once. theme_blocks: list[dict], possibly empty Design-system token values to bake into the stylesheet, in the same shape the landing page uses: {"media": str | None, "color_scheme": str, "variables": [(name, value), ...]}. NOT `|e`-escaped — these are CSS, not HTML, and escaping would corrupt values (`rgba(0, 0, 0, .4)`) while doing nothing about the only real hazard in a
{#- The acceptance gate: the username is stated, never asked for — except on a shared card, where the person opening it names themselves below. #} {%- if shared %}

Sign in to

{%- else %}

Enter password for

{%- endif %} {%- if error %} {%- endif %}
{#- Hidden, not editable: the page is addressed per user, and the server re-validates both values on arrival. #} {%- if shared %} {#- The shared-card exception: anyone on the roster may open this card, so the opener names themselves here. `username` is the field the POST route reads the opener from. #} {%- else %} {%- endif %}