/* ============================================================================
   PADMIN · the component layer
   ============================================================================

   WHY THIS IS A SEPARATE FILE

   padmin.css grew to nine thousand lines by having each design pass appended
   to the end of the last one. There were four token blocks contradicting each
   other and three sections all called the design system. Adding a fifth would
   have repeated the mistake.

   So: tokens live at the top of padmin.css, and every shared component lives
   here, in a layer that comes after everything else. New work goes in this
   file. When a legacy rule needs touching, move it here and delete it there.

   WHAT IT DELIBERATELY DOES NOT DO

   It invents almost no class names. The application already has a vocabulary
   that the screens use consistently: .btn, .card, .field, .page-header,
   .data-table, .status-pill, .pb-hint. Those names are correct. What was
   missing was one definition of each, so a button was styled in six places
   and none of them agreed. Restyling the existing vocabulary means every
   screen improves at once without a single line of HTML being rewritten.

   THE RULES IT FOLLOWS

   · Hierarchy before decoration. Position, weight and space first; borders,
     boxes and colour only when those cannot do the job.
   · One primary action per context. Everything else is quieter than it.
   · Colour carries meaning, never decoration, and never alone.
   · 44px of finger, whatever the control looks like.
   · Nothing moves for longer than 150ms, and nothing moves at all for
     somebody who has asked their device to stop it.
   ============================================================================ */

@layer components {

/* ============================================================================
   1. TYPE — six roles, and no others
   ============================================================================
   The old file had thirty distinct font sizes. Thirty sizes is not a type
   scale, it is the absence of one, and it is most of what makes an interface
   read as developer-built. Each role below is a clear step from the one above
   so that a glance tells you what is important without reading a word. */

.page-title,
h1 {
    margin: 0;
    font-size: var(--fs-xl);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-tight);
    letter-spacing: -0.018em;
    color: var(--text);
}
.section-title, .card-title, h2, h3 {
    font-size: var(--fs-lg);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-tight);
    letter-spacing: -0.012em;
    color: var(--text);
}
h4 { font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--text); }

/* Secondary text must not compete with primary text. One step down in size,
   one step down in colour, never both plus italics plus a box. */
.page-subtitle, .pb-hint, .hint, .field-hint, .help {
    font-size: var(--fs-sm);
    line-height: var(--lh-base);
    color: var(--text-muted);
    max-width: var(--measure);
    margin: 0;
}
/* A label names the thing below it. It is not a heading and should not
   shout: small, medium weight, and the same distance above every control. */
.pb-form label, .field > label, label.field-label {
    display: block;
    margin: 0 0 var(--space-1);
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    line-height: var(--lh-tight);
    color: var(--text-secondary);
    letter-spacing: 0;
    text-transform: none;
}
/* The one micro style: a column heading or an overline. */
.overline, .pb-seclabel, .eyebrow {
    display: block;
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
}
/* Figures are compared with the figure above them, so they line up. */
.num, .data-table td, .data-table th, .stat-value {
    font-variant-numeric: tabular-nums;
}
/* Prose stops being comfortable past about seventy characters. */
.prose, .pb-form > p { max-width: var(--measure); line-height: var(--lh-base); }


/* ============================================================================
   2. PAGE HEADER — where am I, and what is the one thing I can do
   ============================================================================
   Every screen opens the same way, so the answer to "where am I" is always in
   the same place. Title, then one line of context, then the actions, with the
   primary one last on the right where the eye finishes. */

.page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin: 0 0 var(--space-5);
    padding: 0 0 var(--space-4);
    border-bottom: 1px solid var(--border);
}
/* No flex-basis here: on a phone the responsive rules turn this header into
   a column, and a basis of 22rem then sets a 352px HEIGHT rather than a
   width, which left a blank half-screen above the buttons. min-width does
   the same job on a desk without caring which way the axis points. */
.page-header > div:first-child { flex: 1 1 auto; min-width: 0; }
.page-header .page-subtitle { margin-top: var(--space-1); }
.page-header .header-actions,
.page-header > .header-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    flex: 0 0 auto;
}


/* ============================================================================
   3. BUTTONS — one system, four jobs
   ============================================================================
   PRIMARY      the single most likely next action. One per context.
   SECONDARY    .btn on its own: everything ordinary.
   TERTIARY     .btn-outline and .pb-link: available, but quiet.
   DESTRUCTIVE  .btn-danger: separated, and never the default focus.

   They share a height, a radius and a weight, so a row of them lines up and
   the only thing that differs is emphasis, which is the point. */

.btn,
button.btn,
a.btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: var(--h-md);
    padding: 0 var(--space-3);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    background: var(--bg-card);
    color: var(--text);
    font-family: inherit;
    font-size: var(--fs-md);
    font-weight: var(--fw-medium);
    line-height: 1;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease),
                border-color var(--dur-fast) var(--ease),
                color var(--dur-fast) var(--ease);
}
/* However small it looks, it is at least a finger across. */
.btn::after {
    content: '';
    position: absolute;
    inset: 50% 0 auto;
    height: var(--tap);
    transform: translateY(-50%);
    min-width: var(--tap);
    left: 50%;
    width: 100%;
    margin-left: -50%;
}
.btn:hover { background: var(--bg-alt); }
.btn:active { background: var(--border); }

.btn-primary, button.btn-primary, a.btn-primary {
    background: var(--brand);
    border-color: var(--brand);
    color: var(--text-on-brand);
    font-weight: var(--fw-semibold);
}
.btn-primary:hover { background: var(--brand-dark); border-color: var(--brand-dark); }
.btn-primary:active { background: var(--brand-dark); }

.btn-outline, .btn-ghost {
    background: none;
    border-color: transparent;
    color: var(--text-secondary);
}
.btn-outline:hover, .btn-ghost:hover { background: var(--bg-alt); color: var(--text); }

.btn-danger, .btn-destructive {
    background: var(--danger);
    border-color: var(--danger);
    color: var(--text-on-brand);
    font-weight: var(--fw-semibold);
}
.btn-danger:hover, .btn-destructive:hover { filter: brightness(0.93); }

.btn-sm { min-height: var(--h-sm); padding: 0 var(--space-2); font-size: var(--fs-sm); }
.btn-lg { min-height: var(--h-lg); padding: 0 var(--space-4); font-size: var(--fs-md); }
.btn-block { width: 100%; }

.btn[disabled], .btn:disabled, .btn[aria-disabled="true"] {
    opacity: 0.45;
    cursor: not-allowed;
    pointer-events: none;
}

/* A text button. Underlined on hover rather than coloured all the time, so a
   screen of them does not read as a screen of warnings. */
.pb-link {
    display: inline;
    padding: 0;
    border: 0;
    background: none;
    color: var(--brand);
    font: inherit;
    font-weight: var(--fw-medium);
    text-decoration: none;
    cursor: pointer;
}
.pb-link:hover { text-decoration: underline; }

/* The row of actions that closes a form or a dialog. The primary one is last
   because that is where the eye and the thumb finish. */
.pb-actions, .modal-foot {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
    flex-wrap: wrap;
    margin-top: var(--space-5);
}
/* Anything destructive goes to the far left, away from the confirm. */
.pb-actions .btn-danger,
.pb-actions .btn-destructive,
.modal-foot .btn-danger { margin-right: auto; }


/* ============================================================================
   4. SEGMENTED CONTROL — a small, closed set of choices
   ============================================================================
   For two to four options where seeing them all matters. Anything longer is
   a select, and anything that is not a choice is not this. */

.seg {
    display: inline-flex;
    padding: 2px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg-alt);
}
.seg-btn {
    min-height: calc(var(--h-md) - 6px);
    padding: 0 var(--space-3);
    border: 0;
    border-radius: calc(var(--radius) - 2px);
    background: none;
    color: var(--text-secondary);
    font: inherit;
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.seg-btn:hover { color: var(--text); }
.seg-btn.is-on, .seg-btn[aria-pressed="true"] {
    background: var(--bg-card);
    color: var(--text);
    font-weight: var(--fw-semibold);
    box-shadow: var(--shadow-sm);
}


/* ============================================================================
   5. FORM CONTROLS — one height, one radius, one focus ring
   ============================================================================ */

.pb-form input:not([type="checkbox"]):not([type="radio"]),
.pb-form select,
.pb-form textarea,
input.field, select.field, textarea.field {
    width: 100%;
    min-height: var(--h-lg);
    padding: 0 var(--space-3);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    background: var(--bg-card);
    color: var(--text);
    font-family: inherit;
    font-size: var(--fs-md);
    line-height: var(--lh-base);
    transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.pb-form textarea, textarea.field { padding: var(--space-2) var(--space-3); line-height: var(--lh-base); }
.pb-form input::placeholder, .pb-form textarea::placeholder { color: var(--text-muted); }
.pb-form input:hover, .pb-form select:hover, .pb-form textarea:hover { border-color: var(--text-muted); }

/* A field that is wrong says so next to itself, in words, and is not only
   red, because a colour alone is no use to a good many people. */
.field-error input, .field-error select, .field-error textarea,
input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"] {
    border-color: var(--danger);
}
.field-message {
    display: flex;
    gap: var(--space-1);
    margin-top: var(--space-1);
    font-size: var(--fs-sm);
    color: var(--danger);
}

/* Two fields that belong together sit on one row and drop to one column when
   there is no room, which is the only responsive rule most forms need. */
.pb-row, .field-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: var(--space-3);
}
.pb-form .field + .field, .pb-form > label + label { margin-top: var(--space-3); }


/* ============================================================================
   6. FOCUS — one ring, always visible, never only on click
   ============================================================================ */

:where(a, button, input, select, textarea, [tabindex], summary):focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
    border-radius: var(--radius);
    position: relative;
    z-index: 2;
}
/* A mouse user does not need it; a keyboard user must have it. */
:where(a, button, input, select, textarea):focus:not(:focus-visible) { outline: none; }


/* ============================================================================
   7. CARDS AND SECTIONS — grouping, not decoration
   ============================================================================
   A card says "these things belong together". A single field in a card says
   nothing and costs two rectangles. Shadow is not used here at all: a card
   sits on the page, it does not float above it. */

.card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: none;
}
.card + .card { margin-top: var(--space-4); }
.card-title {
    margin: 0 0 var(--space-1);
}
/* A heading with a rule under it separates without drawing a box. */
.section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    margin: var(--space-6) 0 var(--space-3);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--border);
}
.section-head:first-child { margin-top: 0; }


/* ============================================================================
   8. TABLES — dense, scannable, and honest about what matters
   ============================================================================
   The first column identifies the record and is the only bold thing in the
   row. Hairlines separate; there is no zebra striping, because two separators
   doing one job is what makes a table look busy. */

.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

.data-table { width: 100%; border-collapse: collapse; font-size: var(--fs-base); }
.data-table th {
    position: sticky;
    top: 0;
    z-index: 1;
    padding: var(--space-2) var(--space-3);
    background: var(--bg-card);
    border-bottom: 1px solid var(--border-strong);
    color: var(--text-muted);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    letter-spacing: 0.05em;
    text-transform: uppercase;
    text-align: left;
    white-space: nowrap;
}
.data-table td {
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--border);
    color: var(--text-secondary);
    vertical-align: middle;
}
/* What the row IS, rather than what it happens to hold. */
.data-table tbody td:first-child { color: var(--text); font-weight: var(--fw-medium); }
.data-table tbody tr:hover td { background: var(--bg-subtle); }
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table td.center, .data-table th.center { text-align: center; }
.data-table td.num, .data-table th.num { text-align: right; }


/* ============================================================================
   9. STATUS — a word first, a colour second
   ============================================================================ */

.status-pill, .rq-tag, .tag {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 2px var(--space-2);
    border-radius: var(--radius-pill);
    background: var(--bg-alt);
    color: var(--text-secondary);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    line-height: 1.5;
    white-space: nowrap;
}
.status-pill.green, .rq-tag.rq-ok, .tag-success { background: var(--success-light); color: var(--success); }
.status-pill.amber, .rq-tag.rq-warn, .tag-warning { background: var(--warning-light); color: var(--warning); }
.status-pill.red, .rq-tag.rq-bad, .tag-danger { background: var(--danger-light); color: var(--danger); }
.status-pill.blue, .tag-info { background: var(--info-light); color: var(--info); }


/* ============================================================================
   10. EMPTY, LOADING AND ERROR — never an unexplained blank
   ============================================================================
   An empty screen should say what would be here, why it is not, and what to
   do about it. A blank panel reads as a fault every time. */

.empty-state, .fd-empty {
    padding: var(--space-6) var(--space-4);
    text-align: center;
    color: var(--text-muted);
    font-size: var(--fs-md);
}
.empty-state-title {
    margin-bottom: var(--space-1);
    color: var(--text);
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
}
.empty-state .btn { margin-top: var(--space-4); }

/* A skeleton says "this is coming" in the shape of the thing that is coming,
   which a spinner cannot do. */
.skeleton {
    background: linear-gradient(90deg, var(--bg-alt) 25%, var(--bg-subtle) 37%, var(--bg-alt) 63%);
    background-size: 400% 100%;
    border-radius: var(--radius);
    animation: skeleton 1.2s ease-in-out infinite;
}
.skeleton-line { height: 0.75rem; margin-bottom: var(--space-2); }
.skeleton-line:nth-child(3n) { width: 70%; }
@keyframes skeleton { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
@media (prefers-reduced-motion: reduce) { .skeleton { animation: none; } }


/* ============================================================================
   11. MOTION — quick, or not at all
   ============================================================================ */

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}


/* ============================================================================
   12. LAYOUT UTILITIES — so a screen does not need inline styles
   ============================================================================
   index.html carried 657 inline style attributes, a hundred and thirty of
   which were the same two declarations. Inline styles cannot be overridden by
   any stylesheet, cannot be made dark, and cannot be changed in one place.
   These cover what those were doing. */

.stack        { display: flex; flex-direction: column; gap: var(--space-3); }
.stack-sm     { display: flex; flex-direction: column; gap: var(--space-2); }
.stack-lg     { display: flex; flex-direction: column; gap: var(--space-5); }
.row          { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.row-between  { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
.row-end      { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-2); flex-wrap: wrap; }
.grow         { flex: 1 1 auto; min-width: 0; }
.muted        { color: var(--text-muted); }
.secondary    { color: var(--text-secondary); }
.mt-0 { margin-top: 0; }      .mb-0 { margin-bottom: 0; }
.mt-2 { margin-top: var(--space-2); }  .mb-2 { margin-bottom: var(--space-2); }
.mt-4 { margin-top: var(--space-4); }  .mb-4 { margin-bottom: var(--space-4); }
.mt-5 { margin-top: var(--space-5); }  .mb-5 { margin-bottom: var(--space-5); }
.measure      { max-width: var(--measure); }


/* ============================================================================
   13. TOUCH — the same interface, sized for a thumb
   ============================================================================ */

@media (max-width: 640px) {
    .page-header { padding-bottom: var(--space-3); margin-bottom: var(--space-4); }
    /* The top bar already says where you are, so on a phone the page title is
       hidden. A header with no actions is then a rule and some padding round
       whatever context line is left, and where that line is hidden too it is
       an empty bordered box taking sixty pixels above the content. Requests
       was doing exactly that. It keeps its context line where it has one and
       collapses entirely where it does not. */
    .page-header:not(:has(.header-actions)) {
        margin-bottom: 0;
        padding-bottom: 0;
        border-bottom: 0;
    }
    .page-header:not(:has(.header-actions)):has(.page-subtitle) {
        margin-bottom: var(--space-3);
    }
    .page-header .header-actions { width: 100%; }
    /* On a phone the primary action is worth the full width; the rest share
       a row beneath it. A row of five equal buttons is nobody's idea of a
       clear next step. */
    .page-header .header-actions .btn-primary { flex: 1 1 100%; order: -1; }
    /* A menu is not an action, so it does not take the full width and read as
       one. Exits has only an Export menu, and stretched it looked like the
       page's purpose. */
    .page-header .header-actions { justify-content: flex-start; }
    .page-header .header-actions > .menu-btn { flex: 0 0 auto; }
    .btn { min-height: var(--tap); }
    .seg { width: 100%; }
    .seg-btn { flex: 1 1 0; min-height: calc(var(--tap) - 6px); }
    .pb-actions, .modal-foot { gap: var(--space-2); }
    .pb-actions .btn, .modal-foot .btn { flex: 1 1 auto; }
    .pb-actions .btn-primary, .modal-foot .btn-primary { flex: 1 1 100%; order: 1; }
    .pb-actions .btn-danger, .modal-foot .btn-danger { margin-right: 0; }
}

/* ============================================================================
   14. PAGER — where am I in the list, and how do I move
   ============================================================================ */
.pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    flex-wrap: wrap;
    margin-top: var(--space-3);
    padding-top: var(--space-3);
    border-top: 1px solid var(--border);
    font-size: var(--fs-sm);
    color: var(--text-muted);
}
.pager-size { display: flex; align-items: center; gap: var(--space-2); margin: 0; }
.pager-size > span { font-size: var(--fs-sm); color: var(--text-muted); }
.pager-size select {
    min-height: var(--h-sm);
    padding: 0 var(--space-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    background: var(--bg-card);
    color: var(--text);
    font: inherit;
    font-size: var(--fs-sm);
}
.pager-count { margin: 0; }
.pager-moves { display: flex; align-items: center; gap: var(--space-2); }
.pager-where { color: var(--text-secondary); white-space: nowrap; }

/* A bare button inside the app still gets the secondary treatment, so nothing
   ever falls back to the browser's own grey box. */
.main button:not([class]), .modal button:not([class]) {
    min-height: var(--h-md);
    padding: 0 var(--space-3);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    background: var(--bg-card);
    color: var(--text);
    font: inherit;
    font-size: var(--fs-md);
    cursor: pointer;
}

@media (max-width: 640px) {
    .pager { justify-content: center; text-align: center; }
    .pager-moves { width: 100%; justify-content: space-between; }
    .pager-moves .btn { min-height: var(--tap); }
}

/* ============================================================================
   15. FRONT DESK — the attention strip
   ============================================================================
   Tiles are for work, not for statistics. A count of nought is not a tile; it
   is one clause in a sentence that says there is nothing to worry about. */
.fd-attention { margin-bottom: var(--space-5); }
.fd-attention .section-head { margin-top: 0; }

.fd-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(11.5rem, 1fr));
    gap: var(--space-3);
}
.fd-tile {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-height: 0;
    padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);
    border: 1px solid var(--border);
    border-left: 3px solid var(--border-strong);
    border-radius: var(--radius-lg);
    background: var(--bg-card);
    text-align: left;
    cursor: pointer;
    transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.fd-tile:hover { background: var(--bg-subtle); }
.fd-tile.is-bad  { border-left-color: var(--danger); }
.fd-tile.is-warn { border-left-color: var(--warning); }
.fd-tile.is-ok   { border-left-color: var(--success); }
.fd-tile-n {
    font-size: var(--fs-2xl);
    font-weight: var(--fw-semibold);
    line-height: 1.05;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}
.fd-tile-l { font-size: var(--fs-sm); color: var(--text-secondary); line-height: var(--lh-base); }
/* The most urgent thing on the screen is allowed to say so. */
.fd-tile.is-bad .fd-tile-n { color: var(--danger); }

/* Everything that is at nought, in one line. */
.fd-clear {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    margin: var(--space-3) 0 0;
    font-size: var(--fs-sm);
    color: var(--text-muted);
}
.fd-clear-tick { color: var(--success); font-weight: var(--fw-bold); }

@media (max-width: 640px) {
    .fd-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2); }
    .fd-tile { padding: var(--space-3); }
    .fd-tile-n { font-size: 26px; }
}

/* ============================================================================
   16. TOAST — what just happened
   ============================================================================
   Bottom right on a desk, where it is out of the way of the work. Bottom
   centre on a phone, above the thumb rather than under it. It carries a drawn
   mark rather than the characters [OK], announces itself to a screen reader,
   and can be dismissed rather than waited out. */
.toast {
    position: fixed;
    right: var(--space-5);
    bottom: var(--space-5);
    left: auto;
    z-index: 10000;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    max-width: min(26rem, calc(100vw - 2rem));
    padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);
    border: 1px solid var(--border);
    border-left: 3px solid var(--text-muted);
    border-radius: var(--radius-lg);
    background: var(--bg-card);
    color: var(--text);
    font-size: var(--fs-md);
    font-weight: var(--fw-medium);
    box-shadow: var(--shadow-lg);
    transform: translateY(0.5rem);
    opacity: 0;
    pointer-events: none;
    transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.toast.show { transform: translateY(0); opacity: 1; pointer-events: auto; }
.toast.success { border-left-color: var(--success); }
.toast.error   { border-left-color: var(--danger); }
.toast-mark { display: flex; flex: none; }
.toast-mark svg { width: 20px; height: 20px; }
.toast.success .toast-mark { color: var(--success); }
.toast.error .toast-mark { color: var(--danger); }
.toast-text { flex: 1 1 auto; line-height: var(--lh-base); }
.toast-close {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--h-md);
    height: var(--h-md);
    margin: calc(var(--space-2) * -1) calc(var(--space-2) * -1) calc(var(--space-2) * -1) 0;
    border: 0;
    border-radius: var(--radius);
    background: none;
    color: var(--text-muted);
    cursor: pointer;
}
.toast-close svg { width: 16px; height: 16px; }
.toast-close:hover { background: var(--bg-alt); color: var(--text); }

@media (max-width: 640px) {
    /* Above the thumb, not under it, and clear of anything fixed to the
       bottom of the screen. */
    .toast {
        right: var(--space-3);
        left: var(--space-3);
        bottom: calc(var(--space-4) + env(safe-area-inset-bottom, 0px));
        max-width: none;
    }
    .toast-close { width: var(--tap); height: var(--tap); }
}

/* ============================================================================
   17. TABS — one row of views of the same thing
   ============================================================================
   A tab is not a button and should not be drawn as one. The selected tab is
   marked by a rule underneath it and a weight change, which is what every
   platform does and what stops a row of tabs reading as a row of buttons
   competing with the real actions above them. */
.tab-bar, .tabs {
    display: flex;
    gap: var(--space-1);
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
.tab-bar::-webkit-scrollbar, .tabs::-webkit-scrollbar { display: none; }
.tab {
    position: relative;
    flex: 0 0 auto;
    min-height: var(--h-lg);
    padding: 0 var(--space-3);
    border: 0;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    background: none;
    color: var(--text-secondary);
    font: inherit;
    font-size: var(--fs-md);
    font-weight: var(--fw-medium);
    white-space: nowrap;
    cursor: pointer;
    transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.tab:hover { color: var(--text); }
.tab.active, .tab[aria-selected="true"] {
    border-bottom-color: var(--brand);
    color: var(--text);
    font-weight: var(--fw-semibold);
    background: none;
}

/* A telephone number on the front desk is a tap-to-call, and it is one of the
   things reception reaches for most. It gets a finger, not a text link. */
.fd-tel {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap);
    color: var(--text);
    text-decoration: none;
    border-bottom: 1px solid var(--border-strong);
}
.fd-tel:hover { border-bottom-color: var(--brand); color: var(--brand); }

@media (max-width: 640px) {
    .tab { min-height: var(--tap); padding: 0 var(--space-4); font-size: var(--fs-md); }
    /* Tabs run off the edge rather than wrapping into an uneven block, with
       the selected one always brought into view by the browser. */
    .tab-bar, .tabs { margin: 0; }
}

/* ============================================================================
   18. THE FLOATING ACTION ON REQUESTS
   ============================================================================
   Taking a request is what reception does at the desk with somebody in front
   of them or a telephone in their hand, so on a phone it stays within thumb
   reach rather than scrolling away up the page.

   Two things went wrong when the component layer arrived. This layer comes
   after `system`, so the plain `.btn` rules were overruling the ones that
   made this a pill: it had quietly become a 44px rounded rectangle. And the
   toast, newly moved to the bottom of the screen, was landing straight on
   top of it.

   Both are fixed here rather than back in padmin.css, because this is where
   the rules that won are, and a fix in the losing file would look right and
   do nothing. */
@media (max-width: 640px) {
    .rq-tools > .btn.btn-primary {
        position: fixed;
        right: var(--space-4);
        bottom: calc(var(--bottom-inset) + var(--space-3));
        z-index: 40;
        width: auto;
        min-height: 54px;
        padding: 0 var(--space-5);
        border-radius: var(--radius-pill);
        box-shadow: var(--shadow-lg);
        font-size: var(--fs-lg);
        flex: 0 0 auto;
    }
    /* The banner shown while a master is looking as somebody else sits along
       the bottom, so the button steps up over it. */
    body.ac-previewing .rq-tools > .btn.btn-primary { bottom: 90px; }

    /* A message about what just happened must not cover the button that
       causes the next thing to happen. */
    body:has(#page-requests.active) .toast {
        bottom: calc(var(--bottom-inset) + 54px + var(--space-4));
    }
    /* Enough room under the last row that the button never hides one. */
    #page-requests { padding-bottom: calc(54px + var(--space-6)); }
}

/* ============================================================================
   19. BOTTOM NAVIGATION — the phone's top level
   ============================================================================
   Four destinations and a way to the other twenty-five. Only on a phone, only
   when the profile has at least two places worth going, and never instead of
   the drawer.

   --bottom-inset is what everything fixed to the bottom of the screen measures
   from: the tab bar if there is one, the safe area if not. The floating action
   on Requests and the toast both use it, so there is one number to change
   rather than three that drift apart. */
:root { --tabbar-h: 56px; --bottom-inset: env(safe-area-inset-bottom, 0px); }

.m-tabbar { display: none; }

@media (max-width: 640px) {
    body.has-tabbar { --bottom-inset: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px)); }

    .m-tabbar {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 45;
        display: flex;
        height: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));
        padding-bottom: env(safe-area-inset-bottom, 0px);
        border-top: 1px solid var(--border);
        /* Opaque rather than translucent: a list scrolling underneath a
           frosted bar is decoration that costs legibility. */
        background: var(--bg-card);
    }
    .m-tabbar[hidden] { display: none; }

    .m-tab {
        flex: 1 1 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 3px;
        min-width: 0;
        min-height: var(--tabbar-h);
        padding: 0 2px;
        border: 0;
        background: none;
        color: var(--text-muted);
        font: inherit;
        font-size: 10px;
        font-weight: var(--fw-medium);
        letter-spacing: 0.01em;
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
    }
    .m-tab svg { width: 23px; height: 23px; }
    .m-tab > span {
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    /* Where you are is said by the colour AND the weight, so it does not rely
       on somebody being able to tell the red from the grey. */
    .m-tab.is-on { color: var(--brand); font-weight: var(--fw-semibold); }
    .m-tab.is-on svg { stroke-width: 2.1; }
    .m-tab:active { background: var(--bg-alt); }

    /* Room under the content for the bar, or the last row of every list sits
       under it for ever. */
    body.has-tabbar .main { padding-bottom: calc(var(--tabbar-h) + var(--space-4) + env(safe-area-inset-bottom, 0px)); }

    /* With the drawer open the bar is behind it and must not be reachable. */
    body.drawer-open .m-tabbar { display: none; }

    /* Everything else fixed to the bottom now measures from the bar. */
    .toast { bottom: calc(var(--bottom-inset) + var(--space-3)); }
}

/* On a desk the sidebar is the navigation and the bar has no business
   existing, whatever the body class says. */
@media (min-width: 641px) {
    .m-tabbar { display: none !important; }
    body.has-tabbar .main { padding-bottom: 0; }
}

/* ============================================================================
   20. REQUESTS — the toolbar
   ============================================================================
   Two rows, two questions. The chips say which queue; the tools narrow it.
   Nothing in either row is styled like the row above, so they do not read as
   one undifferentiated field of buttons. */
.rq-toolbar { margin: 0 0 var(--space-4); }

/* ---- which queue: these behave as tabs, so they are drawn as tabs -------- */
.rq-views {
    display: flex;
    gap: var(--space-1);
    margin-bottom: var(--space-3);
    padding-bottom: 0;
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
.rq-views::-webkit-scrollbar { display: none; }
.rq-chip {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--h-lg);
    padding: 0 var(--space-3);
    border: 0;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    background: none;
    color: var(--text-secondary);
    font: inherit;
    font-size: var(--fs-md);
    font-weight: var(--fw-medium);
    white-space: nowrap;
    cursor: pointer;
    transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.rq-chip:hover { color: var(--text); background: none; }
.rq-chip.on {
    border-bottom-color: var(--brand);
    color: var(--text);
    font-weight: var(--fw-semibold);
    background: none;
}
/* The count is the reason to look, so it is legible on its own rather than
   trailing after a middle dot. */
.rq-chip-n {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 18px;
    padding: 0 6px;
    border-radius: var(--radius-pill);
    background: var(--bg-alt);
    color: var(--text-secondary);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    font-variant-numeric: tabular-nums;
}
.rq-chip.on .rq-chip-n { background: var(--brand); color: var(--text-on-brand); }

/* ---- narrow it ----------------------------------------------------------- */
.rq-tools {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}
.rq-search {
    flex: 1 1 15rem;
    min-width: 0;
    max-width: 26rem;
    min-height: var(--h-md);
    padding: 0 var(--space-3);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    background: var(--bg-card);
    color: var(--text);
    font: inherit;
    font-size: var(--fs-md);
}
.rq-search::placeholder { color: var(--text-muted); }
.rq-type {
    flex: 0 0 auto;
    min-height: var(--h-md);
    padding: 0 var(--space-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    background: var(--bg-card);
    color: var(--text);
    font: inherit;
    font-size: var(--fs-md);
}
/* The primary sits hard right, away from the filters, so it never reads as
   one of them. */
.rq-tools > .btn.btn-primary { margin-left: auto; }

@media (max-width: 640px) {
    /* No edge-to-edge bleed here. The page padding is not the same on every
       screen, so a fixed negative margin left the first chip four pixels out
       of line with everything under it, which is exactly the kind of small
       wrongness that makes an interface feel homemade. The scroller lives
       inside the content box and lines up by construction. */
    .rq-views { margin: 0 0 var(--space-3); }
    .rq-chip { min-height: var(--tap); }
    .rq-tools { gap: var(--space-2); }
    .rq-search { flex: 1 1 100%; max-width: none; min-height: var(--tap); }
    .rq-tools .seg.rq-scope { flex: 1 1 100%; }
    .rq-type { flex: 1 1 auto; min-height: var(--tap); }
    .rq-tools .menu-btn { flex: 0 0 auto; min-height: var(--tap); }
}


/* ============================================================================
   21. MENU — where the rare actions live
   ============================================================================ */
.menu-btn { gap: var(--space-1); }
.menu-btn-chev { width: 16px; height: 16px; opacity: 0.7; }
.menu-btn[aria-expanded="true"] { background: var(--bg-alt); }

.menu-pop {
    position: fixed;
    z-index: 10001;
    min-width: 13rem;
    max-width: min(20rem, calc(100vw - 1rem));
    padding: var(--space-1);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--bg-card);
    box-shadow: var(--shadow-lg);
    animation: menu-in var(--dur) var(--ease);
}
@keyframes menu-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .menu-pop { animation: none; } }

.menu-item {
    display: block;
    width: 100%;
    min-height: var(--h-lg);
    padding: var(--space-2) var(--space-3);
    border: 0;
    border-radius: var(--radius);
    background: none;
    color: var(--text);
    font: inherit;
    font-size: var(--fs-md);
    text-align: left;
    cursor: pointer;
}
.menu-item:hover, .menu-item:focus-visible { background: var(--bg-alt); }
.menu-item.is-danger { color: var(--danger); }
.menu-item.is-danger:hover { background: var(--danger-light); }
.menu-hint {
    display: block;
    margin-top: 1px;
    color: var(--text-muted);
    font-size: var(--fs-sm);
    font-weight: var(--fw-regular);
}
.menu-sep { height: 1px; margin: var(--space-1) 0; background: var(--border); }

@media (max-width: 640px) {
    .menu-item { min-height: var(--tap); }
}

/* ============================================================================
   22. SELECTS — the control the design system was missing
   ============================================================================
   Several screens fell back on the browser's own select, which is a different
   height and a different corner from every control beside it. One rule, so a
   select on a row with a button and an input lines up with both.

   Not restyled from scratch: the native control opens the platform's own
   picker, which on a phone is a far better thing than anything that could be
   built here, so only the closed state is touched. */
.main select:not([size]):not([multiple]),
.modal select:not([size]):not([multiple]) {
    min-height: var(--h-md);
    padding: 0 var(--space-5) 0 var(--space-3);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    background-color: var(--bg-card);
    color: var(--text);
    font-family: inherit;
    font-size: var(--fs-md);
    cursor: pointer;
    /* One chevron, drawn rather than relying on whatever the platform paints,
       so the control is the same shape in every browser. */
    appearance: none;
    -webkit-appearance: none;
    background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                      linear-gradient(135deg, currentColor 50%, transparent 50%);
    background-position: calc(100% - 17px) 55%, calc(100% - 12px) 55%;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
}
.main select:hover, .modal select:hover { border-color: var(--text-muted); }
.main select:disabled { opacity: 0.5; cursor: not-allowed; }

/* A control with a word in front of it. The word is the label, so it is not
   repeated inside the control as a placeholder. */
.exits-window { margin: 0 0 var(--space-4); gap: var(--space-3); }
.exits-window-pick { gap: var(--space-2); }
.exits-window-pick > span { flex: none; font-size: var(--fs-sm); }
.exits-window-pick select { flex: 1 1 auto; min-width: 11rem; }

@media (max-width: 640px) {
    .main select:not([size]):not([multiple]),
    .modal select:not([size]):not([multiple]) { min-height: var(--tap); font-size: var(--fs-md); }
    .exits-window { flex-direction: column; align-items: stretch; gap: var(--space-2); }
    .exits-window-pick { width: 100%; }
}

/* ============================================================================
   23. CHECKBOXES AND RADIOS
   ============================================================================
   The design system had no definition for either, so screens fell back on the
   browser's, which draws a 13px box and gives a finger a 24px target. The
   visible control is now 20px, which is legible without being a toy, and the
   hit area is a full 44px on touch whatever the box looks like.

   The native control is kept and only recoloured. A checkbox rebuilt out of
   divs loses the platform's own behaviour, its focus ring and, on a phone,
   its haptics, and gains nothing a user can see. */
input[type="checkbox"], input[type="radio"] {
    position: relative;
    width: 20px;
    height: 20px;
    margin: 0;
    accent-color: var(--brand);
    cursor: pointer;
    flex: none;
}
input[type="radio"] { border-radius: 50%; }

/* The target, not the picture. Drawn on a pseudo-element so the box keeps its
   size while the thing a thumb has to hit is comfortably bigger. */
@media (pointer: coarse), (max-width: 640px) {
    input[type="checkbox"]::after, input[type="radio"]::after {
        content: '';
        position: absolute;
        left: 50%;
        top: 50%;
        width: var(--tap);
        height: var(--tap);
        transform: translate(-50%, -50%);
    }
    /* A checkbox inside a label already has the whole label as its target, so
       the extra area would only overlap the next row. */
    label > input[type="checkbox"]::after,
    label > input[type="radio"]::after { content: none; }
    label:has(> input[type="checkbox"]),
    label:has(> input[type="radio"]) { min-height: var(--tap); display: flex; align-items: center; gap: var(--space-2); }
}

}
