/* Blazor-specific additions to the design system in panel.css.
   Every colour here is a token, so these follow the light and dark themes and a licence's branding
   like the rest of the panel. */

/* The design system never resets the browser's 8px page margin, which left a gap around the
   sidebar and made every page, however short, scroll by 16px. The sidebar is drawn a full window
   tall, so the page sits flush against the window instead. */
body {
    margin: 0;
}

/* A navigation section with pages under it is a button that opens and closes them, drawn like the
   template's section links. Its chevron turns when it is open; the brand fill marks the section the
   page on show belongs to. */
.side-nav > button {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 9px 14px;
    border: 0;
    border-radius: 6px;
    background: none;
    color: var(--ink);
    font: inherit;
    font-size: 15px;
    font-weight: 500;
    text-align: left;
    cursor: pointer;
    transition: background .15s;
}

.side-nav > button .lucide {
    width: 20px;
    height: 20px;
    color: var(--ink);
}

.side-nav > button .chev {
    margin-left: auto;
    width: 16px;
    height: 16px;
    color: var(--faint);
    transition: transform .2s;
}

.side-nav > button:hover {
    background: var(--color-neutral-200);
}

.side-nav > button:focus-visible {
    outline: none;
    box-shadow: var(--brand-ring);
}

.side-nav > button.on {
    background: linear-gradient(72deg, var(--brand) 22%, var(--brand-l) 100%);
    color: var(--on-brand);
    box-shadow: var(--brand-sh);
}

.side-nav > button.on .lucide,
.side-nav > button.on .chev {
    color: var(--on-brand);
}

.side-nav > button.open .chev {
    transform: rotate(90deg);
}

/* The router focuses each page's heading after navigation, for screen readers. The focus is
   deliberate, but a ring around a heading reads as a fault to everybody else. */
h1:focus {
    outline: none;
}

/* A page-level heading inside an empty state: the not-found and error pages. */
.empty > h1 {
    margin: 0;
    font-size: 22px;
}

/* Form validation, used by EditForm. A field is marked only when something is wrong with it: a
   green border on every field somebody has typed in says nothing useful. */
.invalid {
    border-color: var(--err);
}

.validation-message {
    color: var(--err);
    font-size: 13px;
    margin-top: 4px;
}

/* An unhandled exception inside one component, caught by an ErrorBoundary. */
.blazor-error-boundary {
    padding: 14px 16px;
    border-radius: var(--radius-md);
    background: var(--err-bg);
    color: var(--err);
    font-weight: 500;
}

.blazor-error-boundary::after {
    content: "This part of the page could not be shown.";
}

/* The bar the framework shows after an unhandled error in the circuit. */
#blazor-error-ui {
    display: none;
    position: fixed;
    inset: auto 0 0 0;
    z-index: 1000;
    align-items: center;
    gap: 16px;
    padding: 12px 20px;
    background: var(--err);
    color: var(--on-brand);
    font-weight: 500;
    box-shadow: var(--shadow-lg);
}

#blazor-error-ui .reload {
    color: var(--on-brand);
    text-decoration: underline;
}

#blazor-error-ui .dismiss {
    margin-left: auto;
    border: 0;
    background: none;
    color: var(--on-brand);
    cursor: pointer;
}

/* First-run setup, which uses the sign-in page's frame from the design template. */
.auth-r-copy {
    display: flex;
    flex-direction: column;
    gap: 20px;
}

/* The picture beside the sign-in form.

   The panel it stands in is the one colour in this sheet that is not a token: it is the colour of
   the picture's own edges, so the picture sits in it without a seam, and no theme or branding
   changes a picture. The picture is cut a little on its left, where there is only a plant, to stand
   taller, and never on its right, where the device is. Its foot fades into the panel, and the words
   stand under it.

   From 900 pixels the picture stands beside the form, which is two hundred pixels sooner than the
   design template gave up on it. The panel is as tall as the window where the picture and the
   words fill that, and as tall as the two of them where the column is narrow, in the middle of the
   window beside the form. It stays in sight while a long form scrolls.

   Narrower than 900 pixels the picture lies above the form, as a banner with no words, and the
   form takes the middle of the page under it. */
.auth-r {
    position: sticky;
    top: 24px;
    align-self: start;
    justify-content: center;
    height: calc(100vh - 48px);
    height: calc(100dvh - 48px);
    padding: 0;
    overflow: visible;
    border-radius: 0;
    background: none;
}

/* The tilted square the template drew where a picture would one day go. */
.auth-r::after {
    content: none;
}

.auth-stage {
    --auth-stage: #251e3d;
    display: flex;
    flex-direction: column;
    min-height: 0;
    max-height: 100%;
    overflow: hidden;
    border-radius: 14px;
    background: var(--auth-stage);
    color: #fff;
}

.auth-pic {
    display: block;
    flex: 0 1 auto;
    width: 100%;
    height: auto;
    min-height: 0;
    aspect-ratio: 6 / 5;
    object-fit: cover;
    object-position: 78% 40%;
    -webkit-mask-image: linear-gradient(to bottom, #000 74%, transparent);
    mask-image: linear-gradient(to bottom, #000 74%, transparent);
}

.auth-r .auth-r-copy {
    flex: none;
    padding: 0 56px 48px;
}

.auth-r h2 {
    font-size: clamp(30px, 3.3vw, 48px);
    color: #fff;
    text-wrap: balance;
}

.auth-r p {
    color: rgb(255 255 255 / .76);
    text-wrap: pretty;
}

@media (min-width: 900px) and (max-width: 1100px) {
    .auth {
        grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    }

    .auth-r {
        display: flex;
    }

    .auth-r .auth-r-copy {
        padding: 0 36px 36px;
    }

    .auth-l {
        padding: 32px 36px;
    }
}

@media (max-width: 899.98px) {
    .auth {
        grid-template-columns: minmax(0, 480px);
        grid-template-rows: auto 1fr;
        justify-content: center;
        padding: 0 20px;
        background: var(--paper);
    }

    .auth-r {
        display: block;
        position: static;
        height: auto;
        margin: 20px 0 0;
    }

    .auth-pic {
        aspect-ratio: 16 / 9;
        object-position: 50% 40%;
        -webkit-mask-image: none;
        mask-image: none;
    }

    .auth-r .auth-r-copy {
        display: none;
    }

    .auth-l {
        padding: 24px 0;
    }

    .auth-form {
        max-width: none;
    }
}

/* A phone: a shallower banner, so the form under it starts in sight. */
@media (max-width: 599.98px) {
    .auth-pic {
        aspect-ratio: 2 / 1;
    }
}

/* A phone on its side has no height to spare for a picture above the form. */
@media (max-width: 899.98px) and (max-height: 560px) {
    .auth-r {
        display: none;
    }
}

.setup-intro {
    margin: 0;
    color: var(--muted);
}

.setup-form {
    display: flex;
    flex-direction: column;
    gap: 18px;
}

.setup-server {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 7.5rem;
    gap: 16px;
}

.setup-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 4px;
}

.setup-actions .btn {
    min-height: 42px;
}

.setup-actions .btn-primary {
    flex: 1;
    justify-content: center;
}

.setup-note {
    margin: 0;
    font-size: 13px;
    color: var(--muted);
    overflow-wrap: anywhere;
}

.setup-note code {
    color: var(--ink);
}

/* A text field with a button inside its right-hand end, such as a password's Show. */
.input-reveal {
    position: relative;
}

.input-reveal .input,
.input-reveal .input:focus {
    width: 100%;
    padding-right: 84px;
}

.input-reveal .btn {
    position: absolute;
    top: 3px;
    right: 4px;
}

/* The design template draws checkboxes itself. The real input stays in the page, out of sight, so the
   box still works from the keyboard and is still announced. */
.check {
    position: relative;
}

.check input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    opacity: 0;
}

.check:has(input:focus-visible) .box {
    box-shadow: var(--brand-ring);
}

/* Read by screen readers, not shown. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* The real checkbox inside a drawn one, as sign in and every interactive page render it. The design
   template marks a ticked box with a class; a real input marks it by being checked. */
.check:has(> input:checked) .box {
    background: var(--brand);
    border-color: var(--brand);
    box-shadow: var(--brand-sh);
}

.check:has(> input:checked) .box::after {
    content: "";
    position: absolute;
    left: 5px;
    top: 1px;
    width: 5px;
    height: 10px;
    border: solid var(--paper);
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}

.check.locked:has(> input:checked) .box {
    background: var(--color-accent-300);
    border-color: var(--color-accent-300);
    box-shadow: none;
}

.check:has(> input:disabled) {
    cursor: not-allowed;
}

.check.ro,
.check.ro:has(> input:disabled) {
    cursor: default;
}

/* The on and off switch, drawn by the template, with the real checkbox kept for the keyboard. */
.tog {
    position: relative;
}

.tog input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    opacity: 0;
}

.tog:has(input:focus-visible) .sw {
    box-shadow: var(--brand-ring);
}

/* A label for a group of controls that is not itself a single input. */
.field-label {
    color: var(--ink);
    font-size: 13.5px;
    margin-bottom: 7px;
}

/* A dialog over the page. */
.modal {
    position: fixed;
    inset: 0;
    z-index: 60;
    display: grid;
    place-items: center;
    padding: 16px;
    outline: none;
}

.modal-scrim {
    position: absolute;
    inset: 0;
    background: var(--scrim);
}

.modal .dialog {
    position: relative;
    width: 100%;
    max-width: 480px;
    max-height: calc(100dvh - 32px);
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: 24px;
    border-radius: 10px;
    background: var(--paper);
    box-shadow: var(--shadow-lg);
}

.modal .dialog.wide {
    max-width: 640px;
}

.modal .dialog-body {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.modal .dialog-body p {
    margin: 0;
}

.modal .dialog-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}

@media (max-width: 760px) {
    .modal {
        padding: 10px;
    }

    .modal .dialog {
        padding: 18px;
    }

    .modal .dialog-actions .btn {
        flex: 1 1 auto;
        justify-content: center;
    }
}

/* Lists that load their rows as they scroll. The rows scroll inside the list, under headings that
   stay put, and the list is only ever as tall as the screen allows. */
.list-sec {
    gap: 14px;
}

.tbl-wrap.vlist {
    max-height: max(360px, calc(100dvh - 300px));
    overflow: auto;
    overscroll-behavior: contain;
}

.vlist thead th {
    position: sticky;
    top: 0;
    z-index: 1;
}

.vtable {
    width: 100%;
}

.vtable td.c-acts {
    width: 1%;
    white-space: nowrap;
}

.vtable .c-users {
    width: 1%;
}

/* The rows the list has not loaded yet, while it fetches them. */
.row-wait td {
    padding-top: 12px;
    padding-bottom: 12px;
}

.skel {
    display: block;
    height: 36px;
    border-radius: 6px;
    background: linear-gradient(90deg, var(--color-neutral-200), var(--color-neutral-100), var(--color-neutral-200));
    background-size: 200% 100%;
    animation: skel 1.2s ease-in-out infinite;
}

@keyframes skel {
    from { background-position: 100% 0; }
    to { background-position: -100% 0; }
}

.row-empty td {
    padding: 28px 16px;
    color: var(--muted);
}

.row-empty b {
    color: var(--head);
}

/* The spacer rows the list uses to stand in for rows out of view. They carry only a height. */
.vtable tbody tr[aria-hidden="true"],
.vtable tbody tr[aria-hidden="true"]:hover {
    padding: 0;
    border: 0;
    background: none;
}

/* A column heading that sorts. */
.th-sort {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: inherit;
    cursor: pointer;
}

.th-sort .lucide {
    width: 14px;
    height: 14px;
    color: var(--faint);
}

th[aria-sort="ascending"] .th-sort .lucide,
th[aria-sort="descending"] .th-sort .lucide {
    color: var(--brand-ink);
}

.th-sort:focus-visible {
    outline: none;
    box-shadow: var(--brand-ring);
    border-radius: 4px;
}

.ph h1 .count {
    font-weight: 600;
    margin-left: 4px;
}

.toolbar-note {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13.5px;
}

.who-name {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px 8px;
}

/* The template draws ink badges and banners in the heading colour with white text. In the dark theme
   the heading colour is near white, so the text vanished. The card colour reads against the heading
   colour in both themes: white on dark ink in the light theme, dark on light ink in the dark one. */
.b-ink,
.banner.ink {
    color: var(--paper);
}

.who-name .badge {
    font-size: 11.5px;
    padding: 0 7px;
}

.vtable .email-under,
.vtable .desc-under,
.vtable .card-l {
    display: none;
}

/* Column headings line up with their column's content, as the design template draws them. The
   permission grid centres its own. */
.table:not(.perm) th {
    text-align: left;
}

.table:not(.perm) th.num {
    text-align: right;
}

.toolbar .search .input {
    width: 100%;
}

/* Wide lists fold their second column under the first before they would need to scroll sideways.
   The users list has more columns than the roles list, so it folds sooner. */
@media (max-width: 1500px) {
    .vtable .c-email {
        display: none;
    }

    .vtable .email-under {
        display: block;
    }
}

@media (max-width: 1300px) {
    .vtable .c-desc {
        display: none;
    }

    .vtable .desc-under {
        display: block;
    }
}

/* On a phone or a narrow tablet each row becomes a card: who or what it is, and its actions, on the
   first line, and the rest beneath. The sorting headings stay as a row of buttons. */
@media (max-width: 900px) {
    .tbl-wrap.vlist > .vtable {
        min-width: 0;
    }

    .vtable,
    .vtable tbody {
        display: block;
    }

    .vtable thead {
        display: block;
        position: sticky;
        top: 0;
        z-index: 1;
        background: var(--paper);
    }

    .vtable thead tr {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 4px 16px;
        padding: 10px 14px;
        border-bottom: 1px solid var(--rule);
    }

    .vtable thead th {
        display: none;
    }

    .vtable thead th.sortable {
        display: block;
        position: static;
        padding: 0;
        border: 0;
        font-size: 12px;
    }

    /* A list with nothing to sort by has no row of buttons to keep. */
    .vtable thead:not(:has(th.sortable)) {
        display: none;
    }

    .vtable tbody tr {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 8px 12px;
        padding: 12px 14px;
        border-bottom: 1px solid var(--rule-soft);
    }

    .vtable tbody td {
        display: block;
        padding: 0;
        border: 0;
    }

    /* Who or what the row is takes the whole first line, so a long name or email is never
       squeezed. The details follow, with the row's actions at the end of them. */
    .vtable tbody tr {
        row-gap: 0;
    }

    .vtable td.c-main {
        order: 0;
        flex: 1 1 100%;
        min-width: 0;
    }

    .vtable tbody td:not(.c-main):not(.c-acts) {
        order: 3;
        margin-top: 8px;
    }

    .vtable td.c-acts {
        order: 5;
        flex: 0 0 auto;
        width: auto;
        margin-top: 8px;
        margin-left: auto;
    }

    .vtable tbody td.c-users,
    .vtable tbody td.c-acts {
        width: auto;
    }

    .vtable .card-hide {
        display: none;
    }

    .vtable td.c-acts small {
        display: none;
    }

    .vtable td.c-email,
    .vtable td.c-desc {
        display: none;
    }

    .vtable td.c-main .email-under,
    .vtable td.c-main .desc-under {
        display: block;
        overflow-wrap: anywhere;
    }

    .vtable tbody td:not(.c-main):not(.c-acts) {
        font-size: 13.5px;
    }

    .vtable .card-l {
        display: inline;
    }

    .vtable .num {
        text-align: left;
    }

    .vtable .row-wait td,
    .vtable .row-empty td {
        flex: 1 1 100%;
    }

    /* A role's reach is a phrase of its own, on a line under its users, type and actions. */
    .vtable.roles-t tbody td.c-reach {
        order: 6;
        flex-basis: 100%;
    }
}

/* The account menu in the top bar. */
.acct-menu {
    position: relative;
}

button.acct {
    border: 0;
    background: none;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

button.acct:focus-visible {
    outline: none;
    box-shadow: var(--brand-ring);
}

/* The layer that closes the account menu on a click anywhere else. It sits just beneath the top bar,
   whose menu stays above it, and above the page. */
.acct-scrim {
    position: fixed;
    inset: 0;
    z-index: 4;
}

.acct-pop {
    position: absolute;
    right: 0;
    top: calc(100% + 8px);
    z-index: 40;
    min-width: 250px;
    max-width: min(320px, calc(100vw - 24px));
    display: flex;
    flex-direction: column;
    padding: 6px;
    border-radius: 8px;
    background: var(--paper);
    box-shadow: var(--shadow-lg);
}

.acct-who {
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 10px 12px 12px;
    margin-bottom: 4px;
    border-bottom: 1px solid var(--rule-soft);
    overflow-wrap: anywhere;
}

.acct-who b {
    color: var(--head);
    font-size: 14.5px;
}

.acct-who small {
    color: var(--muted);
    font-size: 13px;
}

.acct-pop a,
.acct-pop button {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 12px;
    border: 0;
    border-radius: 6px;
    background: none;
    color: var(--ink);
    font: inherit;
    font-size: 14.5px;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.acct-pop a:hover,
.acct-pop button:hover,
.acct-pop a:focus-visible,
.acct-pop button:focus-visible {
    outline: none;
    background: var(--color-neutral-200);
}

.acct-pop .lucide {
    width: 17px;
    height: 17px;
    color: var(--muted);
}

/* Sign in, two factor and choosing a password. */
.auth-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px 16px;
    font-size: 14px;
}

.auth-row a {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.auth-row .lucide {
    width: 16px;
    height: 16px;
}

.inline-form {
    display: inline;
    margin: 0;
}

.link-btn {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: var(--brand-ink);
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
}

.code-input {
    font-size: 20px;
    letter-spacing: .18em;
    font-variant-numeric: tabular-nums;
    max-width: 16rem;
}

/* The Show and Hide control inside a password field on a page without a live connection. */
[data-reveal][aria-pressed="true"] .rv-show,
[data-reveal]:not([aria-pressed="true"]) .rv-hide {
    display: none;
}

.pw-check {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.rules li.ok .if-no,
.rules li.no .if-ok {
    display: none;
}

.page-loading {
    padding: 48px 0;
    color: var(--muted);
    text-align: center;
}

.back-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13.5px;
    font-weight: 500;
    color: var(--muted);
    text-decoration: none;
}

.back-link:hover {
    color: var(--brand-ink);
}

.back-link .lucide {
    width: 15px;
    height: 15px;
}

.sec-p {
    margin: 0;
}

.sub-block {
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding-top: 18px;
    border-top: 1px solid var(--rule-soft);
}

.sub-block h3 {
    margin: 0;
    font-size: 16px;
}

.sub-block p {
    margin: -8px 0 0;
    font-size: 13.5px;
}

.danger-text,
.danger-text:hover {
    color: var(--err);
}

/* A confirmation above the page that can be put away. */
.callout.notice {
    align-items: center;
}

.callout.notice > div {
    flex: 1;
}

.callout.notice > .btn-icon {
    flex: none;
}

/* A role in plain words. */
.role-sum {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.role-sum-text {
    margin: 0;
    font-size: 15px;
    line-height: 1.6;
    color: var(--ink);
    text-wrap: pretty;
}

.role-sum-text b,
.role-sum-list b {
    color: var(--head);
}

.role-sum-list {
    margin: -4px 0 0;
    padding-left: 20px;
    display: flex;
    flex-direction: column;
    gap: 3px;
    font-size: 14.5px;
    color: var(--ink);
}

.role-sum .lbl {
    margin-top: 4px;
}

.pills {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

/* My account. */
.me-head {
    display: flex;
    align-items: center;
    gap: 18px;
}

.me-head > div {
    min-width: 0;
}

.me-line {
    overflow-wrap: anywhere;
}

.codes {
    list-style: none;
    margin: 0;
    padding: 14px;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px 18px;
    border-radius: 8px;
    background: var(--color-neutral-100);
    border: 1px solid var(--rule);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 16px;
    letter-spacing: .06em;
    color: var(--head);
    font-variant-numeric: tabular-nums;
}

.table.fit {
    width: 100%;
}

/* Setting up two factor. */
.tf-step {
    display: flex;
    gap: 14px;
    align-items: flex-start;
}

.tf-step h2 {
    margin: 2px 0 4px;
    font-size: 17px;
}

.tf-n {
    flex: none;
    width: 30px;
    height: 30px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--brand-t);
    color: var(--brand-ink);
    font-weight: 700;
}

img.qr {
    width: 240px;
    height: 240px;
    max-width: 100%;
    margin-left: 44px;
}

.tf-key {
    margin-left: 44px;
    font-size: 14px;
}

.tf-key summary {
    cursor: pointer;
    color: var(--brand-ink);
    font-weight: 500;
}

.tf-key-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 12px;
    margin: 10px 0 6px;
}

.tf-key code {
    font-size: 15px;
    letter-spacing: .08em;
    color: var(--head);
    overflow-wrap: anywhere;
}

.tf-confirm {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 12px 16px;
    margin-left: 44px;
}

.tf-confirm .field {
    flex: 0 1 16rem;
}

.tf-codes {
    max-width: 640px;
}

@media (max-width: 760px) {
    img.qr,
    .tf-key,
    .tf-confirm {
        margin-left: 0;
    }

    img.qr {
        align-self: center;
    }

    .tf-confirm .btn {
        flex: 1 1 100%;
        justify-content: center;
    }

    .me-head {
        gap: 12px;
    }
}

@media (max-width: 420px) {
    .codes {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* The role editor's grid of permissions. It gets the wider share of the page, and its tick box
   columns stay narrow, so the extra actions have room beside them. */
@media (min-width: 1201px) {
    .split.role-split {
        grid-template-columns: minmax(0, 2.3fr) minmax(0, 1fr);
    }
}

.perm-wrap .table {
    width: 100%;
}

.perm th,
.perm td {
    padding-left: 8px;
    padding-right: 8px;
}

.perm th:first-child,
.perm td:first-child {
    padding-left: 14px;
}

.perm td.mod {
    min-width: 150px;
}

.perm td.mod b {
    margin-right: 6px;
}

.perm td.mod .pill {
    margin-right: 6px;
}

.perm .row-all {
    min-height: 26px;
    padding: 2px 8px;
    opacity: .7;
}

.perm tr:hover .row-all,
.perm .row-all:focus-visible {
    opacity: 1;
}

.perm th.ex,
.perm td.ex {
    text-align: left;
}

.perm .extras {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 16px;
}

.perm .extra {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.perm .check {
    align-items: center;
}

.perm .check > span:empty {
    display: none;
}

.perm-l {
    display: none;
}

.perm tr.sel-all td {
    padding-top: 6px;
    padding-bottom: 6px;
}

.perm tr.sel-all .btn {
    margin: auto;
}

.perm.ro .check {
    cursor: default;
}

.legend .check {
    pointer-events: none;
}

/* The plain words and the navigation preview stay in view beside a long grid. */
@media (min-width: 1201px) {
    .sticky-side {
        position: sticky;
        top: 88px;
    }
}

/* On a phone the grid becomes one block per part of the panel, each action named beside its box,
   and an action a part does not offer is simply not listed. */
@media (max-width: 760px) {
    .perm-wrap > table.perm {
        min-width: 0;
    }

    .perm,
    .perm tbody {
        display: block;
    }

    .perm thead {
        display: none;
    }

    .perm tbody tr {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 10px 14px;
        padding: 12px 14px;
        border-bottom: 1px solid var(--rule-soft);
    }

    .perm tbody td,
    .perm tbody td:first-child {
        display: block;
        padding: 0;
        border: 0;
    }

    .perm .check {
        gap: 8px;
    }

    .perm td.mod {
        flex: 1 1 100%;
        white-space: normal;
    }

    .perm td:has(> .na),
    .perm td.ex:not(:has(.extra)) {
        display: none;
    }

    .perm td.ex {
        flex: 1 1 100%;
    }

    .perm-l {
        display: inline;
    }

    .perm tr.grp {
        padding: 8px 14px;
        background: var(--color-neutral-100);
    }

    .perm tr.grp td {
        background: none;
    }

    .perm tr.sel-all {
        gap: 6px 8px;
    }

    .perm tr.sel-all td:first-child {
        flex: 1 1 100%;
    }

    .perm tr.sel-all .btn {
        margin: 0;
        gap: 0;
    }

    .perm .row-all {
        opacity: 1;
    }
}

/* Where somebody's sites are named: in the users list and on their own account page. */
.loc-line {
    display: inline-flex;
    align-items: flex-start;
    gap: 6px;
}

.loc-line .lucide {
    width: 15px;
    height: 15px;
    margin-top: 3px;
    flex: none;
    color: var(--muted);
}

.vtable td.c-loc {
    max-width: 16rem;
}

/* The locations somebody can be given, as the tree they sit in, each group able to tick everything
   in it. A long list scrolls inside its box rather than stretching the page. */
.loc-picks {
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-height: 22rem;
    overflow-y: auto;
    padding: 12px 14px;
    border: 1px solid var(--rule);
    border-radius: 6px;
}

.loc-picks .check {
    padding-left: calc(min(var(--depth, 0), 6) * 22px);
}

.loc-picks .check.group > span {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    font-weight: 600;
}

.loc-picks .check.group .lucide {
    width: 16px;
    height: 16px;
    color: var(--muted);
}

.loc-picks .check.group small {
    font-weight: 400;
}

/* A group some of whose locations are ticked: a dash rather than a tick. */
.check.some .box {
    background: var(--brand);
    border-color: var(--brand);
}

.check.some .box::after {
    content: "";
    position: absolute;
    left: 3px;
    top: 7px;
    width: 9px;
    height: 2px;
    background: var(--paper);
}

/* SET-03. The departments list: a tick box for merging, the name, and its actions. */
.dept-t th.c-pick,
.dept-t td.c-pick {
    width: 48px;
    padding-right: 0;
}

.dept-t td.c-pick .check {
    vertical-align: middle;
}

/* On a phone each department stays one line: tick box, name, members, actions. */
@media (max-width: 900px) {
    .vtable.dept-t tbody tr {
        flex-wrap: nowrap;
    }

    .vtable.dept-t tbody td.c-pick {
        order: 0;
        flex: none;
        width: auto;
        margin-top: 0;
    }

    /* A ticked row is one card, not three separately shaded cells. */
    .vtable.dept-t tbody tr.sel {
        background: var(--brand-t);
    }

    .vtable.dept-t tbody tr.sel td {
        background: none;
    }

    .vtable.dept-t td.c-main {
        order: 1;
        flex: 1 1 0;
    }

    .vtable.dept-t tbody td.c-users {
        order: 2;
        margin-top: 0;
        color: var(--muted);
        white-space: nowrap;
    }

    .vtable.dept-t td.c-acts {
        order: 3;
        margin-top: 0;
    }
}

/* SET-15. The locations tree, beside the place being changed or added. */
.loc-tree {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* The template's own row rule is joined to the line before it, so it never applies on its own; the
   rows are drawn in full here. Groups nest as deeply as an organisation needs, so each row steps in
   by its depth, up to a limit that keeps deep rows readable. */
.loc-tree .tree-i {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
    padding-top: 9px;
    padding-bottom: 9px;
    padding-right: 12px;
    padding-left: calc(12px + min(var(--depth, 0), 6) * 24px);
    border-radius: 6px;
    font-size: 14.5px;
    color: var(--ink);
    text-decoration: none;
}

.loc-tree .tree-i > .lucide {
    flex: none;
}

.loc-tree .tree-t {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0 8px;
    min-width: 0;
}

.loc-tree .tree-t b {
    font-weight: 600;
    overflow-wrap: anywhere;
}

.loc-tree .tree-t small {
    font-size: 13px;
}

.loc-tree .tree-n {
    flex: none;
    font-size: 12.5px;
    white-space: nowrap;
}

/* A place shown only because something inside it matches the search. */
.loc-tree .tree-i.ctx {
    opacity: .6;
}

.loc-tree .tree-i:focus-visible {
    outline: none;
    box-shadow: var(--brand-ring);
}

.loc-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 32px 16px;
    text-align: center;
}

.loc-empty > .lucide,
.loc-kind {
    display: grid;
    place-items: center;
    flex: none;
    border-radius: 8px;
    background: var(--brand-t);
    color: var(--brand-ink);
}

.loc-empty > .lucide {
    width: 44px;
    height: 44px;
    padding: 10px;
}

.loc-empty h2 {
    margin: 0;
    font-size: 18px;
}

.loc-empty p {
    margin: 0;
    max-width: 46ch;
    color: var(--muted);
}

.loc-empty .actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
}

/* Group or location, chosen once when adding. */
.seg.loc-seg {
    display: inline-flex;
    align-self: flex-start;
}

.loc-seg .seg-opt {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
}

.loc-seg .seg-opt input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.loc-seg .seg-opt:has(input:focus-visible) {
    box-shadow: inset 0 0 0 2px var(--brand);
}

.loc-seg .seg-opt .lucide {
    width: 16px;
    height: 16px;
}

.loc-kind {
    width: 38px;
    height: 38px;
}

.loc-kind .lucide {
    width: 20px;
    height: 20px;
}

.sec-h.loc-h {
    justify-content: flex-start;
    flex-wrap: nowrap;
    gap: 12px;
}

.loc-h > div {
    min-width: 0;
}

.loc-h h2 {
    overflow-wrap: anywhere;
}

.loc-h small {
    display: block;
    margin-top: 2px;
    font-size: 13px;
}

.sec.loc-idle {
    flex-direction: row;
    align-items: center;
    gap: 12px;
    color: var(--muted);
}

.loc-idle > .lucide {
    flex: none;
}

.loc-idle p {
    margin: 0;
}

.loc-adds .actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.loc-back {
    display: none;
    align-self: flex-start;
}

/* The place being changed stays in view beside a long tree. */
@media (min-width: 1201px) {
    .loc-side {
        position: sticky;
        top: 88px;
    }
}

/* A phone steps each level in less, and stops sooner. */
@media (max-width: 760px) {
    .loc-tree .tree-i {
        padding-left: calc(8px + min(var(--depth, 0), 4) * 16px);
    }
}

/* Once the two columns stack, the place being changed comes before the tree, with a way back, and
   the prompt to choose one is left out, since the tree itself is right there. */
@media (max-width: 1200px) {
    .loc-split.editing .loc-side {
        order: -1;
    }

    .sec.loc-idle {
        display: none;
    }

    .loc-split.editing .loc-back {
        display: inline-flex;
    }
}

/* MEM-01. The member list. Before it would need to scroll sideways, the Emirates ID folds under the
   name and the consent column becomes a badge beside the name of anybody without it, which leaves
   room for how many devices each member is on. */
.mem-t .eid,
.mem-t .eid-under,
.mem-head .sub {
    font-variant-numeric: tabular-nums;
}

.mem-t .eid {
    white-space: nowrap;
}

.mem-t .eid-warn .lucide {
    width: 15px;
    height: 15px;
    margin-left: 4px;
    vertical-align: -2px;
    color: var(--warn);
}

.mem-t .eid-under,
.mem-t .consent-under {
    display: none;
}

.mem-t .loc-groups {
    display: block;
    font-size: 12.5px;
}

.mem-t td.c-last,
.mem-t td.c-consent,
.mem-t td.c-dev,
.mem-t td.c-type {
    white-space: nowrap;
}

.mem-t td.c-dev {
    font-variant-numeric: tabular-nums;
}

.mem-toolbar select.input {
    max-width: 16rem;
}

@media (max-width: 1700px) {
    .vtable.mem-t .c-eid,
    .vtable.mem-t .c-consent {
        display: none;
    }

    .vtable.mem-t .eid-under {
        display: block;
    }

    .vtable.mem-t .consent-under {
        display: inline-flex;
    }
}

/* Narrower still, how many devices they are on goes under the name too, beside the Emirates ID,
   until the list becomes cards, where it has a line of its own. */
.mem-t .dev-under {
    display: none;
}

@media (min-width: 901px) and (max-width: 1300px) {
    .vtable.mem-t .c-dev {
        display: none;
    }

    .vtable.mem-t .dev-under {
        display: inline;
    }
}

/* On a phone each member is a card: who they are and their Emirates ID, then their type,
   department, location and when they were registered. */
@media (max-width: 900px) {
    .vtable.mem-t td.c-eid,
    .vtable.mem-t td.c-consent {
        display: none;
    }

    .vtable.mem-t td.c-loc {
        max-width: none;
    }

    .vtable.mem-t td.c-dept::before {
        content: "·";
        margin-right: 12px;
        color: var(--faint);
    }

    .vtable.mem-t .loc-groups {
        display: inline;
        margin-left: 6px;
    }

    .mem-toolbar select.input {
        flex: 1 1 10rem;
        max-width: none;
    }
}

/* On a phone the location takes a line of its own, so every card has the same shape whatever the
   names' lengths. */
@media (max-width: 760px) {
    .vtable.mem-t tbody td.c-loc {
        flex-basis: 100%;
    }
}

/* MEM-03. The devices a member is on: each device with whether it has them, and under that why
   not, in words. The name and the state share a line and the state drops under the name when the
   two do not fit. */
.held-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.held-list li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 4px 12px;
    padding: 12px 0;
    border-top: 1px solid var(--rule-soft);
}

.held-list li:first-child {
    border-top: 0;
    padding-top: 0;
}

.held-list li:last-child {
    padding-bottom: 0;
}

.held-who {
    min-width: 0;
    font-weight: 600;
    color: var(--head);
    overflow-wrap: anywhere;
}

.held-who small {
    display: block;
    font-weight: 400;
    font-size: 12.5px;
}

.held-why {
    flex: 1 1 100%;
    margin: 0;
    font-size: 13.5px;
    color: var(--muted);
}

.held-note {
    margin: 12px 0 0;
    font-size: 13.5px;
}

/* MEM-03. The member's initials beside their name, with what the device recorded under it. */
.mem-head {
    align-items: center;
    margin-top: 6px;
}

.mem-head h1 {
    margin: 0;
}

.mem-head .sub {
    margin-top: 2px;
}

/* MEM-09. The member types list: the name, whether it has a department, its members, and its
   actions. On a phone each type is a card of two lines: its name and actions, then its department
   rule and its members. */
.mtype-t td.c-dept {
    white-space: nowrap;
}

@media (max-width: 900px) {
    .vtable.mtype-t td.c-main {
        flex: 1 1 0;
    }

    .vtable.mtype-t td.c-acts {
        order: 1;
        margin-top: 0;
    }

    /* Breaks the card's line after the actions. */
    .vtable.mtype-t tbody tr::after {
        content: "";
        order: 2;
        flex-basis: 100%;
    }

    .vtable.mtype-t tbody td.c-dept,
    .vtable.mtype-t tbody td.c-users {
        order: 3;
    }

    .vtable.mtype-t tbody td.c-users {
        color: var(--muted);
    }
}

/* SET-05 to SET-12. A slider drawn as the template draws one, with a real range input over it that
   nobody sees but everybody uses. */
.range-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.range .range-in {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.range .range-in:disabled {
    cursor: default;
}

.range:has(.range-in:focus-visible) .thumb {
    box-shadow: var(--brand-ring);
}

.range.dis {
    opacity: .6;
}

/* Days of the week as toggles. */
.day-picks .day-pick {
    border: 0;
    padding: 5px 10px;
    font: inherit;
    font-size: 13px;
    cursor: pointer;
}

.day-picks .day-pick.on {
    background: var(--grad-brand);
    color: var(--on-brand);
}

.day-picks .day-pick:disabled {
    cursor: default;
}

.day-picks .day-pick:focus-visible {
    outline: none;
    box-shadow: var(--brand-ring);
}

/* A choice between a few named options, such as the camera. */
.seg.choice-seg {
    display: inline-flex;
    align-self: flex-start;
}

.choice-seg .seg-opt {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
}

.choice-seg .seg-opt input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.choice-seg .seg-opt:has(input:focus-visible) {
    box-shadow: inset 0 0 0 2px var(--brand);
}

.choice-seg .seg-opt:has(input:disabled) {
    cursor: default;
}

.last-change {
    font-size: 13px;
}

.certainty-v,
.optcard-t {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 10px;
}

.certainty-v small,
.optcard-t small {
    flex-basis: 100%;
    color: var(--muted);
    font-size: 13.5px;
}

.certainty-change,
.holiday-add {
    align-self: flex-start;
}

.unit-input {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px 10px;
}

.unit-input .input {
    width: 7rem;
}

.unit-input span {
    color: var(--muted);
    font-size: 14px;
}

/* The check-in and check-out periods, shown under the choice that uses them. */
.scan-periods {
  margin-top: 12px;
}

.fixed-rule {
    padding: 12px;
}

/* SET-06. Shifts and public holidays. */
.shift-t td.c-hours,
.shift-t td.c-days,
.shift-t td.c-main b {
    white-space: nowrap;
}

.shift-t .hours {
    font-variant-numeric: tabular-nums;
}

.shift-t td.c-hours small,
.holiday-n small,
.version-t td.c-main small {
    display: block;
    font-size: 12.5px;
}

.tbl-wrap.vlist.shift-list,
.tbl-wrap.vlist.consent-versions {
    max-height: 480px;
}

.year-pick {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

.year-pick b {
    min-width: 3.2em;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.shift-form,
.holiday-form {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.dept-picks {
    display: flex;
    flex-direction: column;
    gap: 6px;
    max-height: 220px;
    overflow: auto;
    padding: 4px 2px;
}

.dept-picks .check small {
    margin-left: 8px;
}

/* SET-07. */
.loss-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 0 0 12px;
    padding-left: 20px;
}

/* SET-08. The wording, as written and as read. */
.consent-text {
    min-height: 220px;
    line-height: 1.6;
    resize: vertical;
    font-family: inherit;
}

.consent-box.consent-read {
    max-height: none;
    white-space: pre-line;
}

.consent-since {
    margin: 0;
}

.link-btn {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-weight: 600;
    color: var(--head);
    text-align: left;
    cursor: pointer;
}

.link-btn:hover {
    color: var(--brand-ink);
}

.link-btn:focus-visible {
    outline: none;
    box-shadow: var(--brand-ring);
    border-radius: 4px;
}

.version-t tbody tr.sel {
    background: var(--brand-t);
}

.consent-again {
    margin-left: 8px;
}

.sec-h h2 .consent-badge {
    margin-left: 10px;
    vertical-align: middle;
    font-size: 13px;
}

/* On a member's page, the way to the wording they agreed to. */
.link-btn.consent-view {
    margin-top: 4px;
    font-weight: 500;
    font-size: 13.5px;
    color: var(--brand-ink);
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* DEV-01. The device list: counts by state that filter the list, and each device's state, settings
   and members at a glance. */
.dev-counts {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 18px;
}

.dev-count {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    border: 1px solid var(--rule);
    border-radius: 8px;
    background: var(--paper);
    color: var(--ink);
    font: inherit;
    font-size: 14px;
    cursor: pointer;
    box-shadow: var(--shadow-sm);
}

.dev-count b {
    font-family: var(--font-heading);
    font-size: 18px;
    font-variant-numeric: tabular-nums;
    color: var(--head);
}

.dev-count span:last-child {
    color: var(--muted);
}

.dev-count:hover {
    border-color: var(--brand);
}

.dev-count:focus-visible {
    outline: none;
    box-shadow: var(--brand-ring);
}

.dev-count.on {
    border-color: var(--brand);
    background: var(--brand-t);
}

.dev-count.attn b {
    color: var(--warn);
}

.dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex: none;
}

.dot.d-ok { background: var(--ok-base); }
.dot.d-warn { background: var(--warn-base); }
.dot.d-neu { background: var(--color-neutral-500); }
.dot.d-err { background: var(--err-base); }

.badge .dot {
    margin-right: 2px;
}

.dev-ic {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border-radius: 10px;
    background: var(--brand-t);
    color: var(--brand-ink);
    flex: none;
}

.dev-ic .lucide {
    width: 18px;
    height: 18px;
}

.dev-ic.lg {
    width: 48px;
    height: 48px;
    border-radius: 14px;
}

.dev-ic.lg .lucide {
    width: 24px;
    height: 24px;
}

.dev-t small.muted {
    display: block;
    font-size: 12.5px;
    font-variant-numeric: tabular-nums;
}

.dev-t td.c-num small.muted {
    display: inline;
}

.dev-t .loc-groups {
    display: block;
    font-size: 12.5px;
}

.dev-t td.c-state,
.dev-t td.c-last,
.dev-t td.c-conf,
.dev-t td.c-num {
    white-space: nowrap;
}

.dev-empty .lucide {
    color: var(--brand-ink);
}

@media (max-width: 1300px) {
    .vtable.dev-t .c-conf {
        display: none;
    }
}

@media (max-width: 900px) {
    .vtable.dev-t td.c-loc {
        max-width: none;
        flex-basis: 100%;
    }

    .vtable.dev-t .loc-groups {
        display: inline;
        margin-left: 6px;
    }
}

/* DEV-02. One device: its state beside its location in the heading, then health, members and
   identity side by side, folding to one column. */
.dev-sub {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 12px;
    margin: 4px 0 0;
}

.dev-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
    gap: 24px;
    align-items: start;
}

.dev-grid .kv dd {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 8px;
}

.dev-grid .kv dd small {
    flex-basis: 100%;
}

/* How the device shares members takes the whole width under the three cards, so its sentences
   read as sentences. */
.dev-grid .dev-wide {
    grid-column: 1 / -1;
}

/* The other devices of its location, each with whether this one reached it. */
.reach-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
    flex-basis: 100%;
    min-width: 0;
}

.reach-list li {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 8px;
    min-width: 0;
}

.reach-list a {
    overflow-wrap: anywhere;
}

.meter {
    display: inline-block;
    width: 64px;
    height: 8px;
    border-radius: 4px;
    background: var(--rule-soft);
    overflow: hidden;
}

.meter i {
    display: block;
    height: 100%;
    background: var(--ok-base);
}

.meter i.low {
    background: var(--warn-base);
}

/* DEV-03. Pairing: three steps across the top, the device described first, then a large code with
   everything the person at the door needs beside it. */
.pair-steps {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 28px;
    list-style: none;
    margin: 0 0 22px;
    padding: 0;
    font-size: 14px;
    color: var(--faint);
}

.pair-steps li {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.pair-steps span {
    display: grid;
    place-items: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    border: 1.5px solid var(--rule);
    font-size: 12.5px;
    font-weight: 600;
}

.pair-steps li.now {
    color: var(--head);
    font-weight: 600;
}

.pair-steps li.now span {
    border-color: var(--brand);
    background: var(--brand);
    color: var(--on-brand);
}

.pair-steps li.done {
    color: var(--muted);
}

.pair-steps li.done span {
    border-color: var(--ok-base);
    color: var(--ok);
}

.fgrid.one {
    grid-template-columns: minmax(0, 1fr);
}

.addr-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.addr-list li {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.addr-list .lucide {
    width: 16px;
    height: 16px;
    color: var(--faint);
    flex: none;
}

.addr-list code,
.pair-addr {
    overflow-wrap: anywhere;
    font-size: 14px;
}

.pair-grid {
    display: grid;
    grid-template-columns: minmax(0, auto) minmax(0, 1fr);
    gap: 24px;
    align-items: start;
}

/* White in both themes: a phone camera reads dark squares on a light ground, and a code drawn light
   on dark does not scan reliably. */
.pair-qr {
    position: relative;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: white;
}

.pair-qr img {
    display: block;
    width: 320px;
    max-width: 100%;
    height: auto;
    image-rendering: pixelated;
}

.pair-qr.is-off img {
    opacity: .12;
}

.pair-qr-veil {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    text-align: center;
    padding: 24px;
}

.pair-qr-veil p {
    margin: 0;
    font-weight: 600;
    color: rgb(31 27 46);
}

.pair-howto {
    margin: 0;
    padding-left: 20px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    font-size: 14.5px;
}

.pair-code {
    font-family: ui-monospace, "Cascadia Mono", "SF Mono", Consolas, monospace;
    font-size: 26px;
    font-weight: 600;
    letter-spacing: .06em;
    color: var(--head);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.pair-kv dd {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}

.pair-timer {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin: 0;
    font-size: 14px;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}

.pair-timer .lucide {
    width: 16px;
    height: 16px;
}

.pair-timer.low {
    color: var(--warn);
}

.ok-text {
    color: var(--ok);
}

.pair-done {
    max-width: 760px;
}

.pair-done-head {
    display: flex;
    gap: 16px;
    align-items: flex-start;
}

.pair-done-head h2 {
    margin: 0 0 4px;
    font-size: 20px;
}

.pair-done-head .sub {
    margin: 0;
}

.checks {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
    font-size: 14.5px;
}

.checks li {
    display: flex;
    align-items: center;
    gap: 8px;
}

.checks .lucide {
    width: 18px;
    height: 18px;
}

.checks li.ok .lucide {
    color: var(--ok);
}

.checks li.wait {
    color: var(--muted);
}

@media (max-width: 900px) {
    .pair-grid {
        grid-template-columns: minmax(0, 1fr);
    }

    .pair-qr img {
        width: 280px;
    }

    .pair-code {
        font-size: 22px;
    }
}

/* DEV-03. The app files under the pairing form: what each file is, and the ways of getting one
   onto a handset. A file's name, what was written about it and what it is called on disk stand
   together; on a narrow screen each file is a card with its actions on a line of their own. */
.app-files > .hint {
    margin: 0;
}

.tbl-wrap.vlist.app-file-list {
    max-height: 440px;
}

.app-file-t td.c-main small,
.app-file-t td.c-ver small,
.app-file-t td.c-when small {
    display: block;
    font-size: 12.5px;
}

.app-file-t td.c-main .app-desc {
    max-width: 68ch;
    overflow-wrap: anywhere;
}

.app-file-t td.c-main .app-name {
    overflow-wrap: anywhere;
    font-family: ui-monospace, "Cascadia Mono", "SF Mono", Consolas, monospace;
    font-size: 12px;
}

.app-file-t td.c-ver,
.app-file-t td.c-size,
.app-file-t td.c-when {
    white-space: nowrap;
}

.app-file-t .row-acts {
    gap: 8px;
    align-items: center;
}

.app-file-t .row-acts .btn-icon {
    margin-left: -4px;
}

.app-form {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.app-form textarea.input {
    resize: vertical;
    min-height: 76px;
}

.app-chosen {
    min-width: 0;
    overflow-wrap: anywhere;
    font-size: 14px;
    color: var(--head);
}

.app-progress {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.app-bar {
    height: 8px;
    border-radius: 4px;
    background: var(--rule-soft);
    overflow: hidden;
}

.app-bar i {
    display: block;
    height: 100%;
    background: var(--brand);
    transition: width .2s linear;
}

.modal .dialog.app-qr-dialog {
    max-width: 600px;
}

.app-qr {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 20px;
    align-items: center;
}

.app-qr .pair-qr {
    padding: 12px;
    border: 1px solid var(--rule);
    border-radius: 8px;
}

.app-qr .pair-qr img {
    width: 232px;
}

.app-link {
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: 16px;
}

@media (max-width: 900px) {
    .app-file-t td.c-ver,
    .app-file-t td.c-size,
    .app-file-t td.c-when {
        white-space: normal;
    }

    .app-file-t td.c-ver small,
    .app-file-t td.c-when small {
        display: inline;
        margin-left: 4px;
    }

    .vtable.app-file-t td.c-acts {
        flex: 1 1 100%;
        margin-left: 0;
    }

    .app-file-t .row-acts {
        justify-content: flex-start;
        flex-wrap: wrap;
    }
}

/* On a phone the four actions of a file stay on one line: the two that are named lose their
   pictures, which say nothing the words do not. */
@media (max-width: 480px) {
    .app-file-t .row-acts .btn-sm .lucide {
        display: none;
    }
}

@media (max-width: 640px) {
    .app-qr {
        grid-template-columns: minmax(0, 1fr);
        justify-items: center;
    }

    .app-qr .pair-qr img {
        width: 216px;
    }

    .app-link {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* Devices and Sharing: the server addresses devices try, one per line, and the addresses the
   device service found, offered as chips to add with one click. */
.addr-edit {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;
}

.addr-row {
    display: flex;
    align-items: center;
    gap: 6px;
}

.addr-row .input {
    flex: 1 1 auto;
    min-width: 0;
    font-variant-numeric: tabular-nums;
}

.chip-btn {
    display: inline-flex;
    align-items: center;
    margin: 4px 6px 0 0;
    padding: 3px 10px;
    border: 1px solid var(--rule);
    border-radius: 999px;
    background: var(--paper);
    color: var(--ink);
    font: inherit;
    font-size: 13px;
    cursor: pointer;
}

.chip-btn:hover:not(:disabled) {
    border-color: var(--brand);
}

.chip-btn:disabled {
    opacity: .5;
    cursor: default;
}

/* SET-17. Device profiles. The six tabs are drawn as the template draws tabs, on buttons, because
   they change what the page shows without leaving it. They run the width of the page, above the
   tab on show and the notes beside it. */
.tabs button {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 18px;
    border: 0;
    border-radius: 6px;
    background: none;
    font: inherit;
    font-weight: 500;
    font-size: 14.5px;
    color: var(--ink);
    white-space: nowrap;
    cursor: pointer;
}

.tabs button:hover {
    background: var(--brand-t);
    color: var(--brand-ink);
}

.tabs button.on {
    background: var(--brand-solid);
    color: var(--on-brand);
    box-shadow: var(--brand-sh);
}

.tabs button:focus-visible {
    outline: none;
    box-shadow: var(--brand-ring);
}

/* A tab with something on it that has not been saved. */
.tabs .tab-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: currentColor;
    opacity: .85;
}

.profile-tabs {
    padding: 6px;
    border: 1px solid var(--rule);
    border-radius: 8px;
    background: var(--paper);
}

.ph h1 .profile-badge {
    margin-left: 10px;
    vertical-align: middle;
    font-size: 13px;
}

.profile-side .actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

/* The locations that follow a profile. */
.use-list {
    list-style: none;
    margin: 0 0 12px;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.use-list li {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 2px 8px;
    min-width: 0;
}

.use-list .lucide {
    width: 15px;
    height: 15px;
    color: var(--faint);
    flex: none;
    align-self: center;
}

.use-list a,
.use-list span {
    overflow-wrap: anywhere;
}

.use-list small {
    font-size: 12.5px;
}

.vtable.profiles-t .c-when {
    white-space: nowrap;
}

.callout .loss-list {
    margin-top: 8px;
}

/* Beside the page's Save on a tab that saves by itself: what that Save is for. */
.settings-foot .hint {
    margin: 0;
    flex: 1 1 16rem;
}

@media (max-width: 1180px) {
    .tabs.profile-tabs {
        flex-wrap: nowrap;
        overflow-x: auto;
    }
}

/* A dropdown with a search box in it, for a choice among many, such as locations. It looks like a
   select until it is opened. */
.ss {
    position: relative;
}

.ss-field {
    display: flex;
    align-items: center;
    width: 100%;
    padding-right: 36px;
    text-align: left;
    cursor: pointer;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' fill='none' stroke='%236D6982' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' viewBox='0 0 24 24'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    background-position: right 12px center;
    background-repeat: no-repeat;
}

.ss-field:disabled {
    cursor: default;
}

.ss-value {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ss-placeholder {
    color: var(--faint);
}

/* Catches the click that closes the list, wherever outside it that click lands. */
.ss-backdrop {
    position: fixed;
    inset: 0;
    z-index: 40;
}

.ss-pop {
    position: absolute;
    z-index: 41;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 8px;
    border: 1px solid var(--rule);
    border-radius: 8px;
    background: var(--paper);
    box-shadow: var(--shadow-lg);
}

/* In a dialog the list opens in place, since one hanging below the dialog would be cut short. */
.ss.inline .ss-pop {
    position: relative;
    top: 0;
    margin-top: 4px;
    box-shadow: none;
}

.ss-search {
    position: relative;
}

.ss-search .lucide {
    position: absolute;
    top: 50%;
    left: 11px;
    width: 16px;
    height: 16px;
    transform: translateY(-50%);
    color: var(--faint);
    pointer-events: none;
}

.ss-search .input {
    width: 100%;
    padding-left: 36px;
}

.ss-search .input:focus {
    padding-left: 35px;
}

/* Named twice over so that it outranks the template's rule for lists in a dialog. */
.ss .ss-list {
    max-height: 264px;
    margin: 0;
    padding: 0;
    overflow-y: auto;
    list-style: none;
}

.ss-opt {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 8px 10px;
    border-radius: 6px;
    font-size: 14.5px;
    cursor: pointer;
}

.ss-opt span {
    min-width: 0;
    overflow-wrap: anywhere;
}

.ss-opt:hover,
.ss-opt.active {
    background: var(--brand-t);
    color: var(--brand-ink);
}

.ss-opt.on {
    font-weight: 600;
}

.ss-opt .lucide {
    flex: none;
    width: 16px;
    height: 16px;
}

.ss-opt mark {
    background: none;
    color: inherit;
    font-weight: 700;
    text-decoration: underline;
    text-underline-offset: 3px;
}

.ss-note {
    padding: 8px 10px;
    color: var(--muted);
    font-size: 13.5px;
}

/* Lists of attendance and reports. One layout serves them all: the filters, then the figures, then
   the charts, then the rows. Everything here folds down to a phone: the filters stack, the figures
   go two abreast, the charts stack, and each row becomes a card with its cells under their headings. */
.lv-filters {
    padding: 14px 16px;
}

.lv-toolbar {
    gap: 10px;
}

.lv-toolbar .search {
    flex: 1 1 14rem;
    max-width: 20rem;
    min-width: 12rem;
}

.lv-toolbar select.input {
    max-width: 14rem;
}

.lv-dates {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.lv-dates input[type="date"] {
    width: 9.8rem;
    min-width: 0;
}

.lv-day .btn-icon {
    flex: none;
}

/* A searchable dropdown among filters is as wide as a filter, and its list as wide as it needs. */
.toolbar .ss {
    flex: 0 1 15rem;
    min-width: 11rem;
}

.toolbar .ss .ss-field {
    width: 100%;
}

.toolbar .ss .ss-pop {
    min-width: min(18rem, calc(100vw - 48px));
}

.ss-opt.ss-all {
    color: var(--muted);
}

.lv-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 16px;
}

.lv-tile {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    min-width: 0;
    padding: 16px 18px;
    border: 0;
    border-left: 4px solid var(--color-neutral-300);
    border-radius: 10px;
    background: var(--paper);
    box-shadow: var(--shadow-md);
    color: inherit;
    font: inherit;
    text-align: left;
}

.lv-tile .lbl {
    font-size: 13px;
}

.lv-tile .stat {
    font-size: 28px;
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}

.lv-tile .delta {
    font-size: 12.5px;
}

.lv-tile.t-good {
    border-left-color: var(--ok-base);
}

.lv-tile.t-warn {
    border-left-color: var(--warn-base);
}

.lv-tile.t-warn .stat {
    color: var(--warn);
}

.lv-tile.t-bad {
    border-left-color: var(--err-base);
}

.lv-tile.t-info {
    border-left-color: var(--info-base);
}

button.lv-tile {
    cursor: pointer;
    transition: box-shadow .15s, transform .15s;
}

button.lv-tile:hover {
    box-shadow: var(--shadow-lg);
    transform: translateY(-1px);
}

button.lv-tile:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: 2px;
}

button.lv-tile.on {
    border-left-color: var(--brand);
    background: var(--brand-t);
}

.lv-charts {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 16px;
}

.lv-charts.two {
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
}

.lv-chart {
    gap: 12px;
    padding: 16px 18px;
}

.lv-chart .sec-h h2 {
    font-size: 15px;
}

.lv-none {
    margin: 0;
    padding: 18px 0;
    font-size: 13.5px;
}

.lv-bars {
    display: flex;
    align-items: stretch;
    gap: clamp(2px, .5vw, 8px);
    height: 170px;
}

.lv-bar {
    display: flex;
    flex: 1 1 0;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}

.lv-stack {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    justify-content: flex-end;
    min-height: 0;
    border-bottom: 1px solid var(--rule);
}

.lv-stack i {
    display: block;
    width: 100%;
    max-width: 40px;
    margin: 0 auto;
}

.lv-stack i:first-child {
    border-radius: 4px 4px 0 0;
}

.lv-stack i.k1,
.legend i.k1 {
    background: var(--brand);
}

.lv-stack i.k2,
.legend i.k2 {
    background: var(--info-base);
}

.lv-bar:hover .lv-stack i {
    filter: brightness(1.12);
}

.lv-bar small {
    display: flex;
    justify-content: center;
    height: 14px;
    color: var(--faint);
    font-size: 11px;
    line-height: 14px;
    white-space: nowrap;
}

.lv-bar small.skip {
    visibility: hidden;
}

.lv-hbars {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.lv-hbar {
    display: grid;
    grid-template-columns: minmax(0, 9rem) minmax(0, 1fr) auto;
    align-items: center;
    gap: 10px;
    font-size: 13.5px;
}

.lv-hname {
    overflow: hidden;
    color: var(--ink);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lv-htrack {
    height: 10px;
    overflow: hidden;
    border-radius: 999px;
    background: var(--brand-t2);
}

.lv-htrack i {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--brand);
}

.lv-hbar b {
    color: var(--head);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

/* The rows. A cell keeps to one line except where it holds a name of any length or labels. */
.lv-t td {
    white-space: nowrap;
}

.lv-t td.c-main,
.lv-t td.c-badges,
.lv-t td.k-location,
.lv-t td.k-department,
.lv-t td.k-device,
.lv-t td.k-door,
.lv-t td.k-door-in,
.lv-t td.k-door-out,
.lv-t td small {
    white-space: normal;
}

.lv-t td.num,
.lv-t .lv-id {
    font-variant-numeric: tabular-nums;
}

/* An Emirates ID is read as one thing, so it never breaks at a hyphen. */
.lv-t .lv-id {
    white-space: nowrap;
}

.lv-t .lv-badges {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 4px 6px;
}

.lv-t td.c-badges small {
    max-width: 30ch;
}

.lv-t tr.dim td {
    opacity: .7;
}

.lucide.spin {
    animation: lv-spin 1.1s linear infinite;
}

@keyframes lv-spin {
    to { transform: rotate(360deg); }
}

/* Before a table would scroll sideways, the columns that matter least leave it, in three steps. */
@media (min-width: 901px) and (max-width: 1699px) {
    .lv-t .f-wide {
        display: none;
    }
}

@media (min-width: 901px) and (max-width: 1399px) {
    .lv-t .f-medium {
        display: none;
    }
}

@media (min-width: 901px) and (max-width: 1199px) {
    .lv-t .f-narrow {
        display: none;
    }
}

.lv-more {
    display: none;
}

@media (max-width: 1100px) {
    .lv-charts.two {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* On a phone or a narrow tablet a row is a card: what it is about across the top, then each cell
   under its heading, two abreast. A cell with nothing in it is left out. */
@media (max-width: 900px) {
    .vtable.lv-t tbody tr {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
        grid-auto-flow: row dense;
        align-items: start;
        gap: 10px 16px;
        padding: 14px;
    }

    .vtable.lv-t tbody tr[aria-hidden="true"] {
        display: block;
        padding: 0;
    }

    .vtable.lv-t tbody td {
        white-space: normal;
    }

    .vtable.lv-t tbody td:not(.c-main):not(.c-acts) {
        order: 1;
        margin-top: 0;
    }

    .vtable.lv-t tbody td:not(.c-main):not(.c-acts)::before {
        content: attr(data-label);
        display: block;
        margin-bottom: 2px;
        color: var(--faint);
        font-size: 11px;
        font-weight: 600;
        letter-spacing: .04em;
        text-transform: uppercase;
    }

    .vtable.lv-t td.c-main,
    .vtable.lv-t td.c-acts,
    .vtable.lv-t .row-wait td,
    .vtable.lv-t .row-empty td {
        grid-column: 1 / -1;
    }

    .vtable.lv-t td.c-badges,
    .vtable.lv-t td.c-wide,
    .vtable.lv-t td.k-location {
        grid-column: span 2;
    }

    .vtable.lv-t td.c-main {
        order: 0;
        color: var(--head);
        font-weight: 600;
    }

    .vtable.lv-t td.c-acts {
        order: 2;
    }

    .lv-tiles {
        grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
        gap: 12px;
    }

    .vtable.lv-t td.c-none {
        display: none;
    }

    .vtable.lv-t td.c-acts {
        justify-self: start;
        margin: 0;
    }

    .vtable.lv-t td.c-badges small {
        max-width: none;
    }

    .lv-toolbar .search,
    .lv-toolbar select.input,
    .lv-toolbar .ss,
    .lv-dates {
        flex: 1 1 12rem;
        max-width: none;
    }

    .lv-dates input[type="date"] {
        flex: 1 1 0;
        width: auto;
    }
}

@media (max-width: 600px) {
    .lv-tiles {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 12px;
    }

    .lv-tile {
        padding: 12px 14px;
    }

    .lv-tile .stat {
        font-size: 22px;
    }

    /* A figure left alone on the last line takes the whole line. */
    .lv-tiles > :last-child:nth-child(odd) {
        grid-column: 1 / -1;
    }

    .lv-toolbar .search,
    .lv-toolbar select.input,
    .lv-toolbar .ss,
    .lv-dates {
        flex: 1 1 100%;
    }

    /* The two days of a range share the line, however narrow the screen, and stay inside it. */
    .lv-dates {
        min-width: 0;
    }

    .lv-dates input[type="date"] {
        flex: 1 1 0;
    }

    .lv-bars {
        height: 140px;
    }

    .lv-more {
        display: inline-flex;
    }

    .lv-filters:not(.open) .lv-toolbar .lv-extra {
        display: none;
    }

    .lv-hbar {
        grid-template-columns: minmax(0, 6.5rem) minmax(0, 1fr) auto;
    }
}

/* REP-01. The report library: a card for each report. */
.rep-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 16px;
}

.rep-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
    min-width: 0;
    padding: 20px;
    border-radius: 10px;
    background: var(--paper);
    box-shadow: var(--shadow-md);
    color: var(--ink);
    text-decoration: none;
    transition: box-shadow .15s, transform .15s;
}

.rep-card:hover {
    box-shadow: var(--shadow-lg);
    color: var(--ink);
    transform: translateY(-2px);
}

.rep-card:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: 2px;
}

.rep-card b {
    color: var(--head);
    font-size: 16px;
}

.rep-card .muted {
    font-size: 13.5px;
}

.rep-ic {
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    border-radius: 10px;
    background: var(--brand-t);
    color: var(--brand-ink);
}

.rep-ic .lucide {
    width: 20px;
    height: 20px;
}

.rep-open {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: auto;
    padding-top: 6px;
    color: var(--brand-ink);
    font-size: 14px;
    font-weight: 600;
}

.rep-open .lucide {
    width: 16px;
    height: 16px;
}

/* Forms laid out two fields abreast, one on a phone. */
.form-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 16px 20px;
}

.dialog .form-grid {
    margin-bottom: 16px;
}

.sch-when {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.plain-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 0;
    padding-left: 18px;
    color: var(--muted);
    font-size: 14px;
}

.check-line {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 14px 0;
    font-size: 14.5px;
    cursor: pointer;
}

.check-line input {
    width: 18px;
    height: 18px;
    accent-color: var(--brand);
}

.sch-says p {
    font-size: 13.5px;
}

textarea.input {
    width: 100%;
    min-height: 0;
    resize: vertical;
}

@media (max-width: 1100px) {
    .split.mail-split {
        grid-template-columns: minmax(0, 1fr);
    }
}

@media (max-width: 700px) {
    .form-grid,
    .sch-when {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* DASH-01. The dashboard. The template draws the cells, the list of things needing attention, the
   week's chart and the activity; what is here is what the template left to inline styles. */
.stat.warn {
    color: var(--warn);
}

.delta b.down {
    color: var(--warn);
}

.dash-go {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 4px 12px;
}

.dash-go > span {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--brand-ink);
    font-weight: 600;
}

.dash-go .lucide {
    width: 16px;
    height: 16px;
}

.dash-pair {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px;
    margin-top: 10px;
    padding-top: 12px;
    border-top: 1px solid var(--rule-soft);
}

.dash-pair > div {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.dash-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
    padding: 7px 0;
    border-bottom: 1px solid var(--rule-soft);
    color: var(--ink);
    text-decoration: none;
}

.dash-row:last-child {
    border-bottom: 0;
}

.dash-row:hover .stat {
    color: var(--brand-ink);
}

.dash-row:focus-visible,
a.cell:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: 2px;
}

.dash-bar {
    margin: 8px 0 4px;
}

.dash-split {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    color: var(--ink);
    font-size: 13px;
}

.dash-split .warn {
    color: var(--warn);
}

.dash-split b {
    color: var(--head);
    font-variant-numeric: tabular-nums;
}

.sec-h h2 .count {
    margin-left: 4px;
    font-weight: 600;
}

.legend i.acc {
    background: var(--brand);
}

.feed-i time {
    display: flex;
    flex-direction: column;
    font-variant-numeric: tabular-nums;
}

.feed-i time small {
    font-size: 11.5px;
}

.dash-none {
    margin: 0;
    font-size: 14px;
}

/* On a phone a line of the activity puts what the scan was under the name: a label, not a bar. */
.feed-i > .badge {
    justify-self: start;
}

.lv-bars.dash-hours {
    height: 150px;
}

/* The location the dashboard is narrowed to, in its heading. */
.ss.dash-loc {
    width: 17rem;
    max-width: 100%;
}

.ss.dash-loc .ss-field {
    width: 100%;
}

/* What a new installation still has to do. */
.dash-steps {
    display: flex;
    flex-direction: column;
    margin: 0;
    padding: 0;
    list-style: none;
}

.dash-steps li {
    display: grid;
    grid-template-columns: 34px minmax(0, 1fr) auto;
    align-items: center;
    gap: 14px;
    padding: 12px 0;
    border-bottom: 1px solid var(--rule-soft);
}

.dash-steps li:last-child {
    border-bottom: 0;
}

.dash-steps li > div {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.dash-steps li b {
    color: var(--head);
}

.dash-steps li > div > span {
    color: var(--muted);
    font-size: 13.5px;
}

.dash-tick {
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--color-neutral-200);
    color: var(--faint);
}

.dash-tick .lucide {
    width: 18px;
    height: 18px;
}

.dash-steps li.done .dash-tick {
    background: var(--ok-bg);
    color: var(--ok);
}

.dash-steps li.done b {
    color: var(--muted);
    font-weight: 500;
}

@media (max-width: 760px) {
    .ss.dash-loc {
        width: 100%;
    }

    .dash-steps li {
        grid-template-columns: 34px minmax(0, 1fr);
    }

    .dash-steps li > .btn {
        grid-column: 2;
        justify-self: start;
    }
}

/* On a phone the cells stand one under another: two abreast leaves a figure and its words no room. */
@media (max-width: 600px) {
    .cells.dash-cells {
        grid-template-columns: minmax(0, 1fr);
    }
}


/* SET-14. The audit log: what was done is a sentence, so that column wraps and the rest do not. */
.lv-t td.k-who .who-name {
    white-space: nowrap;
}

.lv-t td.k-detail,
.lv-t td.k-about {
    white-space: normal;
}

@media (min-width: 901px) {
    .lv-t td.k-detail {
        min-width: 18rem;
    }
}

/* What an entry is about is most often a person: room for a name of two words on one line. */
@media (min-width: 1280px) {
    .lv-t td.k-about {
        min-width: 8.5rem;
    }
}

@media (max-width: 900px) {
    .vtable.lv-t td.k-detail {
        grid-column: 1 / -1;
    }
}

/* A name in a list of details with the button that explains it. */
.kv .dt-info {
    display: inline-flex;
    align-items: center;
    gap: 2px;
}

.kv .dt-info .info-btn {
    margin: -5px 0;
}

/* How long the log is kept, said over it: a sentence, the button that explains it kept on the line
   of the sentence's last word, and the super admin's button to change it. */
.keep-line {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 16px;
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--rule-soft);
}

.keep-line .keep-text {
    flex: 1 1 16rem;
    min-width: 0;
    font-size: 13.5px;
    color: var(--muted);
}

.keep-line .keep-text b {
    color: var(--ink);
}

.keep-line .keep {
    white-space: nowrap;
}

.keep-line .info-btn {
    vertical-align: middle;
    margin: -4px 0 -4px 2px;
}


/* A field that takes the whole width of a form's grid. */
.form-grid.one {
    grid-template-columns: minmax(0, 1fr);
}

/* What is still to be done before something works, each step ticked once it is. */
.ready-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
    font-size: 14px;
}

.ready-list li {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    color: var(--ink);
}

.ready-list .lucide {
    flex: none;
    width: 16px;
    height: 16px;
    margin-top: 2px;
    color: var(--faint);
}

.ready-list li.ok {
    color: var(--muted);
}

.ready-list li.ok .lucide {
    color: var(--ok);
}

/* The strength of a password, in four steps that fill as its rules are met. A device's battery is
   drawn with a meter of its own, further up, whose rules would otherwise paint these steps full
   whatever the password. */
.pw-check .meter {
    display: grid;
    width: auto;
    height: auto;
    border-radius: 0;
    background: none;
    overflow: visible;
}

.pw-check .meter i {
    height: 6px;
    background: var(--color-neutral-200);
}

.pw-check .meter i.on {
    background: var(--ok);
}

/* What a setting means, behind a button beside its name. A page of settings shows the names and
   the controls and nothing else; the explanation opens in a dialog, where it has room to be whole. */
.info-btn {
    display: inline-grid;
    place-items: center;
    flex: none;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--faint);
    cursor: pointer;
    transition: background-color .12s, color .12s;
}

.info-btn .lucide {
    width: 17px;
    height: 17px;
}

.info-btn:hover,
.info-btn:focus-visible {
    background: var(--brand-t);
    color: var(--brand-ink);
}

.info-btn:focus-visible {
    border-radius: 50%;
}

.info-btn:disabled {
    opacity: .5;
    cursor: default;
}

/* A setting is its name and its control, and nothing under the name. The name's first line stands
   level with a control one line high, whatever the control is. */
.setting {
    align-items: start;
    padding: 12px 0;
}

.setting-t {
    min-width: 0;
}

.setting .setting-t h4 {
    margin: 0;
    padding: 9px 0;
    line-height: 20px;
}

/* The name wraps, and the button stays on the line of its last word. */
.setting-t .keep {
    white-space: nowrap;
}

.setting-t .info-btn {
    vertical-align: middle;
    margin: -4px 0 -4px 4px;
}

.setting .ctl > .tog {
    min-height: 38px;
}

.setting .ctl > .day-picks {
    padding: 4px 0;
}

/* From here down a setting's control stands under its name. */
@media (max-width: 1200px) {
    .setting {
        gap: 4px;
        padding: 14px 0;
    }

    .setting .setting-t h4 {
        padding: 2px 0;
    }

    .setting .ctl > .tog {
        min-height: 32px;
    }

    .setting .ctl > .day-picks {
        padding: 2px 0;
    }
}

/* A field's label with its button, standing where the label alone stood. */
.label-row {
    display: flex;
    align-items: center;
    gap: 2px;
    min-height: 28px;
    margin-bottom: 2px;
}

.label-row > label,
.label-row > .field-label {
    margin: 0;
    color: var(--ink);
    font-size: 13.5px;
    font-weight: 400;
}

.label-row > label em {
    font-style: normal;
    color: var(--err);
    margin-left: 2px;
}

.label-row .info-btn {
    width: 24px;
    height: 24px;
}

.label-row .info-btn .lucide {
    width: 16px;
    height: 16px;
}

/* A section's heading with its button. */
.h-info {
    display: flex;
    align-items: center;
    gap: 4px;
    min-width: 0;
}

/* A box to tick with its button after it. */
.check-row {
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    gap: 6px;
}

.check-row > .check {
    min-width: 0;
}

.check-row > .info-btn {
    margin-top: -3px;
}

/* The dialog an explanation opens in. Its title stays at the top and its button at the foot, and
   what is between them scrolls, however long it is. */
.modal .dialog.help-dialog {
    padding: 0;
    gap: 0;
    overflow: hidden;
    font-weight: 400;
    text-align: left;
    white-space: normal;
}

.modal .help-dialog .dialog-title {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 20px 24px 14px;
    border-bottom: 1px solid var(--rule-soft);
}

.dialog-eyebrow {
    font-family: var(--font-body);
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--brand-ink);
}

.modal .help-dialog .dialog-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 18px 24px 20px;
    color: var(--ink);
    outline: none;
}

.modal .help-dialog .dialog-body:focus-visible {
    box-shadow: inset 0 0 0 2px var(--brand-t3);
}

.modal .help-dialog .dialog-actions {
    justify-content: flex-end;
    padding: 12px 24px 16px;
    border-top: 1px solid var(--rule-soft);
}

/* What an explanation is made of: a paragraph that says what the setting is, the choices and what
   each does, and short headed parts for what changes and what is recommended. */
.help {
    display: flex;
    flex-direction: column;
    gap: 10px;
    font-size: 14.5px;
    line-height: 1.55;
    text-wrap: pretty;
}

.help p,
.help ul,
.help dl {
    margin: 0;
}

/* A dialog spaces its paragraphs and lists for a question and its answer. An explanation is a
   longer read, and sets its own. */
.modal .help p,
.modal .help li,
.modal .help dd {
    margin: 0;
    max-width: none;
    font-size: inherit;
    font-weight: 400;
    color: inherit;
}

.modal .help ul {
    margin: 0;
}

.help > p:first-child {
    font-size: 15px;
    color: var(--head);
}

.help h3 {
    margin: 8px 0 -4px;
    font-family: var(--font-body);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--muted);
}

.help ul {
    padding-left: 18px;
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.help b {
    color: var(--head);
    font-weight: 600;
}

/* Each choice a setting offers, with what it does beside it. */
.help .choices {
    display: grid;
    grid-template-columns: minmax(5.5rem, max-content) minmax(0, 1fr);
    gap: 8px 14px;
    padding: 12px 14px;
    border: 1px solid var(--rule-soft);
    border-radius: 8px;
    background: var(--color-neutral-100);
}

.help .choices dt {
    font-weight: 600;
    color: var(--head);
}

.help .choices dd {
    margin: 0;
}

.help .rec {
    display: flex;
    gap: 8px;
    align-items: flex-start;
    padding: 10px 12px;
    border-radius: 8px;
    background: var(--brand-t);
    color: var(--ink);
}

.help .rec b {
    flex: none;
    color: var(--brand-ink);
}

.help .warn-note {
    padding: 10px 12px;
    border-radius: 8px;
    background: var(--warn-bg);
    color: var(--ink);
}

@media (max-width: 760px) {
    .modal .help-dialog .dialog-title {
        padding: 16px 18px 12px;
    }

    .modal .help-dialog .dialog-body {
        padding: 14px 18px 16px;
    }

    .modal .help-dialog .dialog-actions {
        padding: 10px 18px 14px;
    }

    .help .choices {
        grid-template-columns: minmax(0, 1fr);
        gap: 2px 0;
    }

    .help .choices dd + dt {
        margin-top: 8px;
    }

    /* A finger needs more to press than a pointer does. */
    .info-btn,
    .label-row .info-btn {
        width: 34px;
        height: 34px;
    }

    .setting-t .info-btn {
        margin: -7px 0 -7px 2px;
    }

    .label-row {
        min-height: 34px;
        margin-bottom: 0;
    }

    .check-row > .info-btn {
        margin-top: -6px;
    }
}
