/* =========================================================================
   StaffMIS UI — "Duty board"
   =========================================================================

   DESIGN BRIEF
   Operational compliance software for UK leisure centres. It is read by duty
   managers, lifeguards, instructors and cleaners, on tablets and phones, at
   poolside — bright reflective light, wet hands, in a hurry, against statutory
   obligations (pool tests, fire checks, legionella, training currency).

   So this is not a marketing surface. Status IS the product. The reference is
   the physical laminated duty board and poolside statutory signage: colour-coded,
   unambiguous, readable across a room, no decoration competing for attention.

   SIGNATURE — the status spine
   Every row or card that carries a compliance state gets a 5px colour bar on its
   leading edge, paired with an uppercase chip. One device, used everywhere,
   encoding real information. It is what makes a screen scannable at arm's length,
   the same way the board on the wall is.

   =========================================================================
   SAFETY — READ THIS BEFORE APPLYING
   =========================================================================
   Everything is scoped under the root class `.sm`. Nothing here applies unless
   an element is inside `<div class="sm">`, so this file can be loaded on a page
   that has NOT been migrated without changing a single pixel of it.

   That is deliberate: it makes the rollout screen-by-screen (or tenant-by-tenant)
   and instantly reversible — remove the class, the old look returns. There are no
   bare element selectors, no `*`, and no `!important` outside the two documented
   utilities at the end.

   It restyles the Bootstrap 3.2 classes the app ALREADY emits
   (.form-group / .control-label / .form-control / .panel / .btn / .table /
   .sidebar-menu__*) so screens can be reskinned without rewriting the PHP that
   generates them.
   ========================================================================= */

/* Archivo: a utilitarian grotesque with signage lineage — legible at small sizes
   on cheap tablets, more character than Helvetica, no SaaS-geometric softness.
   Narrow carries labels and dense table headers.
   TO SELF-HOST: drop Archivo + Archivo Narrow woff2 into public/fonts/ and add the
   @font-face blocks here.
   The Google Fonts @import that shipped with the mock is deliberately NOT here. It is a
   render-blocking request to a third-party host, and these screens are used poolside on
   locked-down leisure-centre networks where that request stalls rather than fails fast --
   which would hold up first paint on every page in the app. The stack below degrades
   cleanly to the system UI font until the faces are self-hosted. */

.sm {
    /* --- Colour ------------------------------------------------------------
       Institutional teal rather than the current bright cyan: cyan on white
       washes out badly under poolside light and fails contrast at small sizes.
       Status colours stay green/amber/red because staff already read them that
       way on the board and on signage — changing that vocabulary would be a
       safety regression, not a redesign. */
    --ink:        #0F2027;   /* deep slate, near-black with a green cast */
    --ink-soft:   #4A5F66;   /* secondary text */
    --ink-faint:  #7B8E94;   /* captions, meta */
    --pool:       #0B6E75;   /* brand / primary action */

    /* The navigation rail. Its own token, not --ink: the rail is permanent chrome
       and stays dark in every theme, whereas --ink is text and flips. */
    --rail:       #0F2027;

    /* Rail width. The nav tree lives in here now, so it is wider than the old
       icon-only strip, and the top bar and content offsets read the same value. */
    --rail-w:     260px;

    /* Height of a panel's title bar. The tab strip sticks directly below the bar,
       so both read this one value and cannot drift apart -- they did once, when the
       bar's padding decided its height and the strip was left on a stale number. */
    --pagehead-h: 72px;
    --pool-deep:  #06484D;   /* hover, active */
    --pool-wash:  #E8F2F2;   /* selected rows, quiet fills */
    --surface:    #F6F8F8;   /* page background — cool, tiled */
    --card:       #FFFFFF;
    --line:       #D8E1E2;
    --line-soft:  #EAF0F0;

    --done:       #17703F;
    --done-wash:  #E6F2EA;
    --due:        #B36A00;
    --due-wash:   #FDF2E0;
    --overdue:    #A81F16;
    --overdue-wash:#FBEAE8;
    --idle:       #6B7F85;
    --idle-wash:  #EEF2F3;

    /* --- Scale -------------------------------------------------------------
       Touch targets are 44px minimum throughout. Not a preference: the people
       using this are wearing gloves or have wet hands. */
    --tap: 44px;
    --r: 6px;             /* restrained radius — signage, not bubbles */
    --shadow: 0 1px 2px rgba(15, 32, 39, .06), 0 4px 12px rgba(15, 32, 39, .05);

    --font: 'Archivo', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
    --font-narrow: 'Archivo Narrow', 'Archivo', 'Segoe UI', Arial, sans-serif;

    font-family: var(--font);
    color: var(--ink);
    background: var(--surface);
    font-size: 15px;
    line-height: 1.5;
    margin: 0;                /* harmless when .sm is on a wrapper; kills the UA
                                 body gutter when it is applied to <body> */
    -webkit-font-smoothing: antialiased;
}

.sm *, .sm *::before, .sm *::after { box-sizing: border-box; }

/* All figures align in columns. Compliance is read by comparing numbers. */
.sm table, .sm .sm-num, .sm .sm-stat__value { font-variant-numeric: tabular-nums; }

.sm a { color: var(--pool); text-decoration: none; }
.sm a:hover { color: var(--pool-deep); text-decoration: underline; }

/* Visible focus everywhere. Staff tab through these forms with a keyboard at the
   desk and this must never be invisible. */
.sm a:focus-visible,
.sm button:focus-visible,
.sm input:focus-visible,
.sm select:focus-visible,
.sm textarea:focus-visible,
.sm [tabindex]:focus-visible {
    outline: 3px solid var(--pool);
    outline-offset: 2px;
    border-radius: 3px;
}

/* ======================================================================
   Eyebrow / section label — the signage voice
   ====================================================================== */
.sm-eyebrow {
    font-family: var(--font-narrow);
    font-weight: 700;
    font-size: 11px;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin: 0 0 6px;
}

/* ======================================================================
   Shell: fixed left rail + content
   Mirrors the app's existing sidebar structure so the same PHP layout works.
   ====================================================================== */
/* The shell carries the rail's colour so the dark band reaches the bottom on
   long pages — the rail itself is sticky at 100vh and would otherwise stop short. */
.sm-shell { display: flex; min-height: 100vh; background: var(--ink); }

.sm-rail {
    width: var(--rail-w);
    flex: 0 0 var(--rail-w);
    background: var(--ink);
    color: #C7D6DA;
    display: flex;
    flex-direction: column;
    position: sticky;
    top: 0;
    height: 100vh;
}

.sm-rail__brand {
    padding: 20px 18px 16px;
    border-bottom: 1px solid rgba(255, 255, 255, .08);
}
.sm-rail__brand img { max-width: 100%; height: auto; display: block; }
.sm-rail__brand-text {
    font-family: var(--font-narrow);
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: #FFFFFF;
    font-size: 15px;
}

.sm-rail__who {
    padding: 14px 18px;
    border-bottom: 1px solid rgba(255, 255, 255, .08);
    display: flex;
    align-items: center;
    gap: 10px;
}
.sm-rail__avatar {
    width: 32px; height: 32px; border-radius: 50%;
    background: var(--pool); color: #fff;
    display: flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: 13px; flex: 0 0 32px;
}
.sm-rail__name { color: #fff; font-weight: 600; font-size: 14px; line-height: 1.2; }
.sm-rail__role { color: #8AA3A9; font-size: 12px; }

.sm-rail__nav { padding: 10px 0; overflow-y: auto; flex: 1; }
.sm-rail__link {
    display: flex; align-items: center; gap: 12px;
    min-height: var(--tap);
    padding: 0 18px;
    color: #C7D6DA;
    font-size: 14px;
    font-weight: 500;
    border-left: 3px solid transparent;
}
.sm-rail__link:hover { background: rgba(255, 255, 255, .05); color: #fff; text-decoration: none; }
.sm-rail__link--active {
    background: rgba(255, 255, 255, .07);
    color: #fff;
    border-left-color: var(--pool);
}
.sm-rail__icon { width: 18px; text-align: center; opacity: .85; flex: 0 0 18px; }
.sm-rail__badge {
    margin-left: auto;
    background: var(--overdue); color: #fff;
    font-size: 11px; font-weight: 700;
    border-radius: 9px; padding: 1px 7px;
}

.sm-main { flex: 1; min-width: 0; display: flex; flex-direction: column; background: var(--surface); }

/* ======================================================================
   Page header
   ====================================================================== */
.sm-head {
    background: var(--card);
    border-bottom: 1px solid var(--line);
    padding: 18px 28px;
    display: flex;
    align-items: flex-end;
    gap: 20px;
    flex-wrap: wrap;
}
.sm-head__title { font-size: 24px; font-weight: 700; margin: 0; letter-spacing: -.01em; }
.sm-head__meta { color: var(--ink-faint); font-size: 13px; margin: 2px 0 0; }
.sm-head__actions { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }

.sm-body { padding: 24px 28px 48px; flex: 1; }
.sm-body--narrow { max-width: 880px; }

/* ======================================================================
   Action banner — the app's existing "You have actions which require
   completing" bar, restyled. Kept prominent: it is the app's main nudge.
   ====================================================================== */
.sm-banner {
    display: flex; align-items: center; gap: 12px;
    background: var(--overdue-wash);
    border: 1px solid #F0C4BF;
    border-left: 5px solid var(--overdue);
    border-radius: var(--r);
    padding: 12px 16px;
    margin-bottom: 20px;
    color: #7C160F;
    font-weight: 500;
}
.sm-banner__count {
    background: var(--overdue); color: #fff;
    font-family: var(--font-narrow); font-weight: 700;
    border-radius: 4px; padding: 1px 8px; font-size: 13px;
}
.sm-banner--calm {
    background: var(--done-wash); border-color: #BFDCC9;
    border-left-color: var(--done); color: #10502D;
}

/* ======================================================================
   Buttons
   ====================================================================== */
.sm .btn,
.sm-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    min-height: var(--tap);
    padding: 0 18px;
    border-radius: var(--r);
    border: 1px solid transparent;
    font-family: var(--font);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    background: var(--card);
    color: var(--ink);
    border-color: var(--line);
    transition: background .12s ease, border-color .12s ease;
}
.sm .btn:hover, .sm-btn:hover { background: var(--pool-wash); border-color: #B9D2D3; text-decoration: none; color: var(--ink); }

.sm .btn-primary, .sm-btn--primary { background: var(--pool); border-color: var(--pool); color: #fff; }
.sm .btn-primary:hover, .sm-btn--primary:hover { background: var(--pool-deep); border-color: var(--pool-deep); color: #fff; }

.sm .btn-danger, .sm-btn--danger { background: var(--overdue); border-color: var(--overdue); color: #fff; }
.sm .btn-danger:hover, .sm-btn--danger:hover { background: #8C1911; border-color: #8C1911; color: #fff; }

/* .btn-default is the app's MOST-used button (354 uses) and the mock had no rule for it.
   Bootstrap gives it orange text on white here (.btn-default{color:#f58a1b;border-color:#f58a1b}
   in the customised bootstrap.min.css), and a.btn-default in basic_stylesheet.css:10 forces
   colour #333 on the anchor form -- which beats the base .sm .btn rule for `color` because it
   carries an element (a.btn-default = 0,2,1 vs .sm .btn = 0,2,0).
   Stated explicitly rather than left to specificity: this is the neutral secondary button. */
.sm .btn-default,
.sm a.btn-default,
.sm a.btn-default:link,
.sm a.btn-default:visited { background: var(--card); border-color: var(--line); color: var(--ink); }
.sm .btn-default:hover,
.sm a.btn-default:hover,
.sm a.btn-default:link:hover,
.sm a.btn-default:visited:hover { background: var(--pool-wash); border-color: #B9D2D3; color: var(--ink); }

.sm-btn--quiet { background: transparent; border-color: transparent; color: var(--pool); }
.sm-btn--sm { min-height: 34px; padding: 0 12px; font-size: 13px; }

/* ======================================================================
   Status chip
   ====================================================================== */
.sm-chip {
    display: inline-flex; align-items: center; gap: 6px;
    font-family: var(--font-narrow);
    font-weight: 700;
    font-size: 11px;
    letter-spacing: .09em;
    text-transform: uppercase;
    padding: 3px 9px;
    border-radius: 3px;
    white-space: nowrap;
}
.sm-chip--done    { background: var(--done-wash);    color: var(--done); }
.sm-chip--due     { background: var(--due-wash);     color: var(--due); }
.sm-chip--overdue { background: var(--overdue-wash); color: var(--overdue); }
.sm-chip--idle    { background: var(--idle-wash);    color: var(--idle); }

/* A dot carries the state for anyone who cannot separate the hues.
   Colour is never the only signal — the chip's word says it too. */
.sm-chip__dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: 0 0 7px; }

/* ======================================================================
   SIGNATURE — status spine
   ====================================================================== */
.sm-spine {
    position: relative;
    background: var(--card);
    border: 1px solid var(--line);
    border-left-width: 5px;
    border-left-color: var(--idle);
    border-radius: var(--r);
    padding: 14px 16px 14px 18px;
    margin-bottom: 10px;
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
}
.sm-spine--done    { border-left-color: var(--done); }
.sm-spine--due     { border-left-color: var(--due); }
.sm-spine--overdue { border-left-color: var(--overdue); }
.sm-spine--idle    { border-left-color: var(--idle); }

.sm-spine__main { flex: 1; min-width: 220px; }
.sm-spine__title { font-weight: 600; font-size: 15px; margin: 0 0 2px; }
.sm-spine__meta { color: var(--ink-faint); font-size: 13px; }
.sm-spine__side { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }

/* The whole row is a link, but it should read as content, not as link text — the
   chip and button carry the affordance, and a list of 50 teal headings is noise.
   NOTE the selector weight: `.sm a` is (0,1,1), so `.sm-spine--link` alone (0,1,0)
   loses and the titles stay teal. Qualifying with `a` is what makes this apply. */
.sm a.sm-spine--link { color: var(--ink); }
.sm a.sm-spine--link:hover { border-color: #B9D2D3; box-shadow: var(--shadow); text-decoration: none; color: var(--ink); }
.sm a.sm-spine--link:hover .sm-spine__title { color: var(--pool-deep); }

/* ======================================================================
   Cards / panels — restyles the app's existing .panel
   ====================================================================== */
.sm .panel,
.sm-card {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--r);
    margin-bottom: 20px;
    box-shadow: none;
}
.sm .panel-heading,
.sm-card__head {
    padding: 14px 18px;
    border-bottom: 1px solid var(--line-soft);
    display: flex;
    align-items: center;
    gap: 12px;
    background: transparent;
}
.sm-card__title { font-size: 16px; font-weight: 700; margin: 0; }
.sm .panel-body,
.sm-card__body { padding: 18px; }

/* ======================================================================
   Stat tiles
   ====================================================================== */
.sm-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px; margin-bottom: 22px; }
.sm-stat {
    background: var(--card);
    border: 1px solid var(--line);
    border-top: 3px solid var(--pool);
    border-radius: var(--r);
    padding: 16px 18px;
}
.sm-stat--done    { border-top-color: var(--done); }
.sm-stat--due     { border-top-color: var(--due); }
.sm-stat--overdue { border-top-color: var(--overdue); }
.sm-stat__label {
    font-family: var(--font-narrow); font-weight: 600; font-size: 11px;
    letter-spacing: .12em; text-transform: uppercase; color: var(--ink-faint);
    margin-bottom: 6px;
}
.sm-stat__value { font-size: 32px; font-weight: 700; line-height: 1; letter-spacing: -.02em; }
.sm-stat__note { font-size: 12px; color: var(--ink-faint); margin-top: 6px; }

/* ======================================================================
   Tables — restyles the app's existing .table
   ====================================================================== */
.sm-tablewrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r); background: var(--card); }
.sm .table,
.sm-table { width: 100%; border-collapse: collapse; margin: 0; }
.sm .table > thead > tr > th,
.sm-table th {
    text-align: left;
    font-family: var(--font-narrow);
    font-weight: 700; font-size: 11px;
    letter-spacing: .11em; text-transform: uppercase;
    color: var(--ink-faint);
    padding: 11px 14px;
    border-bottom: 1px solid var(--line);
    background: #FBFCFC;
    white-space: nowrap;
}
.sm .table > tbody > tr > td,
.sm-table td {
    padding: 12px 14px;
    border-top: 1px solid var(--line-soft);
    vertical-align: middle;
    font-size: 14px;
}
.sm .table > tbody > tr:hover,
.sm-table tbody tr:hover { background: var(--pool-wash); }
.sm-table__num { text-align: right; }

/* Element-level typography, scoped to .sm.
   349 of the app's 491 <table> tags carry NO .table class, so the rules above reach only 142 of
   them. The rest take their font from app-ui.css:11-30, which sets `a, body, input, option,
   select, td` to Helvetica/14px/#666 -- so without this block the majority of tables and every
   bare input would keep the old typography while their surroundings changed, which reads as a
   half-finished migration rather than a redesign.
   These are scoped (.sm td = 0,1,1), so they beat app-ui.css's bare element selectors (0,0,1)
   while still applying only inside .sm -- the file's no-bare-element-selector contract holds. */
.sm td,
.sm th,
.sm input,
.sm select,
.sm option,
.sm textarea,
.sm td.listCells { font-family: var(--font); color: var(--ink); }
.sm td,
.sm td.listCells { font-size: 14px; }

/* ======================================================================
   Forms — restyles the Bootstrap 3 markup the app already emits.
   The horizontal .form-group / .control-label / .col-sm-* structure is kept
   EXACTLY as generated; only its appearance changes.
   ====================================================================== */
/* Row chrome is deliberately tight. Measured on the 52-field Profile form, the mock's
   10px vertical padding plus a 4px margin added ~24px of pure spacing per row and made
   the page 44% LONGER than the original Bootstrap layout (4,231px against 2,931px,
   median row 65px against 34px). On a form that size the extra air stops being generous
   and turns into scrolling.
   The 44px control height is NOT reduced -- that is the tap target for wet hands on a
   poolside tablet, and it is the one measurement in this design that is a safety
   decision rather than an aesthetic one. The separator line does the work the padding
   was doing. */
/* align-items: start, NOT center. Row height is set by the FIELD, so centring floated
   every label away from the control it names: measured on the admin profile, "Staff No"
   sat 23px below its input, "First Name" 11px below, and the "Notes" label hung in the
   middle of a 134px textarea row instead of beside its first line. The label is then
   padded down to sit level with the text INSIDE the control rather than the control's
   top edge, which is what makes a column of labels read as aligned. */
/* :not(.row) here for the same reason as the two rules below, and this is the one
   that actually bites: the 10px gap is added to a two-column layout row whose
   columns already total exactly 100%, so 608 + 10 + 608 overflows 1216 and the
   second column wraps underneath. On the IMS page that put the page actions on
   their own line below the title, and the field divider drew a rule under the
   page header. */
.sm .form-group:not(.row) { margin-bottom: 0; padding: 3px 0; border-bottom: 1px solid var(--line-soft); display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px; }
.sm .form-group:not(.row):last-child { border-bottom: 0; }
.sm .control-label {
    font-weight: 600; font-size: 14px; color: var(--ink);
    text-align: left; margin: 0;
    flex: 0 0 260px; max-width: 260px;
    /* Sits the label's text on the same line as the text inside the control. The control
       is 44px tall with 8px of its own padding, so its value renders ~13px down; without
       this the label clings to the top edge of the row and reads as floating above its
       field rather than beside it. Line-height matches the control's so the two agree. */
    padding-top: 13px;
    line-height: 1.4;
}
/* The field column. The mock only claimed .col-sm-10, but the app emits the whole range:
   col-sm-10 (180), col-sm-9 (157), col-sm-6 (66), col-sm-3 (44), col-sm-12 (32), col-sm-2 (26),
   col-sm-4 (21), col-sm-8 (5). Matching only col-sm-10 would leave every other field carrying
   its Bootstrap percentage width inside a flex row next to a fixed 260px label, so the field
   would wrap under the label on the majority of forms in the app.

   :not(.control-label) is load-bearing, not defensive. The label is ITSELF a col-sm-* on 309 of
   ~314 occurrences ("control-label col-sm-2" 118, "col-sm-3 control-label" 105, ...), so a bare
   [class*="col-sm-"] would hand the label flex:1 as well and collapse the two-column layout that
   this rule exists to preserve. */
.sm .form-group:not(.row) > [class*="col-sm-"]:not(.control-label),
.sm .form-group:not(.row) > .sm-field { flex: 1; min-width: 220px; padding: 0; width: auto; }

.sm .form-control,
.sm-input {
    display: block; width: 100%; max-width: 420px;
    min-height: var(--tap);
    padding: 8px 12px;
    font-family: var(--font); font-size: 14px; color: var(--ink);
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--r);
    box-shadow: none;
}
.sm .form-control:focus, .sm-input:focus { border-color: var(--pool); box-shadow: 0 0 0 3px rgba(11, 110, 117, .12); outline: none; }
.sm select.form-control, .sm-input--select { max-width: 260px; }

/* The 420px cap above is a readability measure for ordinary fields, but the app has
   two cases that mean "use the whole width" and the cap silently shrank both.
   Measured on a real page before fixing: .form-control.full-size computed to 420px.

   .full-size (110 uses, basic_stylesheet.css:318 `display:block;width:100%`) is applied
   exactly where a field is meant to fill its container -- long text, comment boxes.

   .input-group lays its parts out as a table row; capping a member of that row breaks
   the addon/button alignment rather than just narrowing the field. */
.sm .form-control.full-size,
.sm select.form-control.full-size,
.sm .input-group .form-control,
.sm .input-group select.form-control { max-width: none; }

/* Disabled reads as genuinely unavailable, not merely faded — the greyed
   dependent permissions must be unmistakable. */
.sm .form-control[disabled], .sm .form-control:disabled, .sm-input:disabled {
    background: #EFF3F3; color: var(--ink-faint); cursor: not-allowed; border-color: var(--line);
}
.sm .help-block, .sm-help { font-size: 13px; color: var(--ink-faint); margin: 6px 0 0; }
.sm-help--req { color: var(--due); font-weight: 500; }

.sm-fieldset { margin-bottom: 26px; }
.sm-fieldset__legend {
    font-family: var(--font-narrow); font-weight: 700; font-size: 12px;
    letter-spacing: .12em; text-transform: uppercase; color: var(--pool);
    padding-bottom: 8px; margin-bottom: 4px; border-bottom: 2px solid var(--pool-wash);
    width: 100%;
}

/* ======================================================================
   Quick links grid (home)
   ====================================================================== */
.sm-links { display: grid; grid-template-columns: repeat(auto-fill, minmax(178px, 1fr)); gap: 12px; }
.sm-link {
    display: flex; flex-direction: column; gap: 10px;
    min-height: 104px;
    padding: 16px;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--r);
    color: var(--ink);
    font-weight: 600;
    position: relative;
}
.sm-link:hover { border-color: var(--pool); box-shadow: var(--shadow); text-decoration: none; color: var(--ink); }
.sm-link__icon { color: var(--pool); font-size: 20px; }
.sm-link__name { font-size: 14px; line-height: 1.3; }
.sm-link__count {
    position: absolute; top: 12px; right: 12px;
    background: var(--overdue); color: #fff;
    font-family: var(--font-narrow); font-weight: 700; font-size: 11px;
    border-radius: 9px; padding: 1px 7px;
}

/* ======================================================================
   Progress bar (training compliance)
   ====================================================================== */
.sm-meter { height: 8px; background: var(--line-soft); border-radius: 4px; overflow: hidden; min-width: 110px; }
.sm-meter__fill { height: 100%; background: var(--done); border-radius: 4px; }
.sm-meter__fill--due { background: var(--due); }
.sm-meter__fill--overdue { background: var(--overdue); }

/* ======================================================================
   Login
   ====================================================================== */
.sm-auth { min-height: 100vh; display: grid; grid-template-columns: 1.1fr .9fr; }
.sm-auth__panel {
    background: var(--ink);
    color: #DCE7E9;
    padding: 56px 52px;
    display: flex; flex-direction: column; justify-content: space-between;
    /* A calm tiled wash — the pool hall, abstracted. Two gradients, no images:
       nothing to 404, nothing extra to download at the door. */
    background-image:
        radial-gradient(900px 500px at 78% -8%, rgba(11, 110, 117, .40), transparent 62%),
        linear-gradient(180deg, rgba(255,255,255,.03) 0 1px, transparent 1px 44px);
}
.sm-auth__lede { font-size: 30px; line-height: 1.25; font-weight: 700; color: #fff; max-width: 15ch; letter-spacing: -.01em; }
.sm-auth__sub { color: #9DB6BB; margin-top: 14px; max-width: 40ch; }
.sm-auth__foot { font-size: 12px; color: #7B979D; }

.sm-auth__form { display: flex; align-items: center; justify-content: center; padding: 40px 28px; background: var(--card); }
.sm-auth__box { width: 100%; max-width: 380px; }
.sm-auth__logo { margin-bottom: 28px; max-height: 64px; }

/* ----------------------------------------------------------------------
   The app-UI login and its 2FA step (views/login_screen__app_ui.view.php,
   views/login2fa.view.php): a light card, centred on the site's own
   backdrop (its colour and particle animation are left as they are).

   It was a translucent dark card pinned to the top of the screen, with the
   two fields squeezed side by side by the app-wide .form-group pairing rule,
   "Remember me" in dark text on the dark card and a plain white button. On
   a light card the app's own tokens read correctly in every display mode
   (dark, high contrast, colour-blind, larger text), so most of the .sm form
   rules are right as they stand and only the layout is set here.

   Keyed to the two panel ids and the login classes only: <custom-panel> is
   shared with the navigation overlay and the app list, and .form-group,
   .checkbox and .btn serve every form in the app. IDs, too, because the
   panel's own scoped styles are added to <head> at run time, after this file.
   ---------------------------------------------------------------------- */

/* the panel scrolls only when the card is taller than the screen (not a permanent empty track) */
.sm #app-ui-login-panel .custom-panel,
.sm #app-ui-login-code-panel .custom-panel { overflow-y: auto; }

/* centre the card; a tall card (phone, larger text) grows the panel and scrolls instead of clipping */
.sm #app-ui-login-panel .custom-panel__body,
.sm #app-ui-login-code-panel .custom-panel__body {
    display: flex;
    flex-direction: column;
}
.sm #app-ui-login-panel .custom-panel__container,
.sm #app-ui-login-code-panel .custom-panel__container {
    flex: 1 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 32px 16px;
}

.sm #app-ui-login-panel .app-ui-login-form,
.sm #app-ui-login-code-panel .app-ui-login-form {
    width: 100%;
    max-width: 400px;
    /* not app-ui.css's margin:auto: in a flex column an auto margin takes all the free
       space, which would override the centring above */
    margin: 0 auto;
    padding: 28px 24px 20px;
    background: var(--card);
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: var(--r);
    box-shadow: 0 18px 48px rgba(0, 0, 0, .35);
}

/* the glass tint was for the translucent card */
.sm .app-ui-login-form__filter { display: none; }

/* a white frame, so a white-backed logo still looks deliberate on a dark-mode card */
.sm .app-ui-login-form__body .app-ui-login-form__logo-wrapper {
    background: #fff;
    padding: 8px 12px;
    border-radius: var(--r);
}
.sm .app-ui-login-form__body .app-ui-login-form__logo { max-height: 72px; }

/* one field per line, full width: out of the app-wide two-column pairing and its row rule */
.sm .app-ui-login-form__body .form-group:not(.row) {
    display: block;
    width: 100%;
    max-width: none;
    padding: 0;
    border-bottom: 0;
    margin: 0 0 16px;
}
.sm .app-ui-login-form__body label.as-bootstrap {
    display: block;
    margin: 0 0 6px;
    font-weight: 600;
    font-size: var(--base-font, 14px);
}
/* at least 16px: iOS zooms the page into any smaller field it focuses */
.sm .app-ui-login-form__body .form-control { font-size: max(16px, var(--base-font, 14px)); }

.sm .app-ui-login-form__body .checkbox { margin: -4px 0 14px; }

/* the page's one action, filled in the accent; color var(--card) stays readable in dark mode */
.sm .app-ui-login-form__body .app-ui-login-form__btn {
    width: 100%;
    margin-top: 4px;
    background: var(--pool);
    border-color: var(--pool);
    color: var(--card);
    font-weight: 600;
}
.sm .app-ui-login-form__body .app-ui-login-form__btn:hover,
.sm .app-ui-login-form__body .app-ui-login-form__btn:focus,
.sm .app-ui-login-form__body .app-ui-login-form__btn:active {
    background: var(--pool-deep);
    border-color: var(--pool-deep);
    color: var(--card);
}

.sm .app-ui-login-form__body .app-ui-login-form__link-list {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 4px 16px;
    margin: 16px 0 0;
    padding: 12px 0 0;
    border-top: 1px solid var(--line);
}
/* underlined, so a link is a link without relying on its colour */
.sm .app-ui-login-form__body .app-ui-login-form__link {
    display: inline-block;
    padding: 8px 4px;
    font-family: inherit;
    font-size: var(--base-font, 14px);
    color: var(--pool);
    text-decoration: underline;
    text-underline-offset: 2px;
}
.sm .app-ui-login-form__body .app-ui-login-form__link:hover,
.sm .app-ui-login-form__body .app-ui-login-form__link:focus { color: var(--pool-deep); }

/* one look for every message: "Invalid Login" and "Password changed." arrive the same way */
.sm .app-ui-login-form__body .app-ui-login-form__alert {
    padding: 10px 12px;
    /* a tint and an edge of its own: --surface is the card colour, or nearly, in light and
       high-contrast modes, which left the message looking like plain text */
    border: 1px solid var(--line);
    border-left: 3px solid var(--pool);
    background: var(--pool-wash);
    color: var(--ink);
    border-radius: var(--r);
    overflow-wrap: anywhere;
}

/* the site's policy pages, under the card on the backdrop: the panel's light text, underlined */
.sm #app-ui-login-panel .app-ui-login-navigation,
.sm #app-ui-login-code-panel .app-ui-login-navigation {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 4px 20px;
    max-width: 400px;
    margin: 20px 0 0;
    padding: 0;
}
/* none to show: no gap under the card to push it off centre */
.sm #app-ui-login-panel .app-ui-login-navigation:not(:has(li)),
.sm #app-ui-login-code-panel .app-ui-login-navigation:not(:has(li)) { display: none; }
.sm #app-ui-login-panel .app-ui-login-navigation__link,
.sm #app-ui-login-panel .app-ui-login-navigation__link:hover,
.sm #app-ui-login-panel .app-ui-login-navigation__link:focus,
.sm #app-ui-login-code-panel .app-ui-login-navigation__link,
.sm #app-ui-login-code-panel .app-ui-login-navigation__link:hover,
.sm #app-ui-login-code-panel .app-ui-login-navigation__link:focus {
    display: inline-block;
    padding: 8px 4px;
    color: inherit;
    font-family: inherit;
    font-size: var(--base-font, 14px);
    text-decoration: underline;
}

@media (max-width: 480px) {
    .sm #app-ui-login-panel .custom-panel__container,
    .sm #app-ui-login-code-panel .custom-panel__container { padding: 24px 16px; }
    .sm #app-ui-login-panel .app-ui-login-form,
    .sm #app-ui-login-code-panel .app-ui-login-form { padding: 22px 16px 16px; }
}

/* the moving particles behind the card, for anyone who has asked for less motion */
@media (prefers-reduced-motion: reduce) {
    .sm #app-ui-login-panel .particles-js,
    .sm #app-ui-login-code-panel .particles-js { display: none; }
}

/* ======================================================================
   Small print / footer
   ====================================================================== */
.sm-foot { padding: 18px 28px; color: var(--ink-faint); font-size: 12px; border-top: 1px solid var(--line); }

/* ======================================================================
   Responsive — the rail collapses to a top bar on tablet/phone, which is
   where most of the floor staff actually are.
   ====================================================================== */
@media (max-width: 900px) {
    .sm-shell { flex-direction: column; }
    .sm-rail {
        width: 100%; flex: none; height: auto; position: static;
        flex-direction: row; align-items: center; overflow-x: auto;
    }
    .sm-rail__brand, .sm-rail__who { border-bottom: 0; border-right: 1px solid rgba(255,255,255,.08); white-space: nowrap; }
    .sm-rail__nav { display: flex; padding: 0; }
    .sm-rail__link { border-left: 0; border-bottom: 3px solid transparent; white-space: nowrap; }
    .sm-rail__link--active { border-left: 0; border-bottom-color: var(--pool); }
    .sm-head, .sm-body { padding-left: 16px; padding-right: 16px; }
    .sm-auth { grid-template-columns: 1fr; }
    .sm-auth__panel { display: none; }
    .sm .control-label { flex-basis: 100%; max-width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
    .sm * { transition: none !important; animation: none !important; }
}

/* Print: a duty board often ends up on paper on the wall. */
@media print {
    .sm-rail, .sm-head__actions { display: none !important; }
    .sm { background: #fff; }
    .sm-spine, .sm-card, .sm .panel { break-inside: avoid; }
}

/* ======================================================================
   App chrome — the rail, restyled onto the markup the app ALREADY emits.

   The mock's rail is new markup (.sm-rail__*), but app-ui.php emits
   .sidebar / .sidebar-menu__* and carries things that must not be disturbed:
   eight conditional entries, the #nav-icon and #communications-icon JS hooks,
   the .communications-badge counter, and both `if ($hide_nav != 'Y')` guards.
   Rewriting that markup to match the mock risks all of it for a purely visual
   gain, so the rail is reproduced here on the existing classes instead. The
   .sm-rail__* rules above stay for any future markup that wants them.

   Specificity note: app-ui.css positions the chrome with ID selectors
   (#sidebar-wrapper, #page-wrapper), which beat any class rule, so the layout
   overrides here are written as `.sm #id` (0,1,1,0) to outrank `#id` (0,1,0,0)
   while staying scoped to the new UI.

   The positioning MODEL is deliberately left alone -- fixed bottom bar on
   mobile, fixed left column on desktop. The mock uses a flex .sm-shell, which
   would need the shell markup restructured; only the appearance changes here.
   ====================================================================== */

/* --- Mobile: the bottom bar ------------------------------------------- */
/* --rail, NOT --ink. --ink is the text colour; it only happened to be dark in the
   light theme. Keyed to it, the rail inverted to light grey the moment dark mode
   made --ink near-white. */
.sm #sidebar-wrapper { background-color: var(--rail); border-top: 1px solid rgba(255,255,255,.08); }

.sm .sidebar-menu,
.sm .sidebar-menu.panel,
.sm .sidebar-menu.panel-default {
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    margin-bottom: 0;
}
.sm .sidebar-menu > .panel-body { padding: 0; }

.sm .sidebar-menu__link { color: #C7D6DA; }
.sm .sidebar-menu__link:hover,
.sm .sidebar-menu__link:focus,
.sm .sidebar-menu__link:active { color: #fff; text-decoration: none; }
.sm .sidebar-menu__link--red { color: var(--overdue); }
.sm .sidebar-menu__icon { opacity: .9; }

/* The unread-communications counter. app-ui.css paints it #f44336 and pins it
   absolutely (top:-5px/left:59px) to sit over the OLD stacked icon; in a horizontal
   row that lands on top of the label, so it is returned to normal flow and pushed to
   the trailing edge like the mock's .sm-rail__badge. */
.sm .sidebar-menu__link .badge,
.sm .communications-badge { background-color: var(--overdue); color: #fff; font-weight: 700; border: 0; }
.sm .sidebar-menu__link .communications-badge {
    position: static;
    top: auto; left: auto;
    /* The badge precedes the label in the markup, so margin-left:auto alone would push
       the LABEL right and leave the count wedged between icon and text. order sends it
       to the trailing edge without touching the PHP. */
    order: 99;
    margin-left: auto;
    border-radius: 9px;
    padding: 1px 7px;
    font-size: 11px;
}

/* app-ui.css turns the whole link red while messages are unread. #A81F16 is chosen
   for white backgrounds and goes muddy on the dark rail, so lift it for contrast --
   the signal is kept, only the tone changes. */
.sm .sidebar-menu__link--red { color: #FF8A80; }
.sm .sidebar-menu__link--red:hover { color: #FFB3AC; }

/* The sidebar had no active state at all: every page looked identical in the rail.
   The hrefs are one-shot xtl tokens so the item cannot know it is current on its own;
   the server marks it (see $active_nav in webout_page_processing.php).

   Colour is deliberately NOT forced on the comms link when it is red: that red is the
   unread-messages signal, and turning it white while the item is active would hide a
   real notification. Background and the edge marker carry the active state instead. */
.sm .sidebar-menu__item--active > .sidebar-menu__link,
.sm .sidebar-menu__item--active > .sidebar-menu__link:hover,
.sm .sidebar-menu__item--active > .sidebar-menu__link:focus {
    background: rgba(255,255,255,.10);
    font-weight: 600;
}
.sm .sidebar-menu__item--active > .sidebar-menu__link:not(.sidebar-menu__link--red) { color: #fff; }

/* --- Desktop: the 208px rail ------------------------------------------ */
@media (min-width: 768px) {
    .sm #sidebar-wrapper {
        width: var(--rail-w);
        padding: 0;
        border-top: 0;
        border-right: 1px solid rgba(255,255,255,.08);

        /* FIXED, not absolute. app-ui.css positions the desktop rail absolutely, which
           means it scrolls away with the document: measured on the 4,231px Profile page,
           scrolling to the bottom put the rail at top:-3331 -- entirely off screen, so
           the navigation simply vanished partway down every long page. It also set
           `bottom: auto`, leaving the rail only as tall as its own menu items, which was
           invisible on a white sidebar and obvious on a dark one.
           Fixed + inset keeps it in place and full height at any scroll position, which
           is what the mock's sticky .sm-rail does. #page-wrapper already carries
           margin-left, so taking the rail out of flow changes no layout. */
        position: fixed;
        top: 0;
        bottom: 0;
        left: 0;
        right: auto;
        overflow-y: auto;   /* long menus stay reachable rather than being clipped */
    }
    /* app-ui.css sets margin-left:170px to clear the old rail; match the new width. */
    .sm #page-wrapper { margin-left: var(--rail-w); }

    .sm .sidebar__logo {
        padding: 18px 16px 14px;
        margin: 0;
        border-bottom: 1px solid rgba(255,255,255,.08);
        background: rgba(255,255,255,.03);
    }
    .sm .sidebar__logo img { max-width: 100%; height: auto; }

    .sm .sidebar-menu__items { padding: 8px 0; }

    /* Icon beside the label rather than stacked above it, with the active-edge
       device from the mock. */
    .sm .sidebar-menu__item { margin: 0; padding: 0; }
    .sm .sidebar-menu__link {
        display: flex;
        align-items: center;
        gap: 12px;
        min-height: var(--tap);
        padding: 0 16px;
        font-size: 14px;
        font-weight: 500;
        border-left: 3px solid transparent;
    }
    .sm .sidebar-menu__link:hover { background: rgba(255,255,255,.05); border-left-color: rgba(255,255,255,.18); }
    /* .sidebar-menu__link already reserves a 3px transparent left border for this. */
    .sm .sidebar-menu__item--active > .sidebar-menu__link { border-left-color: var(--pool); }

    .sm .sidebar-menu__icon {
        font-size: 16px;
        display: inline-block;
        margin: 0;
        width: 18px;
        flex: 0 0 18px;
        text-align: center;
    }
    .sm .sidebar-menu__icon--large { font-size: 18px; }

    .sm .sidebar-menu__item-name { display: block; }

    .sm .sidebar-menu__user-avatar {
        margin: 0;
        width: 30px; height: 30px;
        flex: 0 0 30px;
        border-radius: 50%;
        background-size: cover;
        background-position: center;
    }
}

/* ======================================================================
   Site banner — the "actions which require completing" strip.
   Injected into every page by str_replace on </header>, so it is the single
   most-seen component in the app. basic_stylesheet.css themes it .main
   (#2db6bf cyan), .info, .danger and .warning; those are the old palette and
   they sit directly above content that is now teal.
   Kept as a full-width strip -- staff scan for it -- but on the design's own
   status colours, and given the leading status spine used elsewhere so it
   reads as part of the same vocabulary.
   ====================================================================== */
.sm .site-banners__banner {
    padding: 11px 14px;
    font-weight: 600;
    font-size: 14px;
    border-left: 5px solid transparent;
    border-radius: var(--r);
}
.sm .site-banners__banner.main {
    background-color: var(--pool-wash);
    border-left-color: var(--pool);
    color: var(--ink);
}
.sm .site-banners__banner.main.site-banners__banner--actionable:hover { background-color: #DAEAEA; }

.sm .site-banners__banner.info {
    background-color: var(--pool-wash);
    border-left-color: var(--pool);
    color: var(--ink);
}
.sm .site-banners__banner.info.site-banners__banner--actionable:hover { background-color: #DAEAEA; }

.sm .site-banners__banner.danger {
    background-color: var(--overdue-wash);
    border-left-color: var(--overdue);
    color: #7A1610;
}
.sm .site-banners__banner.warning {
    background-color: var(--due-wash);
    border-left-color: var(--due);
    color: #7A4700;
}


/* =========================================================================
   StaffMIS UI — Bootstrap 3 compatibility layer
   Appended to public/css/staffmis-ui.css, same contract: every selector scoped
   under .sm / .sm-, no !important, no bare element selectors, and the app's
   spacing utilities (.mb-0 .mb-1 .mt-0 .mt-1 .mt-05 .ml-1) are never
   redefined — only excluded with :not() where a rule here would outrank them.

   :not() is always CHAINED, never a selector list: `:not(a, b)` is CSS
   Selectors 4 and is dropped WHOLE by older Android WebView, a real target
   here (poolside tablets).
   ========================================================================= */


/* =========================================================================
   NAV — .nav-tabs / .nav-pills / .nav-sequence
   BS4 JS against BS3 CSS, so two active patterns are both live: server-rendered
   tabs mark `li.active > a`, data-toggle="tab" tabs mark `li > a.active` with
   no class on the li. Every state rule covers both, or the Task Book tabs have
   no active state at all.
   ========================================================================= */

/* BS clearfixes .nav with :before/:after{content:" ";display:table}. Those
   become anonymous FLEX ITEMS once the ul is flex, and each eats a `gap`. */
.sm .nav-tabs::before,
.sm .nav-tabs::after,
.sm .nav-pills::before,
.sm .nav-pills::after { display: none; }

.sm .nav-tabs {
    display: flex;
    flex-wrap: wrap;          /* wrap, not scroll: an off-screen tab is a tab
                                 nobody knows exists */
    gap: 2px;
    border-bottom: 2px solid var(--line);
    padding-left: 0;
    list-style: none;
}
/* Guard, not a redefinition: (0,2,0) would outrank the app's (0,1,0) margin
   utilities, and the matrix navs ship `nav nav-tabs mb-0`. */
.sm .nav-tabs:not(.mb-0):not(.mb-1) { margin-bottom: 20px; }

/* Pulls the anchor's 3px underline down over the strip's 2px rule. */
.sm .nav-tabs > li { margin-bottom: -2px; }

.sm .nav-tabs > li > a {
    display: flex;
    align-items: center;
    gap: 8px;                 /* holds an fa icon and the count badge off the label */
    min-height: var(--tap);   /* 44px — gloves and wet hands, not a preference */
    padding: 0 16px;
    margin-right: 0;
    border: 0;
    border-bottom: 3px solid transparent;
    border-radius: var(--r) var(--r) 0 0;
    background: transparent;
    color: var(--ink-soft);   /* 6.8:1 — an inactive tab is still a label to read */
    font-weight: 600;
    font-size: 14px;
    line-height: 1.3;
    white-space: nowrap;
}
.sm .nav-tabs > li > a:hover,
.sm .nav-tabs > li > a:focus {
    background: var(--line-soft);
    border-bottom-color: var(--line);
    color: var(--ink);
    text-decoration: none;
}

/* Underline + fill replaces BS's folder tab: task_compliance_matrix renders 9
   tabs and the folder trick breaks on a second row, where the fill is the only
   signal that still reads. */
.sm .nav-tabs > li.active > a,
.sm .nav-tabs > li.active > a:hover,
.sm .nav-tabs > li.active > a:focus,
.sm .nav-tabs > li > a.active,
.sm .nav-tabs > li > a.active:hover,
.sm .nav-tabs > li > a.active:focus {
    background: var(--pool-wash);
    border: 0;
    border-bottom: 3px solid var(--pool);
    color: var(--pool);
    font-weight: 700;
    cursor: default;
}

/* A 2px-offset ring overlaps the neighbouring tab and reads as two selected
   tabs; inset keeps it inside the one that has focus. */
.sm .nav-tabs > li > a:focus-visible,
.sm .nav-pills > li > a:focus-visible { outline-offset: -3px; }

/* Tab count badges. On this product the count IS the information, so it must
   not read as decoration. Hover is handled once, in the badge section. */
.sm .nav-tabs > li > a > .badge {
    margin-left: 0;
    background: var(--idle-wash);
    color: var(--ink-soft);
    font-family: var(--font-narrow);
    font-weight: 700;
    font-size: 12px;
    border-radius: 3px;       /* squared — a pill badge next to a pill tab is mush */
    padding: 2px 7px;
}
.sm .nav-tabs > li.active > a > .badge,
.sm .nav-tabs > li > a.active > .badge { background: var(--pool); color: #fff; }

/* .nav-pills — the SECONDARY switcher, always nested below a tab strip. A solid
   chip row reads as "filter within this section", not "change section". */
.sm .nav-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding-left: 0;
    list-style: none;
}
.sm .nav-pills:not(.mb-0):not(.mb-1) { margin-bottom: 16px; }
.sm .nav-pills > li { float: none; margin-bottom: 0; }
.sm .nav-pills > li + li { margin-left: 0; }

.sm .nav-pills > li > a {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: var(--tap);
    padding: 0 16px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--card);
    color: var(--ink-soft);
    font-weight: 600;
    font-size: 14px;
    white-space: nowrap;
}
.sm .nav-pills > li > a:hover,
.sm .nav-pills > li > a:focus {
    background: var(--pool-wash);
    border-color: #B9D2D3;
    color: var(--ink);
    text-decoration: none;
}
.sm .nav-pills > li.active > a,
.sm .nav-pills > li.active > a:hover,
.sm .nav-pills > li.active > a:focus,
.sm .nav-pills > li > a.active,
.sm .nav-pills > li > a.active:hover,
.sm .nav-pills > li > a.active:focus {
    background: var(--pool);
    border-color: var(--pool);
    color: #fff;
    font-weight: 700;
    cursor: default;
}

/* Panes: NOTHING here may set display or visibility. BS switches panes with
   `.tab-content > .tab-pane{display:none}` / `> .active{display:block}`, and a
   `.sm .tab-pane` rule (0,2,0) would tie and win on order, showing every pane
   at once. learning_records nests a .tab-content inside a .tab-pane, so the
   inner pane must not add a third gap. */
.sm .tab-content { padding-top: 18px; }
.sm .tab-content > .tab-pane { padding-top: 0; }

/* .nav-sequence — the shift-availability wizard breadcrumb. Its markup is
   `<ul class="nav nav-sequence">`; it never carries .nav-tabs, so it inherits
   NONE of the base above and would keep BS's ~30px anchors. display:flex is
   deliberately not put on the <ul>: its children are floated. `disabled` marks
   steps the user has not reached yet, so it is the one real disabled nav. */
.sm .nav-sequence > li > a {
    display: flex;
    align-items: center;
    min-height: var(--tap);
    padding: 0 16px;
    font-weight: 600;
}
.sm .nav-sequence > li.active > a { border-bottom: 3px solid var(--pool); color: var(--pool); }
.sm .nav-sequence > li.disabled > a,
.sm .nav-sequence > li.disabled > a:hover,
.sm .nav-sequence > li.disabled > a:active,
.sm .nav-sequence > li.disabled > a:focus { color: var(--ink-faint); }

@media (max-width: 600px) {
    /* Tighter on a phone, never below the 44px tap target. */
    .sm .nav-tabs > li > a,
    .sm .nav-pills > li > a { padding: 0 12px; font-size: 13px; }
}


/* =========================================================================
   ALERTS (196 uses)
   Only BS 3.2's washed-out palette is being replaced — no app stylesheet
   defines .alert. Each variant gets the same 5px leading spine as .sm-spine and
   .site-banners__banner so a flash message joins one status vocabulary. Body
   copy stays --ink: the spine and wash carry the state, and ink on a pale wash
   is the highest-contrast option under glare.
   NO margin-bottom: BS's own `.alert{margin-bottom:20px}` is (0,1,0), so the
   app's spacing utilities keep beating it on source order. A (0,2,0) rule here
   would silently win over every `alert mb-0` / `mb-1`.
   ========================================================================= */
.sm .alert {
    --sm-alert-accent: var(--idle);
    --sm-alert-wash: var(--idle-wash);
    --sm-alert-line: var(--line);

    padding: 12px 16px;
    background-color: var(--sm-alert-wash);
    border: 1px solid var(--sm-alert-line);
    border-left: 5px solid var(--sm-alert-accent);
    border-radius: var(--r);
    color: var(--ink);
    font-size: 14px;
}
/* Bare .alert and any BS4 variant the Vue components emit (`alert-${type}` in
   BaseAlert.vue) fall through to idle rather than rendering transparent. */
.sm .alert-info    { --sm-alert-accent: var(--pool);    --sm-alert-wash: var(--pool-wash);    --sm-alert-line: #C3DADB; }
.sm .alert-success { --sm-alert-accent: var(--done);    --sm-alert-wash: var(--done-wash);    --sm-alert-line: #C2DECD; }
.sm .alert-warning { --sm-alert-accent: var(--due);     --sm-alert-wash: var(--due-wash);     --sm-alert-line: #F0DCB4; }
.sm .alert-danger  { --sm-alert-accent: var(--overdue); --sm-alert-wash: var(--overdue-wash); --sm-alert-line: #F0CFCB; }

/* The dismiss button is float:right, so a one-line alert would not contain it.
   Excluded on the 5 `.d-flex` alerts, where this would become a stray flex
   item — none of those has a close button. */
.sm .alert:not(.d-flex)::after { content: ""; display: table; clear: both; }

/* BS zeroes the margin on every direct p/ul, running paragraphs together. */
.sm .alert > p,
.sm .alert > ul { margin-bottom: 8px; }
.sm .alert > p:last-child,
.sm .alert > ul:last-child { margin-bottom: 0; }

/* `.sm a` is teal, wrong inside a red or amber block; the underline keeps
   colour from being the only signal. (0,2,1) also beats BS's
   `.alert-danger .alert-link` (0,2,0). */
/* :not(.btn): a link styled as a button keeps its button colours; as a plain alert link the
   accent colour turned e.g. a btn-primary label blue on blue (invisible). */
.sm .alert a:not(.btn) { color: var(--sm-alert-accent); font-weight: 600; text-decoration: underline; }
.sm .alert a:not(.btn):hover { color: var(--ink); }

/* .close (60 uses). BS ships it at opacity .2 with a white text-shadow bevel —
   near-invisible on a coloured wash under glare. VISIBILITY ONLY here: no
   position, float, width or height, because the same class is used in modal
   headers, in a grid column (dashboard `close col-xs-1`) and in the site
   banner, where basic_stylesheet.css:1552 positions it absolutely. The
   modal-header close is sized in the modals section — deliberately not here, so
   it has exactly one 44px strategy. */
.sm .close { color: var(--ink-soft); opacity: 1; text-shadow: none; }
.sm .close:hover,
.sm .close:focus { color: var(--ink); opacity: 1; }

/* In an alert the dismiss gets a real --tap box: it is the primary affordance
   on flash messages and is pressed with wet hands. Negative margins pull it
   into the alert's own padding so a one-line alert is ~52px, not 68px.
   top/right neutralise `.alert-dismissible .close{top:-2px;right:-21px}`, which
   existed for a 35px right padding this replaces. `:not([class*="col-"])`
   leaves the dashboard toast alone; sizing it would alter a grid column. */
.sm .alert .close:not([class*="col-"]) {
    position: relative;
    top: 0;
    right: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: var(--tap);
    min-height: var(--tap);
    margin: -8px -8px -8px 8px;
    border-radius: var(--r);
    color: var(--ink-soft);
}
.sm .alert .close:not([class*="col-"]):hover { background-color: rgba(15,32,39,.07); color: var(--ink); }
/* A teal focus ring on a red alert reads as a third status. */
.sm .alert .close:focus-visible { outline: 3px solid var(--sm-alert-accent); outline-offset: -3px; }


/* =========================================================================
   LIST GROUPS — .list-group / .list-group-item
   NO margin RULE ON .list-group: 10+ call sites combine it with the app's
   margin utilities (mb-0 / mb-1 / mb-05) at (0,1,0), and any (0,2,0) margin
   here would silently outrank all of them. Structure is left to Bootstrap: the
   -1px margin collapse and `.panel > .list-group .list-group-item
   {border-width:1px 0}` (0,3,0) give the edge-to-edge list-inside-a-panel that
   user_forms_team/show.view.php relies on. Colour, density and state only.
   ========================================================================= */

/* border-COLOR, not the shorthand — a shorthand would set border-width too.
   min-height is stated as well as padding because
   `#file-library-pages .list-group-item{padding:10px}` is (1,1,0) and beats any
   padding here; min-height is a property it does not set, so the 44px target
   survives on that screen. */
.sm .list-group-item {
    min-height: var(--tap);
    padding: 12px 15px;
    background-color: var(--card);
    border-color: var(--line);
    color: var(--ink);
    font-size: 14px;
}

/* Hover only on rows that are ACTUALLY actionable — a hover wash on a static
   <li> (handover notes, rota days) promises a tap target that is not there.
   The .list-group-clickable form is (0,5,0) so it wins outright over the app's
   own (0,4,0) hover; its :not(--unclickable) marks the row being edited.
   :not(.disabled) is load-bearing: without it (0,4,1) beats BS's
   `.list-group-item.disabled:hover` (0,3,0) and lights up locked rows. */
.sm a.list-group-item:not(.disabled):hover,
.sm a.list-group-item:not(.disabled):focus,
.sm button.list-group-item:not(.disabled):hover,
.sm .list-group-clickable .list-group-item:not(.list-group-item--unclickable):hover {
    background-color: var(--pool-wash);
    color: var(--ink);
    text-decoration: none;
}

/* webout_user_forms.php:1085 emits `list-group-item active` with a count .badge
   inside. The :hover/:focus forms are (0,4,0) to beat BS's own (0,3,0)
   variants, which would re-assert the retired blue on hover. */
.sm .list-group-item.active,
.sm .list-group-item.active:hover,
.sm .list-group-item.active:focus {
    z-index: 2;
    background-color: var(--pool);
    border-color: var(--pool);
    color: #fff;
}
.sm .list-group-item.active > .badge { background-color: #fff; color: var(--pool); }

/* Locked row — --ink-soft, not the --ink-faint used for disabled INPUTS: a
   locked training module still carries content the user must read. --ink-faint
   on --idle-wash is 3.1:1; --ink-soft is 6.1:1 and passes AA. */
.sm .list-group-item.disabled,
.sm .list-group-item.disabled:hover,
.sm .list-group-item.disabled:focus {
    background-color: var(--idle-wash);
    border-color: var(--line);
    color: var(--ink-soft);
    cursor: not-allowed;
}

/* Rota and timesheet day cards. margin-bottom is set on its own, not via the
   shorthand, so it cannot clobber `.list-group-rota .list-group-item-text +
   .list-group-item-text{margin-top:4px}`, which stacks shift lines. */
.sm .list-group-item-heading {
    margin-top: 0;
    margin-bottom: 4px;
    font-size: 15px;
    font-weight: 600;
    color: var(--ink);
}
.sm .list-group-item-text {
    margin-bottom: 0;
    font-size: 13px;
    line-height: 1.4;
    color: var(--ink-soft);
}

/* Unread communication. basic_stylesheet.css:2261 already gives this a 5px left
   bar in the old #0f92ff blue. Re-toned to --pool: unread is "new", not a
   compliance state, so it must NOT borrow a status colour. (0,3,0) so it
   survives inside a .panel, where the edge-to-edge border rule (0,3,0) would
   otherwise flatten the bar. */
.sm .list-group .list-group-item--unread {
    background-color: var(--card);
    border-left-width: 5px;
    border-left-style: solid;
    border-left-color: var(--pool);
}


/* =========================================================================
   PANELS — title / footer / wells
   .panel, .panel-heading and .panel-body are already in staffmis-ui.css; only
   the missing parts are added here.
   ========================================================================= */

/* The heading is display:flex in the base sheet. filelibrary.view.php:57-66 is
   the app's ONLY heading with a second child (the category description after
   the title), and without wrapping it lands on the title's line. flex:1 1 100%
   below gives the title the whole first row so the sibling wraps under it;
   where the title is the only child this is identical to the base. */
.sm .panel-heading { flex-wrap: wrap; }

/* Emitted on <h3>, <h4>, <div>, and on an ANCHOR for the collapse toggles
   (training_control/index.view.php:26 `class="d-block panel-title relative"`) —
   there is no `.panel-title > a` anywhere in the app. min-width:0 lets a long
   title shrink so the `pull-right` chevron filelibrary floats INSIDE the title
   is not pushed out of the heading. */
.sm .panel-title {
    margin: 0;
    font-family: var(--font);
    font-size: 16px;
    font-weight: 700;
    color: var(--ink);
    flex: 1 1 100%;
    min-width: 0;
}
/* `.sm a:hover` (0,2,1) ties basic_stylesheet.css:137 `a.panel-title:hover
   {color:inherit}` and wins on source order, turning the collapse header teal
   and underlined. (0,2,1), stated last. */
/* BOTH forms exist: the anchor carries the class in training_control, and
   webout_packets.php:13497-13500 emits `<h4 class="panel-title"><a ...>`. */
.sm .panel-title > a,
.sm .panel-title > a:hover,
.sm .panel-title > a:focus { color: inherit; text-decoration: none; }
.sm a.panel-title,
.sm a.panel-title:hover,
.sm a.panel-title:focus { color: var(--ink); text-decoration: none; }

/* Action bar under a panel — "Create New Group", "Add Job Role". Both real
   footers follow a .list-group whose last row still carries its own bottom
   border, so without the second rule the two stack into a 2px seam. */
.sm .panel-footer {
    padding: 12px 18px;
    background-color: #FBFCFC;
    border-top: 1px solid var(--line-soft);
    border-bottom-right-radius: var(--r);
    border-bottom-left-radius: var(--r);
}
.sm .list-group + .panel-footer { border-top-width: 0; }

/* .well — the quiet nested block. --surface not --pool-wash: a well is a
   recess, and --pool-wash is spoken for as the selected/hover state, so reusing
   it would make a static block look interactive. NO margin rule, same reason as
   .list-group: 13 call sites combine .well with the margin utilities. */
.sm .well {
    padding: 16px;
    background-color: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--r);
    box-shadow: none;
}
/* MUST follow .sm .well: BS's size modifier is (0,1,0), so the (0,2,0) padding
   above would flatten it. Equal weight here, later, so the scale survives. */
.sm .well-sm { padding: 10px 12px; border-radius: 4px; }


/* =========================================================================
   MODALS — BS 3.2 markup driven by BS 4.5.3 JS. basic_stylesheet.css:156-177
   already bridges .show/.fade to .in; nothing below sets opacity, transform or
   z-index, so that bridge keeps working and the animation is untouched.
   ========================================================================= */

/* .modal-lg is 900px, declared as `width` inside BS's min-width:768px block, so
   it overflows a 768-820px tablet sideways. max-width leaves those width values
   intact and only bites below ~920px. */
.sm .modal-dialog { max-width: calc(100% - 20px); }

.sm .modal-content {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--r);
    /* Heavier than --shadow on purpose: the sheet floats over a dimmed page and
       the token shadow disappears there. position:relative is left to BS —
       basic_stylesheet.css:1999 anchors the .is-loading overlay to it. */
    box-shadow: 0 12px 34px rgba(15, 32, 39, .30);
}

.sm .modal-header {
    padding: 14px 18px;
    border-bottom: 1px solid var(--line-soft);
    min-height: 0;
}
/* Deliberately NOT a flex row, unlike .panel-heading: .close is the FIRST child
   in all 37 headers with the title after it, so flex would move the x to the
   leading edge. BS's float model already puts it right; it just lacks a
   clearfix, so a taller close button would hang past the border. */
.sm .modal-header::after { content: ""; display: table; clear: both; }

.sm .modal-title {
    font-size: 16px;
    font-weight: 700;
    line-height: 1.3;
    color: var(--ink);
    margin: 0;
}

/* THE ONLY 44px strategy for this button. The 24px glyph is centred in a 44px
   box (10px inset each side), so pulling 10px back off top/right/bottom leaves
   the x exactly where an ungrown glyph would sit, with a 44px hit area
   overlapping the header padding — header height is unchanged. (0,3,0) beats
   `.modal-header .close` (0,2,0), `button.close` (0,1,1) and `.close:hover`
   (0,2,0), so the hover opacity fade is overridden too. */
.sm .modal-header .close {
    width: var(--tap);
    height: var(--tap);
    margin: -10px -10px -10px 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    line-height: 1;
    font-weight: 400;
    color: var(--ink-soft);
    opacity: 1;
    text-shadow: none;
    background: transparent;
    border: 0;
    border-radius: var(--r);
}
.sm .modal-header .close:hover,
.sm .modal-header .close:focus { color: var(--ink); background: var(--pool-wash); opacity: 1; }

/* Matches .sm-card__body so a modal and a panel read as the same surface. */
.sm .modal-body { padding: 18px; }

.sm .modal-footer {
    padding: 14px 18px;
    border-top: 1px solid var(--line-soft);
}
/* 8 footers are literally <div class="modal-footer"></div> and render as a
   stray grey bar. */
.sm .modal-footer:empty { display: none; }

/* A margin, not a flex gap: footers use .pull-left to hold destructive actions
   away from the confirm button, and flex would ignore those floats and slide
   Delete next to Ok. */
.sm .modal-footer .btn + .btn { margin-left: 8px; }

/* Background only. opacity is left to .modal-backdrop.fade/.show — setting it
   here would tie at (0,2,0), win on order and freeze the fade. */
.sm .modal-backdrop { background-color: var(--ink); }

/* Modal forms are STACKED label-above-field with a plain <label> (no
   .control-label) — ~90 groups across the rota, handover, newsletter and
   account modals; `.sm .form-group{display:flex}` would put the label beside
   its input and rule a line under each one. The three .form-horizontal forms
   that DO want two columns are excluded.
   :not(.input-group) on all four selectors is load-bearing: these are (0,5,0)
   and would otherwise beat `.sm .form-group.input-group{display:table}`
   (0,3,0), re-blockifying the Edit-slideshow attachment rows inside
   <v-bs-modal> and collapsing their delete-button cell to ~11px. */
.sm .modal-body > .form-group:not(.input-group):not(.row),
.sm .modal-body :not(.form-horizontal) > .form-group:not(.input-group):not(.row) {
    display: block;
    padding: 0;
    margin-bottom: 15px;
    border-bottom: 0;
}
.sm .modal-body > .form-group:not(.input-group):last-child,
.sm .modal-body :not(.form-horizontal) > .form-group:not(.input-group):last-child { margin-bottom: 0; }


/* =========================================================================
   TABLES — extends the .table block in staffmis-ui.css (width, border-collapse,
   narrow thead, cell padding, base row hover); nothing here restates it.
   ========================================================================= */

/* Scoped to `div`: 8 elements carry .table-responsive on the <table> itself,
   and the container chrome would draw a rounded box around a table that is
   itself the box. border-color alone re-tints BS's <768px #ddd edge on every
   wrapper, including the two excluded below. */
.sm div.table-responsive {
    -webkit-overflow-scrolling: touch;
    border-color: var(--line);
}

/* Chrome matching .sm-tablewrap. Two wrappers excluded, both for cause:
   .has-fixed-col freezes the first column by absolutely positioning
   td:first-child at left:0 with NO positioned ancestor, so adding position here
   would re-anchor it and move every frozen column on the rota screens;
   .qe-table-wrap is the Vue dashboard table, whose <style scoped> rules compile
   to (0,2,0) and would lose to these (0,3,1). */
.sm div.table-responsive:not(.has-fixed-col):not(.qe-table-wrap) {
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--card);
}
/* min-width, not width: a narrow table fills the box, but the wide report grids
   this container exists to scroll are still allowed to overflow. The wrapper
   now draws the outer edge, and BS only removes the table's own border under
   768px, so at desktop that would be two parallel lines 1px apart. */
.sm div.table-responsive:not(.has-fixed-col):not(.qe-table-wrap) > table { min-width: 100%; }
.sm div.table-responsive:not(.has-fixed-col):not(.qe-table-wrap) > .table-bordered { border-width: 0; }

/* .table-condensed — GATED ON .table, AND THE GATE IS LOAD-BEARING. 10 of the
   app's table-condensed elements are bootstrap-datepicker calendar grids, which
   carry no .table and are sized by `.datepicker table tr td{width:30px;
   height:30px}` (0,1,3). An ungated `.sm .table-condensed > tbody > tr > td` is
   (0,2,3), wins, and re-pads every date cell in the app. All 59 real data-table
   uses carry `table table-condensed`, so the gate separates them exactly.
   ~36px rows: BS's 5px gives ~30px, unusable with wet hands, but the full 44px
   is reserved for the controls inside a cell — 44px rows would push columns off
   a tablet screen, which is the worse failure. */
.sm .table.table-condensed > thead > tr > th,
.sm .table.table-condensed > thead > tr > td { padding: 7px 10px; }
.sm .table.table-condensed > tbody > tr > td,
.sm .table.table-condensed > tbody > tr > th,
.sm .table.table-condensed > tfoot > tr > td,
.sm .table.table-condensed > tfoot > tr > th { padding: 8px 10px; }

/* .table-bordered — the one place vertical rules are information: wide report
   grids read by tracking one row across 15+ columns. Kept, but dropped to
   --line-soft so the grid recedes behind the data. The header rule stays
   --line: it is the only thing separating labels from data once you have
   scrolled sideways. */
.sm .table.table-bordered { border-color: var(--line); }
.sm .table.table-bordered > thead > tr > th,
.sm .table.table-bordered > thead > tr > td,
.sm .table.table-bordered > tbody > tr > th,
.sm .table.table-bordered > tbody > tr > td,
.sm .table.table-bordered > tfoot > tr > th,
.sm .table.table-bordered > tfoot > tr > td { border: 1px solid var(--line-soft); }
.sm .table.table-bordered > thead > tr > th { border-bottom: 2px solid var(--line); }

/* .table-hover — the base row hover already washes every .table row, so what
   this adds is a LEADING MARKER: on the wide bordered grids a background wash
   is invisible once the row extends past the viewport, and losing your row
   mid-scroll is the real failure mode. An inset box-shadow on the first cell,
   not border-left on the row: no layout shift, and box-shadow on <tr> is
   unreliable in collapsed tables. */
@media (hover: hover) {
    .sm .table.table-hover > tbody > tr:hover { background: var(--pool-wash); }
    .sm .table.table-hover > tbody > tr:hover > td:first-child,
    .sm .table.table-hover > tbody > tr:hover > th:first-child { box-shadow: inset 3px 0 0 0 var(--pool); }
}
/* Keyboard equivalent, in its OWN rule — a browser that does not know
   :focus-within drops the whole selector list, and grouping would take the
   hover styling down with it. */
.sm .table.table-hover > tbody > tr:focus-within { background: var(--pool-wash); }
.sm .table.table-hover > tbody > tr:focus-within > td:first-child,
.sm .table.table-hover > tbody > tr:focus-within > th:first-child { box-shadow: inset 3px 0 0 0 var(--pool); }

/* .file_row / .file_row_left — the legacy record lists (job roles, rota blocks,
   suppliers, profile fields, polls). Always a <td> inside
   <table class="file_table">, which carries NO .table class, so none of the
   rules above reach them and content_style.css:103 is all that styles them.
   Two real defects there: padding-bottom:0, which is why these lists look
   squashed, and no horizontal rule, so a 40-row list is one block. */
.sm .file_row,
.sm .file_row_left {
    padding: 9px 10px;
    border-color: var(--line-soft);
    border-bottom-style: solid;   /* content_style.css sets this to `none` */
    vertical-align: middle;
}
/* Every first cell is an <a> to the edit screen, but the table has no .table
   class so the base row hover never reaches it. Selecting the owned cell class
   from a hovered row gives full-row feedback and cannot leak into the many
   legacy layout tables that also use bare <tr>. */
@media (hover: hover) {
    .sm tr:hover > .file_row,
    .sm tr:hover > .file_row_left { background: var(--pool-wash); }
}

/* Print: overflow-x:auto CLIPS on paper — every column past the fold is
   silently dropped with no indication anything is missing. On a compliance
   report that is a correctness problem, not a cosmetic one. */
@media print {
    .sm div.table-responsive,
    .sm .sm-tablewrap { overflow: visible; }
    .sm .table.table-condensed > tbody > tr,
    .sm .table.table-bordered > tbody > tr { break-inside: avoid; }
}


/* =========================================================================
   CHECKBOXES AND RADIOS
   The native control is KEPT — no pseudo-element facsimile: assistive tech
   reads the real input and the app's JS reads .checked throughout.
   BS's mechanic is kept, only rescaled. The input is placed by its STATIC
   position pulled left with a negative margin, NOT with `left`: `left` resolves
   against the wrapper's padding box, which would tear the control out of the
   app's own indents (.shift_availability_step1/_step4 .checkbox
   {padding-left:20px}, #shift_opportunities .checkbox{padding-left:40px}).
   A 22px box therefore needs 34px of label padding and a matching -34px pull.
   ========================================================================= */

/* Inherited, so this colours every checkbox and radio in the app — including
   bare ones no rule here selects — without touching the control's box, its
   semantics or its a11y. */
.sm { accent-color: var(--pool); }

/* Bare inputs, outside a wrapper: 18px not 22px because these live in fixed
   gutters (one positioned absolutely at right:0 inside a p with
   padding-right:20px, another at left:0 in a td with padding-left:20px) and
   22px would overflow both. Both exclusions are real: .switch__checkbox is a
   visually-hidden 0x0 input behind .switch__slider, so sizing it would
   resurrect the native box behind the toggle; .color-legend__checkbox sits in
   an 18px line-height legend row. Chained :not() — see the file header. */
.sm input[type="checkbox"]:not(.switch__checkbox):not(.color-legend__checkbox),
.sm input[type="radio"]:not(.switch__checkbox):not(.color-legend__checkbox) {
    width: 18px;
    height: 18px;
    cursor: pointer;
}

/* The label IS the tap target. 15px/1.4 text plus 11px padding lands a
   single-line row on exactly 44px. display:block is required, not cosmetic —
   BS's min-height:20px is inert on an inline label, so without it there is no
   hit area to enlarge. Weight 500 sits between BS's 400 and the 600 given to
   .control-label, so an option reads clearly without competing with the field
   label above it. */
.sm .checkbox label,
.sm .radio label {
    display: block;
    min-height: var(--tap);
    padding: 11px 0 11px 34px;
    margin: 0;
    font-size: 15px;
    line-height: 1.4;
    font-weight: 500;
    color: var(--ink);
    cursor: pointer;
}

/* The inline variants ARE their own label, so the hit area goes on the element
   itself; the right padding extends the target past the text. */
.sm .checkbox-inline,
.sm .radio-inline {
    min-height: var(--tap);
    padding: 11px 12px 11px 34px;
    margin-bottom: 0;
    font-size: 15px;
    line-height: 1.4;
    font-weight: 500;
    color: var(--ink);
    cursor: pointer;
}

/* The wrapped control carries the SAME two exclusions as the bare rule so it
   lands at (0,5,1) and beats that rule's (0,4,1) OUTRIGHT, not on source order.
   Without them the chained bare rule would force wrapped checkboxes to 18px.
   vertical-align:middle does real work: the input is absolutely positioned with
   `top` unset, so it resolves to the static position, which this sets. On
   baseline alignment a 22px box hangs ~7px above the line; with middle it
   measures dead-centre whether the label wraps or not, so a wrapping option
   keeps its control on line one. */
.sm .checkbox input[type="checkbox"]:not(.switch__checkbox):not(.color-legend__checkbox),
.sm .radio input[type="radio"]:not(.switch__checkbox):not(.color-legend__checkbox),
.sm .checkbox-inline input[type="checkbox"]:not(.switch__checkbox):not(.color-legend__checkbox),
.sm .radio-inline input[type="radio"]:not(.switch__checkbox):not(.color-legend__checkbox) {
    width: 22px;
    height: 22px;
    margin-top: 0;
    margin-left: -34px;
    vertical-align: middle;
}

/* BS tightens stacked controls with .checkbox+.checkbox{margin-top:-5px},
   leaving a 5px gap between what are now 44px targets. :not(.mt-0) is a guard,
   not a redefinition: ~21 elements ship as "checkbox mt-0" and this rule
   (0,3,0) would otherwise beat that utility (0,1,0). */
.sm .checkbox + .checkbox:not(.mt-0),
.sm .radio + .radio:not(.mt-0) { margin-top: 6px; }

/* Disabled arrives from Vue as an attribute on the INPUT, not as BS's
   .checkbox.disabled on the wrapper. :has() greys the label as a progressive
   enhancement — where unsupported the rule is dropped and the control still
   reads as disabled. No focus rule is needed: `.sm input:focus-visible` in
   staffmis-ui.css already rings these, and nothing here sets outline:none. */
.sm .checkbox input[type="checkbox"]:disabled,
.sm .radio input[type="radio"]:disabled { cursor: not-allowed; opacity: .55; }
.sm .checkbox label:has(input:disabled),
.sm .radio label:has(input:disabled),
.sm .checkbox-inline:has(input:disabled),
.sm .radio-inline:has(input:disabled) { color: var(--ink-faint); cursor: not-allowed; }


/* =========================================================================
   FORMS — static values, horizontal gutters, input groups, validation
   ========================================================================= */

/* REGRESSION FIX. basic_stylesheet.css:318 defines .form-control.full-size
   {display:block;width:100%} and the app uses it on fields meant to be
   full-width — but `.sm .form-control{max-width:420px}` (0,2,0) capped every
   one, and a .full-size <select> at 260px. (0,3,0) beats both that and the
   select-only `.sm select.form-control` (0,2,1). */
/* .form-control.full-size / .input-group .form-control cap release lives in the base
   sheet (staffmis-ui.css) -- not restated here. */

/* Static values (105 uses) — a read-only value shown where a field would be.
   BS pads it 7px vertically and 0 horizontally, so on a form mixing static and
   input rows the values sit 13px left of the input text and the left edge
   visibly zig-zags. Match the field box instead. 7 of them ARE the field column
   (`<p class="col-sm-10 form-control-static">`) rather than sitting inside one,
   so they are hit by `.sm .form-group > [class*="col-sm-"]:not(.control-label)
   {padding:0}` (0,4,0); the second rule restates the padding at equal weight,
   later, so both markup shapes align identically. */
.sm .form-control-static {
    min-height: var(--tap);
    padding: 11px 12px;
    margin: 0;                 /* clears the UA <p> margin; these are all <p> */
    font-size: 14px;
    line-height: 1.5;
    color: var(--ink);
}
.sm .form-group > .form-control-static[class*="col-sm-"] { padding: 11px 12px; }

/* BS gives .form-horizontal .form-group margin-left/right:-15px to cancel the
   grid gutter of its .col-* children. .form-group is no longer a grid row here
   (it is flex, and the field column already has padding:0), so that gutter is
   orphaned and every row bleeds 15px past the container on both sides, dragging
   its border-bottom over the panel edge. The clearfix goes for the same reason:
   in a flex container those pseudo-elements become zero-width FLEX ITEMS and
   each eats a `gap`. The label keeps its own padding zeroed because it is
   itself a .col-sm-* on 349 of 362 occurrences. */
.sm .form-horizontal .form-group { margin-left: 0; margin-right: 0; }
.sm .form-horizontal .form-group::before,
.sm .form-horizontal .form-group::after { display: none; }
.sm .form-horizontal .form-group > .control-label { padding-left: 0; padding-right: 0; }

/* Input groups. BS builds these as display:table with table-cell children, so a
   max-width on the CONTROL applies to a cell inside an auto table and tears the
   group apart — on .input-daterange the two date fields capped at 420px each
   while the table stayed 1088px wide, leaving a 105px hole before the "to"
   addon. The cap belongs on the GROUP, and is released where the group is not
   acting as a single form field (inside a table cell it must fill the cell;
   .full-size is the app's own opt-out). */
/* The base sheet already releases the 420px cap on controls INSIDE an input-group,
   which is what stopped .input-daterange tearing apart. A cap on the GROUP itself is
   deliberately NOT added: it would be a new constraint that never existed, and its
   intended opt-out cannot be expressed -- .full-size is always on the CONTROL in this
   app, never on the group, so `.input-group.full-size` matches nothing. */

/* 3 places put both classes on ONE element (suppliers, training-module
   materials, slideshow materials) — repeated "value + delete" rows.
   `.sm .form-group{display:flex}` overrides BS's display:table there and
   blockifies the cells: the trash-button cell collapses from a button to 11px,
   taking the button with it. These rows are list editors rather than a labelled
   field, so they also keep full width and drop the field divider. */
.sm .form-group.input-group {
    display: table;
    max-width: none;
    padding: 0;
    border-bottom: 0;
    margin-bottom: 10px;
}

/* border-COLOR, never the `border` shorthand: BS removes the border on the
   joining side (`.input-group-addon:first-child{border-right:0}`, 0,2,0) and a
   shorthand here would tie, win on order, and put a double rule down the middle
   of every group. The radius rules touch only the two OUTER corners, so a
   middle addon (the "to" in a date range) correctly stays square. */
.sm .input-group-addon {
    background-color: var(--pool-wash);
    border-color: var(--line);
    color: var(--ink-soft);
    font-family: var(--font);
    font-size: 14px;
    font-weight: 600;
    line-height: 1.2;
    padding: 0 12px;
}
.sm .input-group-addon:first-child { border-top-left-radius: var(--r); border-bottom-left-radius: var(--r); }
.sm .input-group-addon:last-child  { border-top-right-radius: var(--r); border-bottom-right-radius: var(--r); }

/* One chevron everywhere instead of each OS's own control, with room for a
   thumb. [multiple] and [size] are excluded: they are list boxes (the select2
   multi-pickers degrade to them before JS runs) and must keep their native
   height and scrollbar. */
.sm select.form-control:not([multiple]):not([size]) {
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    padding-right: 34px;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%234A5F66' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 12px 8px;
}
/* `.sm .form-control[disabled]` uses the `background` shorthand, which would
   blank the chevron; restate the fill as a longhand so both survive. */
.sm select.form-control:not([multiple]):not([size]):disabled { background-color: #EFF3F3; }

.sm select.form-control[multiple],
.sm select.form-control[size] { padding: 6px 8px; }

.sm textarea.form-control {
    padding: 10px 12px;
    line-height: 1.5;
    resize: vertical;          /* never horizontal: it escapes the field column */
}

/* flex-basis is the ONLY thing this adds — font-size, colour and margins are
   already set by `.sm .help-block, .sm-help` in staffmis-ui.css at the same
   (0,2,0), and restating them would leave a duplicate where a later edit to one
   silently does nothing. FormControl.vue, the shared control behind most Vue
   forms, emits a STACKED .form-group with `<p class="help-block">` carrying the
   validation error, and `.sm .form-group{display:flex}` turns that into a row,
   putting the message beside the field it explains. 100% wraps it onto its own
   line and is inert where the help block is not a flex item. */
.sm .help-block { flex-basis: 100%; line-height: 1.45; }

/* REGRESSION FIX. BS's `.has-error .form-control`, `.control-label` and
   `.help-block` are all (0,2,0), exactly tying the base rules in
   staffmis-ui.css — and that sheet loads last, so it won: border --line, help
   text --ink-faint, label plain ink. Every error signal was gone. Restored at
   (0,3,0) on the design's own status colours. .has-warning / .has-success are
   NOT defined: zero occurrences in the app, and .has-error is the only state
   FormControl.vue emits. The :focus form is (0,4,0) because
   `.sm .form-control:focus` (0,3,0) would otherwise revert a focused invalid
   field to the teal ring, reading as valid while it is being corrected. */
.sm .has-error .form-control { border-color: var(--overdue); box-shadow: inset 0 0 0 1px var(--overdue); }
.sm .has-error .control-label { color: var(--overdue); }
.sm .has-error .help-block { color: var(--overdue); font-weight: 600; }
.sm .has-error .input-group-addon { background-color: var(--overdue-wash); border-color: var(--overdue); color: var(--overdue); }
.sm .has-error .form-control:focus {
    border-color: var(--overdue);
    box-shadow: inset 0 0 0 1px var(--overdue), 0 0 0 3px rgba(168, 31, 22, .16);
}


/* =========================================================================
   HEADINGS — h1-h4
   BS gives these font-weight:500 and h2:30px / h3:24px / h4:18px, leaving a
   bare <h2> LARGER than a .page-title.

   NOT set, deliberately: font-family and color (BS already declares
   `h1..h6{font-family:inherit;color:inherit}`, so restating either would beat
   .text-success / .text-danger / .text-muted (0,1,0) on the headings that carry
   them and drop a real status colour); and line-height (.centre_log_navigation
   h4 / .week_navigation h4 (0,1,1) pin it to 34px to align with an input row).

   The :not() chains keep three populations disjoint so nothing depends on
   source order: .card-title / .qe-title (the Vue dashboard's own BS4
   typography), .page-title / .page_title (next block), and .panel-title /
   .list-group-item-heading (the APP's own heading classes). The last pair is
   load-bearing: these chains are (0,6,1) and (0,7,1) and beat
   `.sm .panel-title` (0,2,0) outright, so without the guard every
   <h3 class="panel-title"> takes margin:20px 0 8px inside a display:flex
   .panel-heading, inflating and de-centring the heading box on the File
   Library, Global Courses and Training Module screens.
   ========================================================================= */
/* :not() guards, not redefinitions: an element rule is (0,1,1) and would outrank the
   app's own (0,1,0) margin utilities where a heading carries one. */
.sm h1 { font-size: 28px; font-weight: 700; letter-spacing: -.015em; }
.sm h1:not(.mb-0):not(.mb-1):not(.mt-0):not(.mt-1) { margin: 24px 0 12px; }

.sm h2:not(.card-title):not(.qe-title):not(.page-title):not(.page_title):not(.panel-title) {
    font-size: 22px; font-weight: 700; letter-spacing: -.01em; margin: 22px 0 10px;
}
.sm h3:not(.card-title):not(.qe-title):not(.page-title):not(.page_title):not(.panel-title) {
    font-size: 18px; font-weight: 700; letter-spacing: -.01em; margin: 20px 0 8px;
}
/* h4 gets no margin and no line-height: five app rules position it inside
   layout (.rota-day-block, .centre_log_navigation, .week_navigation,
   .shift_availability_step2, .subtask-card), all (0,1,1) — a tie this sheet
   would win on source order, moving rota and navigation rows. */
.sm h4:not(.card-title):not(.qe-title):not(.page-title):not(.page_title):not(.panel-title):not(.list-group-item-heading) {
    font-size: 16px; font-weight: 700; letter-spacing: -.01em;
}

/* The <small> BS allows inside a heading — 65% and #777 by default. */
.sm h1 small, .sm h2 small, .sm h3 small, .sm h4 small,
.sm h1 .small, .sm h2 .small, .sm h3 .small, .sm h4 .small {
    color: var(--ink-faint);
    font-weight: 500;
}

/* basic_stylesheet.css:440 shrinks this to 14px/normal, making the heading on
   the Assets, Asset Groups, Projects and Recipients screens smaller than their
   own body text. Those <h1>s are page titles. (0,2,1) beats both
   `.page-header h1` (0,1,1) and `.sm h1` (0,1,1). */
.sm .page-header h1 {
    font-family: var(--font-narrow);
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -.015em;
    line-height: 1.15;
    margin: 0;
}

/* Page headers that carry action buttons (content_header()'s third argument). The
   member form is ~50 rows in a single <form>, so Save and Cancel ride with the title
   and stay reachable from every tab instead of sitting in a bar that scrolls away.

   min-height is not decoration. The tab strip below pins itself at top: 64px, and
   this is the measurement that number refers to; without it the strip would reserve
   space for a bar that no longer exists and leave a 64px void. */
.sm .page-header--actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
    min-height: var(--pagehead-h);
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--surface);
}
.sm .page-header--actions .sm-pagehead__actions { display: flex; gap: 10px; }

/* In a flex row the heading's own vertical margins add to the line rather than
   collapsing, which pushed this header to 85px and hid the tab strip pinned beneath
   it. The margins do nothing here: flex centring already positions the title. */
/* The :not() chain is not decoration, it is specificity arithmetic. The spacing utility
   rule `.sm h1:not(.mb-0):not(.mb-1):not(.mt-0):not(.mt-1)` is (0,5,1), so a plain
   `.sm .page-header--actions > h1` (0,2,1) loses to it and the margins stay. Matching
   the same chain makes this (0,6,1) and keeps the mt-/mb- utilities working.

   Note the sibling `margin: 0` in `.sm .page-header h1` above is (0,2,1) and is
   therefore inert for the same reason. It is left as it is: making it bite would
   change the spacing of every page header in the app, which is not this change. */
.sm .page-header--actions > h1:not(.mb-0):not(.mb-1):not(.mt-0):not(.mt-1) { margin: 0; }

/* The profile photo shares its cell with the file-name select. Left inline it renders
   beside that select; block puts it underneath, on the same left edge as every other
   control on the form. */
.sm #photo1 { display: block; margin-top: 8px; }

/* Page title — .page-title / .page_title (147 uses). It is the HEADING ITSELF,
   and on 14 screens it also carries grid classes (`page-title col-sm-6
   col-sm-pull-6`), so no padding, width or display is set here or a (0,2,0)
   rule would break the row. It is also a container — build_steps.php:149 emits
   the title, then an optional <small>, a .dropdown and pull-right buttons
   inside the same tag — so making it flex would take those floats out of flow.
   Archivo Narrow at 26px also buys width: these titles are tenant data
   ("Location Assessment Matrix — Billingham Forum"). */
.sm .page-title,
.sm .page_title {
    font-family: var(--font-narrow);
    font-weight: 700;
    line-height: 1.15;
}
/* Sized per level because the class names three roles: h2 is the page heading
   (132 uses), h3 is either the page heading or a section head, h4 is a summary
   line ("Total value: £X"). margin-top:0 is restated because these (0,2,1)
   rules outrank basic_stylesheet's `.page_title,.page-title` (0,1,0). */
.sm h2.page-title, .sm h2.page_title { font-size: 26px; letter-spacing: -.015em; margin-top: 0; }
.sm h3.page-title, .sm h3.page_title { font-size: 20px; letter-spacing: -.01em;  margin-top: 0; }
.sm h4.page-title, .sm h4.page_title { font-size: 16px; letter-spacing: -.01em;  margin-top: 0; }

/* Only where the view has not already chosen a gap: 23 page titles carry
   mb-0/mb-1/mb-2 at (0,1,0), including the mb-0 that keeps the File Library
   title level with the buttons beside it. A guard, not a redefinition. */
.sm h2.page-title:not([class*="mb-"]), .sm h2.page_title:not([class*="mb-"]) { margin-bottom: 18px; }
.sm h3.page-title:not([class*="mb-"]), .sm h3.page_title:not([class*="mb-"]) { margin-bottom: 14px; }

/* var(--font), not narrow, so it separates from the title it follows.
   build_steps.php:157 concatenates <small> straight onto the title with no
   whitespace, hence the margin. The .dropdown holds a 44px .btn, which drops
   the heading's baseline without an explicit alignment. */
.sm .page-title small, .sm .page_title small,
.sm .page-title .small, .sm .page_title .small {
    font-family: var(--font);
    font-size: 14px;
    font-weight: 500;
    color: var(--ink-faint);
    margin-left: 8px;
}
.sm .page-title .dropdown,
.sm .page_title .dropdown { vertical-align: middle; }

@media (max-width: 767px) {
    .sm h2.page-title, .sm h2.page_title { font-size: 22px; }
    .sm h3.page-title, .sm h3.page_title { font-size: 18px; }
    .sm .page-header h1 { font-size: 22px; }
}

.sm .text-muted { color: var(--ink-faint); }

/* CORRECTION to staffmis-ui.css: `.sm .table > thead > tr > th` (0,3,3) sets
   text-align:left, which outranks .text-right / .text-center (0,1,0) and is
   forcing numeric headers back to the left while their <td>s stay aligned —
   visible on the Login Report, Total Hours, Task Book feedback, Learning
   Records and the News Stories report. Only <th> is affected, which is what
   makes the mismatch obvious. */
.sm .table > thead > tr > th.text-right,
.sm-table th.text-right { text-align: right; }
.sm .table > thead > tr > th.text-center,
.sm-table th.text-center { text-align: center; }


/* =========================================================================
   STATUS LABELS — .label / .label-*
   Not decorative here: the label vocabulary IS the status vocabulary.
   task_compliance_matrix and location_assessment_matrix map it directly —
   default=Not set up, primary=Assigned, warning=Soon, danger=Expired,
   success=Completed. Styled as the sheet's .sm-chip so a table of thirty stays
   readable and the red still means something. info takes the pool wash with
   soft ink rather than a third grey: it renders directly beside primary in the
   Data Set and Packet Steps editors (Optional vs Required).
   ========================================================================= */
.sm .label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--font-narrow);
    font-weight: 700;
    font-size: 11px;
    letter-spacing: .09em;
    text-transform: uppercase;
    padding: 3px 9px;
    border-radius: 3px;
    white-space: nowrap;
    vertical-align: middle;
    /* TestResults.vue uses <label class="label label-success">; the bare `label`
       element rule contributes margin-bottom:5px, which pushes those chips off
       the line they sit on. Harmless on the <span> cases. */
    margin-bottom: 0;
}

.sm .label-default { background: var(--idle-wash);    color: var(--idle); }
.sm .label-primary { background: var(--pool-wash);    color: var(--pool); }
.sm .label-info    { background: var(--pool-wash);    color: var(--ink-soft); }
.sm .label-success { background: var(--done-wash);    color: var(--done); }
.sm .label-warning { background: var(--due-wash);     color: var(--due); }
.sm .label-danger  { background: var(--overdue-wash); color: var(--overdue); }

/* MUST FOLLOW the block above, and is (0,3,0) so it wins outright.
   basic_stylesheet.css:1525 stacks these labels inside the Standard Rota and
   Time Sheets day cards — `<span class="label label-default">` holding a
   "Location / Department / Job Role" string in a card capped at 400px.
   `.sm .label` alone flips them to inline-flex (stacked shift labels collapse
   onto one line), nowrap (long strings overflow the card) and margin-bottom:0
   (the 4px separation between stacked labels is lost). */
.sm .list-group-rota .label {
    display: block;
    white-space: normal;
    margin-top: 8px;
    margin-bottom: 4px;
}

/* BS's `.label:empty` is (0,2,0) and ties `.sm .label`; this sheet loads last,
   so without restating it every empty label renders as a bare box. */
.sm .label:empty { display: none; }

/* a.label gets color:#fff on hover from BS — white on a wash is invisible. */
.sm a.label:hover,
.sm a.label:focus { color: inherit; text-decoration: none; }


/* =========================================================================
   COUNT BADGES — .badge
   A count, never a status: tab counts, the Valid / Due to expire / Expired
   tallies on the qualifications screens, and the sidebar unread counter. Kept
   neutral because the word next to it carries the state, and
   .qualifications-label .badge currently paints all three tallies the same pink
   (basic_stylesheet.css:2256), which reads as a status signal while encoding
   nothing. .qe-badge is excluded: the Vue dashboard's own row-count chip.

   WHY .communications-badge IS NOT ADDED TO THE :not() CHAIN: this selector is
   (0,3,0), and a second :not() would lift it to (0,4,0) — which would then beat
   `.sm .nav-tabs > li > a > .badge` (0,3,2) and repaint every tab count badge.
   The sidebar counter is restated below instead.
   ========================================================================= */
.sm .badge:not(.qe-badge) {
    background-color: var(--idle-wash);
    color: var(--ink-soft);
    font-family: var(--font-narrow);
    font-weight: 700;
    font-size: 11px;
    min-width: 20px;
    padding: 3px 7px;
    border-radius: 9px;
    vertical-align: middle;
}

/* MUST FOLLOW the rule above — a lost status signal, not a cosmetic detail.
   `.sm .sidebar-menu__link .badge` (staffmis-ui.css:640) is ALSO (0,3,0), so it
   TIES the rule above and loses on source order, turning the unread-
   communications counter (app-ui.php:161, `class="badge communications-badge"`)
   from red-on-white into --idle-wash with --ink-soft text on the dark --ink
   rail — near-invisible. Only the two clobbered properties are restated;
   everything else in the shipped sidebar rules still applies. */
.sm .sidebar-menu__link .badge,
.sm .sidebar-menu__link .communications-badge {
    background-color: var(--overdue);
    color: #fff;
}

/* Same (0,2,0) tie as .label:empty — without this, empty badges become dots. */
.sm .badge:empty { display: none; }

/* basic_stylesheet.css:1770 flips the badge to #005CA8 (the retired brand blue)
   on tab hover, at (0,4,2) — the training-control tabs are the main badge
   screen, so it is the most visible surviving instance of the old palette.
   ONE rule for all nav variants: every tab and pill strip in the app carries
   `nav` alongside `nav-tabs`/`nav-pills`, so per-variant copies at the same
   (0,5,2) would only shadow each other. This also covers .nav-sequence. */
.sm .nav > li:not(.active) > a:hover > .badge {
    background-color: var(--pool-wash);
    color: var(--pool);
}


/* =========================================================================
   PAGINATION — Communications, Asset Tasks, the centre-log paginator. BS gives
   these 6px/12px padding (a ~34px target) with #005ca8 blue and a #fa8a07
   orange hover, both retired. Sized to --tap: paging is a repeated,
   mis-hit-prone action done one-handed on a wet tablet. No margin is set — all
   four usages carry `mt-0 mb-0` at (0,1,0). The .active and .disabled forms
   list :hover/:focus because BS states them that way at (0,3,1), so the plain
   form alone would be a source-order tie.
   ========================================================================= */
.sm .pagination > li > a,
.sm .pagination > li > span {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--tap);
    min-width: var(--tap);
    padding: 0 14px;
    color: var(--pool);
    background-color: var(--card);
    border-color: var(--line);
    font-weight: 600;
}
.sm .pagination > li > a:hover,
.sm .pagination > li > a:focus {
    color: var(--pool-deep);
    background-color: var(--pool-wash);
    border-color: #B9D2D3;
}
.sm .pagination > .active > a,
.sm .pagination > .active > span,
.sm .pagination > .active > a:hover,
.sm .pagination > .active > span:hover,
.sm .pagination > .active > a:focus,
.sm .pagination > .active > span:focus {
    background-color: var(--pool);
    border-color: var(--pool);
    color: #fff;
}
.sm .pagination > .disabled > a,
.sm .pagination > .disabled > span,
.sm .pagination > .disabled > a:hover,
.sm .pagination > .disabled > span:hover,
.sm .pagination > .disabled > a:focus,
.sm .pagination > .disabled > span:focus {
    color: var(--ink-faint);
    background-color: var(--surface);
    border-color: var(--line);
}
.sm .pagination > li:first-child > a,
.sm .pagination > li:first-child > span { border-top-left-radius: var(--r); border-bottom-left-radius: var(--r); }
.sm .pagination > li:last-child > a,
.sm .pagination > li:last-child > span { border-top-right-radius: var(--r); border-bottom-right-radius: var(--r); }


/* =========================================================================
   DROPDOWNS — the app's action menu (~34 toggles). Two things are fixed beyond
   colour: items were 30px tall (the same wet-hands argument as pagination), and
   the menu gets a scroll cap, because the shift-name pickers list every shift
   name on the site and a BS3 dropdown has no max-height, so a long list runs
   off the bottom of the screen. Safe to clip — there is no .dropdown-submenu
   anywhere in the app.
   ========================================================================= */
.sm .dropdown-menu {
    background-color: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--r);
    box-shadow: var(--shadow);
    padding: 6px 0;
    max-height: 60vh;
    overflow-y: auto;
}
/* (0,3,1), to clear `.dropdown-menu > li > a` at (0,2,1). The hover BS ships is
   #fee8cf, the old orange wash. */
.sm .dropdown-menu > li > a {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: var(--tap);
    padding: 8px 18px;
    color: var(--ink);
    font-size: 14px;
    line-height: 1.35;
}
.sm .dropdown-menu > li > a:hover,
.sm .dropdown-menu > li > a:focus {
    background-color: var(--pool-wash);
    color: var(--pool-deep);
    text-decoration: none;
}

/* Both used by the rota re-assign menus. BS paints the divider #004075 —
   retired brand navy. */
.sm .dropdown-menu .divider { background-color: var(--line-soft); margin: 6px 0; }
.sm .dropdown-header {
    font-family: var(--font-narrow);
    font-weight: 700;
    font-size: 11px;
    letter-spacing: .11em;
    text-transform: uppercase;
    color: var(--ink-faint);
    padding: 8px 18px 4px;
}

/* While a menu is open BS turns .btn-default's text #f58a1b with a #ca6b09
   border and .btn-primary #004075 — the retired orange and navy, on every
   dropdown in the app, in the one state a user is guaranteed to be looking at.
   (0,4,0) clears the original (0,3,0). */
.sm .open > .dropdown-toggle.btn-default {
    background-color: var(--pool-wash);
    border-color: #B9D2D3;
    color: var(--ink);
}
.sm .open > .dropdown-toggle.btn-primary {
    background-color: var(--pool-deep);
    border-color: var(--pool-deep);
    color: #fff;
}

/* A 4px triangle is close to invisible on a glossy tablet in daylight, and on
   several toggles it is the button's only content. Scaled rather than given
   thicker borders: .dropup .caret rebuilds the triangle out of border
   longhands, and a border rule here would tie at (0,2,0), win on source order,
   and leave that caret pointing both ways. */
.sm .caret { transform: scale(1.25); }

/* ======================================================================
   Full-page loader — the first thing anyone sees on every page load.

   app-ui.css:45-55 leaves it as a cog spinner in #005CA8, pinned 2em from the
   top of an otherwise empty white page. That blue belongs to no palette we use
   any more, and because `body.loading #page-wrapper{display:none}` hides
   everything else, it IS the whole screen for as long as the page takes.

   Specificity here is fiddly and worth stating exactly. app-ui.css has TWO rules:
   `#page-loader` (1,0,0) and `body.loading #page-loader` (1,1,1). The second is
   the one that actually paints the loader while it is on screen, and a plain
   `.sm #page-loader` (1,1,0) LOSES to it -- display stayed `block` and the flex
   centring silently did nothing, while every other property applied, which is
   what made it look half-broken rather than unstyled.
   `body.sm.loading #page-loader` is (1,2,1) and wins outright.
   ====================================================================== */
/* Appearance only. `display` is deliberately NOT set here: app-ui.css:5 hides the
   loader with `#page-loader{display:none}` once the body loses .loading, and a
   `display` on this selector (1,1,0) beats that (1,0,0) -- which leaves a
   full-viewport overlay sitting on top of the loaded page. */
.sm #page-loader {
    position: fixed;
    inset: 0;
    margin: 0;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;

    background: var(--surface);
    color: var(--ink-soft);
    font-family: var(--font);
    font-size: 15px;
    font-weight: 500;
    line-height: 1.4;
    letter-spacing: .01em;
}

/* display lives ONLY on the .loading state, mirroring app-ui.css:61 so the loader
   shows while loading and is hidden by the base rule the moment that class goes. */
body.sm.loading #page-loader { display: flex; }

/* Smaller and calmer than the old 2em cog. A large spinning gear reads as work
   being done TO you; a quiet mark reads as waiting, which is what is happening. */
.sm #page-loader i.fa {
    font-size: 26px;
    color: var(--pool);
}

/* The markup is `<i>` + <br> + bare text, so the text cannot be targeted on its
   own -- the <br> is neutralised instead and flex handles the spacing. */
.sm #page-loader br { display: none; }

/* Respect a reduced-motion preference: keep the mark, drop the spin. The sheet
   already does this globally, but #page-loader renders before much else and the
   icon is the only moving thing on screen. */
@media (prefers-reduced-motion: reduce) {
    .sm #page-loader i.fa { animation: none; }
}

/* ======================================================================
   Two-column forms on wide screens.

   Most rows hold ONE short control (34 of 41 on the Profile form), so a
   full-width row leaves the right half empty and doubles the scrolling. Pairing
   them halves the page without touching any markup.

   inline-flex rather than a flex parent: every .form-group on that form shares an
   UNCLASSED parent div, so there is nothing to target to make it a flex or grid
   container. Inline-level children flow and wrap on their own, which needs no
   parent rule and no clearfix.

   Rows that hold something bulky stay full width -- a textarea, a table, a
   checkbox or radio list, a file input, or the multi-select groups. :has() carries
   that test. It is CSS Selectors 4, so on an older Android WebView the whole rule
   is dropped and every row stays full width: the layout degrades to exactly what
   it does today rather than breaking, which is why it is safe to use here.

   Only above 1100px. Below that a half-width column cannot hold a 260px label and
   a usable field side by side, so single column remains correct.
   ====================================================================== */
@media (min-width: 1100px) {
    /* :not(.row) for the reason given in bs3-compat.css: a form-group that is also a
       grid row is doing layout, not holding a field, and pairing it would squeeze a
       full-width row to half. */
    .sm .form-group:not(.row) {
        display: inline-flex;
        /* Half the line MINUS half the gutter. 49.5% used to leave a sliver between
           the columns only by accident: the field's 420px cap happened not to fill
           its half. Once the panel gained side padding the two became exactly equal
           and the left column's input ended precisely where the right column's label
           began. This reserves the gutter instead of hoping for it. */
        width: calc(50% - 12px);
        vertical-align: top;
    }

    /* Inside a tab pane the parent is ours, so the gutter can be a real gap. It has to
       be: the tabs script moves rows with appendChild, which leaves the whitespace
       text nodes behind, and whitespace was the only thing separating inline-level
       boxes. gap applies BETWEEN items only, so the right column still ends flush
       with the panel's padding. */
    .sm .sm-formtabs__pane {
        display: flex;
        flex-wrap: wrap;
        column-gap: 24px;
    }

    /* A narrower label in a half-width column; the field needs the room more. */
    .sm .form-group:not(.row) > .control-label { flex-basis: 170px; max-width: 170px; }

    /* A row with NO form control is not a field and must not be paired. Two real cases
       on the Profile form, both of which looked wrong when pulled into a column:
       the Ok/Cancel action row, which ended up sitting beside an unrelated field, and
       a label-only row like "Photo" whose control lives in the NEXT form-group, so
       pairing split the label away from the thing it labels.

       The test has to ignore inputs that are not data entry: the app's action rows use
       `<input type="submit">` for Ok, and hidden inputs are scattered through these
       forms, so a plain :has(input) counts both as fields and pairs the row anyway. */
    .sm .form-group:not(:has(input:not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="reset"]))):not(:has(select)):not(:has(textarea)) {
        display: flex;
        width: 100%;
    }

    /* Anything bulky keeps the full width. */
    .sm .form-group:has(textarea),
    .sm .form-group:has(table),
    .sm .form-group:has(.checkbox),
    .sm .form-group:has(.radio),
    .sm .form-group:has(input[type="file"]),
    .sm .form-group:has(ul),
    .sm .form-group:has(select + select) {
        display: flex;
        width: 100%;
    }
    /* Full-width rows deliberately keep the SAME 170px label column as the paired rows.
       Widening them to 260px pushed their field 90px further right than every other
       field on the form -- the Notes textarea started at x=504 while Staff No, Mobile
       and Age all started at x=414. Two label widths on one form is worse than a
       slightly narrow label: a single left edge down the whole column is the thing that
       makes it read as aligned. */

    /* Modal bodies are narrow: their rows are stacked deliberately elsewhere in this
       sheet and must not be pulled into two columns. */
    .sm .modal-body .form-group { display: block; width: 100%; }
}

/* ======================================================================
   The task editor: New / Edit Task (modify_packet(), form.sm-task-editor).

   One column at every width. The two-column pairing above is for forms of
   short fields; this one is a sequence of sections (Details, Schedule,
   Content, Options) whose rows belong in order, and pairing sat the
   description editor beside "Hide until Task Time" and Complete by beside
   its notify list. The rules that were meant to keep a textarea or a
   multi-select full width lose to the pairing rule's :not(.row) weight, so
   this form opts out as a whole, the way .sm-taskboard__filters does.

   Only the look changes: no field, name or value is added or moved out of
   the form.
   ====================================================================== */
.sm .sm-task-editor .form-group:not(.row) {
    display: flex;
    width: 100%;
}

/* A wide label column, one left edge for every field (above phone widths, where the
   label stacks over its field as elsewhere). */
@media (min-width: 901px) {
    .sm .sm-task-editor .form-group:not(.row) > .control-label {
        flex-basis: 220px;
        max-width: 220px;
    }
    /* a row with no label (Send email alert) lines up with the fields */
    .sm .sm-task-editor .form-group:not(.row) > .col-sm-12:only-child { margin-left: 230px; }
}

.sm .sm-task-editor__section {
    margin: 22px 0 10px;
}

/* The asset rows: meant to stay hidden until an asset is linked (basic_stylesheet.css
   .assets-dropdown-container-hide / .no-assets-message-hide), but the form-group rules
   above out-rank those, so an empty Asset select and "no assets yet" showed on every
   new task. The id is for weight over the no-control rule; the -show classes still show. */
.sm #packet-form.sm-task-editor .form-group.assets-dropdown-container-hide,
.sm #packet-form.sm-task-editor .form-group.no-assets-message-hide {
    display: none;
}

/* The description: the plain textarea (users without the rich editor) the width of its
   column, like the rich editor, instead of its 80 columns running past a narrower one. */
.sm .sm-task-editor #packet-content-plain {
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
}

/* Complete by: its choice and the value it needs on one line. The script shows and hides
   the value fields with inline display, which this leaves alone. */
.sm .sm-task-editor .form-control[name^="packet_schedule_complete_by"] {
    display: inline-block;
    width: auto;
    vertical-align: middle;
}

/* the date picker at the width of a date */
.sm .sm-task-editor .input-group.date { max-width: 260px; }

/* Task Group is marked full-size; here it keeps the width of the fields around it */
.sm .sm-task-editor select.full-size { max-width: 420px; }

/* Attachments: the editor's list with its Add button under it, left-aligned like the other
   fields' buttons; and that button a secondary one (it is blue in the editor bundle), so
   Send is the page's only primary action. */
.sm .sm-task-editor__attachments .row { margin: 0; }
.sm .sm-task-editor__attachments .row > [class*="col-sm-"] {
    flex: 0 0 100%;
    max-width: 100%;
    width: 100%;
    padding: 0;
}
/* The button's column, left-aligned: it carries .text-right, which a flex item's own
   placement does not follow. Not the list's column, whose files are floated tiles side by
   side, which a flex container would stack one per line. */
.sm .sm-task-editor__attachments .row > [class*="col-sm-"]:not(.attachment-list) {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}
.sm .sm-task-editor__attachments .btn-primary {
    background: var(--card);
    border-color: var(--line);
    color: var(--ink);
    --bs-btn-active-bg: var(--pool-wash);
    --bs-btn-active-border-color: var(--line);
    --bs-btn-active-color: var(--ink);
}
.sm .sm-task-editor__attachments .btn-primary:hover,
.sm .sm-task-editor__attachments .btn-primary:focus {
    background: var(--pool-wash);
    border-color: var(--line);
    color: var(--ink);
}

/* the actions, set off from the form */
.sm .sm-task-editor__actions {
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid var(--line);
}
/* Save Template Changes and its menu arrow: pressed or open, the secondary look (SmartAdmin
   paints an open button in its own purple), and one arrow: the caret span, which both the
   Bootstrap 5 and Bootstrap 3 set-ups draw, not Bootstrap 5's own as well. */
.sm .sm-task-editor__actions .btn.btn-default {
    --bs-btn-active-bg: var(--pool-wash);
    --bs-btn-active-border-color: var(--line);
    --bs-btn-active-color: var(--ink);
}
.sm .sm-task-editor__actions .dropdown-toggle::after { display: none; }

/* ======================================================================
   Tabbed member form (see public/js/member_form_tabs.js).

   The Modify User form is ~50 rows in ONE <form>. The script groups the rows into
   panes; this styles the shell and pins the title and actions so Ok/Cancel stay
   reachable from every tab rather than only from the bottom of whichever pane
   happens to be open.
   ====================================================================== */
.sm .sm-formtabs { margin-top: 4px; }

/* Actions ride with the header. content_header() emits them as a .form-group, so
   the form-row styling has to be undone here or they inherit a separator line and
   the two-column pairing. */
/* DORMANT since the member form moved its actions into .page-header--actions: nothing
   creates a .sm-formtabs__actions element any more. Kept because member_form_tabs.js
   still has the matching guard, so a form that does emit a button-bar row would style
   correctly. Delete this and that guard together or neither. */
.sm .sm-formtabs__actions {
    position: sticky;
    top: 0;
    z-index: 5;
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    width: 100%;
    padding: 10px 0;
    margin: 0;
    background: var(--surface);
    border-bottom: 1px solid var(--line);
}
.sm .sm-formtabs__actions > [class*="col-"] { padding: 0; width: auto; }

/* The nav sits directly under those actions and stays with them. */
.sm .sm-formtabs__nav {
    position: sticky;
    top: var(--pagehead-h);
    z-index: 4;
    /* The panel is white and the title bar above is the tinted band, so the strip
       takes the PANEL's colour: tinting it too would read as a second header. It
       still needs an opaque background of its own, because rows scroll under it. */
    background: var(--card);
    margin-bottom: 0;
    padding-top: 2px;
}

/* The gap under the tab strip belongs on the PANE, not on its first row. Above
   1100px rows pair up two to a line, and :first-child is only the LEFT one, so
   padding it pushed that row's label and field 5px below its neighbour's -- two
   fields sitting side by side and visibly out of line. Padding the pane spaces
   the whole first line at once and keeps every pair level. */
.sm .sm-formtabs__pane { padding-top: 12px; }

@media (max-width: 900px) {
    /* Sticky chrome costs too much height on a phone; let it scroll away. */
    .sm .sm-formtabs__actions,
    .sm .sm-formtabs__nav { position: static; }
}

/* ======================================================================
   Searchable table (see public/js/site_list_table.js).

   Built for the admin Sites List, which lists every active site on the brand -- several
   hundred rows on a real installation -- and used to render as one unbroken column of
   links. The chrome here is deliberately generic (data-sm-tablekit) so any other long
   table can opt in without new CSS.
   ====================================================================== */
.sm .sm-tablekit__title {
    font-family: var(--font-narrow);
    font-weight: 700;
    font-size: 13px;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin-bottom: 8px;
}

.sm .sm-tablekit__bar {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}
.sm .sm-tablekit__search { width: 100%; max-width: 320px; }
.sm .sm-tablekit__count { color: var(--ink-soft); font-size: 13px; }

/* Wide content scrolls inside its own box; the page itself must never scroll sideways. */
.sm .sm-tablekit__scroll { overflow-x: auto; }

.sm .sm-tablekit__muted { color: var(--ink-soft); }
.sm .sm-tablekit__actions-col { width: 1%; white-space: nowrap; text-align: right; }

.sm .sm-tablekit__sortable { cursor: pointer; user-select: none; }
.sm .sm-tablekit__sortable:hover { color: var(--pool); }
/* The caret is generated, so a column with no sort applied shows nothing and the header
   does not jump when one is. */
.sm .sm-tablekit__sortable::after {
    content: "";
    display: inline-block;
    width: 12px;
    opacity: .45;
    font-size: 11px;
}
.sm .sm-tablekit__sortable[aria-sort="ascending"]::after { content: " \2191"; opacity: 1; }
.sm .sm-tablekit__sortable[aria-sort="descending"]::after { content: " \2193"; opacity: 1; }

.sm .sm-tablekit__empty { color: var(--ink-soft); padding: 14px 2px; margin: 0; }

/* Bootstrap 3 predates the global [hidden] rule, and the script toggles that attribute. */
.sm .sm-tablekit [hidden] { display: none; }

/* ======================================================================
   The content panel (#main-container) and its title bar.

   #main-container is the card content_header() wraps every screen in. The legacy
   sheet gives it a border, an 8px radius and a flat 10px of padding, and no
   background at all -- so the page showed through, the title sat in the same tone as
   the fields below it, and text ran almost to the panel's edge.

   Here the panel becomes a real card: white, with the heading as a tinted bar across
   the top and comfortable room down both sides. The bar is the SAME element
   content_header() already emits, bled out to the panel edge with a negative margin
   that cancels the container's own padding, so no markup changed.

   Scoped to `#main-container >` deliberately: a .page-header rendered anywhere else
   is left exactly as it was.
   ====================================================================== */
.sm #main-container {
    background: var(--card);
    border-color: var(--line);
    padding: 20px 24px;
}

.sm #main-container > .page-header {
    display: flex;
    align-items: center;
    /* The height comes from min-height alone, with NO vertical padding, so the bar is
       exactly --pagehead-h tall (box-sizing is border-box, so the border is inside
       that). The tab strip pins itself at the same variable, so the two cannot drift
       apart the way they did when padding decided the height. */
    min-height: var(--pagehead-h);
    /* -24px cancels the container's horizontal padding, so the bar spans the full
       panel while its text stays on the same left edge as the content below. */
    margin: 0 -24px 20px;
    padding: 0 24px;
    background: var(--surface);
    border-bottom: 1px solid var(--line);
    /* Matches the panel's own radius. The container cannot use overflow:hidden to
       clip this, because that would make it a scroll container and silently kill
       position:sticky on the header. */
    border-radius: 8px 8px 0 0;
}

/* Only a header that OPENS the panel bleeds to its top edge. Two of the views that
   emit #main-container directly (assets/show, assets/asset_tasks) start with a tab
   strip instead, and with no top padding those would have sat flush against the
   panel's border. The container keeps its top padding; a leading header cancels it. */
.sm #main-container > .page-header:first-child { margin-top: -20px; }

/* Same specificity problem as the --actions heading: the spacing utility rule
   `.sm h1:not(.mb-0)...` is (0,5,1), so the margin reset has to out-rank it. The id
   here makes this (1,6,1). Without it the heading's margins push the bar taller than
   --pagehead-h and the tab strip hides behind it. */
.sm #main-container > .page-header > h1:not(.mb-0):not(.mb-1):not(.mt-0):not(.mt-1) { margin: 0; }

.sm #main-container > .page-header--actions {
    /* The sticky bar keeps the panel's own background, not the page's, or the rows
       scrolling underneath would show through it. */
    background: var(--surface);
}

@media (max-width: 900px) {
    .sm #main-container { padding: 14px; }
    .sm #main-container > .page-header { margin: 0 -14px 14px; padding: 0 14px; }
    .sm #main-container > .page-header:first-child { margin-top: -14px; }
}

/* ======================================================================
   Data table controls (see public/js/sm-datatable.js): pager, column picker
   and export. These sit alongside the search and count already defined above.
   ====================================================================== */
.sm .sm-tablekit__spacer { flex: 1 1 auto; }

/* Column picker. A <details> is used rather than a scripted dropdown so it
   opens, closes and is reachable by keyboard with no JavaScript of its own. */
.sm .sm-tablekit__colmenu { position: relative; }
.sm .sm-tablekit__colmenu > summary {
    cursor: pointer;
    list-style: none;
    padding: 6px 12px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--card);
    font-size: 13px;
    user-select: none;
}
.sm .sm-tablekit__colmenu > summary::-webkit-details-marker { display: none; }
.sm .sm-tablekit__cols {
    position: absolute;
    z-index: 20;
    top: calc(100% + 4px);
    left: 0;
    min-width: 190px;
    max-height: 320px;
    overflow-y: auto;
    padding: 8px;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--r);
    box-shadow: var(--shadow);
}
.sm .sm-tablekit__col {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 2px;
    font-weight: 400;
    margin: 0;
    white-space: nowrap;
}

/* Pager. Rendered only when the filtered set is longer than one page, and
   windowed around the current page so a 300-page table does not draw 300
   buttons. */
.sm .sm-tablekit__pager {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
    margin-top: 12px;
}
.sm .sm-tablekit__page {
    min-width: 34px;
    min-height: 34px;
    padding: 0 10px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--card);
    color: var(--ink);
    font-size: 13px;
    cursor: pointer;
}
.sm .sm-tablekit__page:hover:not(:disabled) { background: var(--surface); }
.sm .sm-tablekit__page:disabled { opacity: .45; cursor: default; }
.sm .sm-tablekit__page.is-current {
    background: var(--pool);
    border-color: var(--pool);
    color: #fff;
    font-weight: 600;
}
.sm .sm-tablekit__gap { padding: 0 2px; color: var(--ink-faint); }

/* Low-priority columns fold away on a narrow screen rather than forcing the
   table into a horizontal scroll the user has to discover. */
@media (max-width: 900px) {
    .sm [data-sm-priority="low"] { display: none !important; }
}

/* ======================================================================
   Select2 (3.5) -- the dropdowns.

   sm-select2.js makes every plain <select> a Select2 box, and marks every other
   Select2 on the page (outside the rota grids) the same way: .select2-sm on both the
   box (.select2-container) and its list (.select2-drop). The select2- prefix is
   load-bearing: Select2 rewrites both elements' classes whenever the select changes,
   keeping only its own select2-* classes and the select's.

   The box copies the select's classes, so a .form-control select's box is a
   .form-control too. The field's border, padding and height come off that wrapper
   here and go on the .select2-choice inside it, where they match the other fields:
   44px, the app's border and radius, the same chevron as a native select.
   The list is appended to <body>, which carries .sm, so it reads the same tokens and
   follows dark mode, high contrast and big text with no rules of its own.
   ====================================================================== */
.sm .select2-container.select2-sm {
    display: inline-block;
    vertical-align: middle;
    max-width: 100%;
    font-family: var(--font);
    font-size: var(--base-font, 14px);
    color: var(--ink);
}
.sm .select2-container.select2-sm.form-control {
    display: block;
    width: 100%;
    max-width: 260px;
    min-height: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
}
/* the whole width where a select would have it (see .full-size / .input-group above) */
.sm .select2-container.select2-sm.form-control.full-size,
.sm .input-group .select2-container.select2-sm.form-control { max-width: none; }

/* The box. An <a>: .sm a would colour it as a link and underline it on hover, and
   app-ui.css sets every <a> to 14px, which would ignore the big-text setting. */
.sm .select2-container.select2-sm .select2-choice {
    font-family: inherit;
    font-size: inherit;
    display: flex;
    align-items: center;
    height: auto;
    min-height: var(--tap);
    padding: 6px 34px 6px 12px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--card);
    color: var(--ink);
    line-height: 1.4;
    text-decoration: none;
    box-shadow: none;
}
.sm .select2-container.select2-sm .select2-choice > .select2-chosen {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    padding: 0;
    line-height: inherit;
}
.sm .select2-container.select2-sm.select2-allowclear .select2-choice { padding-right: 56px; }
.sm .select2-container.select2-sm.select2-allowclear .select2-choice > .select2-chosen { margin-right: 0; }

/* In a table cell: tighter, so a row of them (HR Setup's seven "Days Open") fits the
   width a row of selects did. */
.sm td .select2-container.select2-sm .select2-choice,
.sm th .select2-container.select2-sm .select2-choice { padding-left: 8px; padding-right: 26px; }
.sm td .select2-container.select2-sm.select2-allowclear .select2-choice,
.sm th .select2-container.select2-sm.select2-allowclear .select2-choice { padding-right: 46px; }
.sm td .select2-container.select2-sm .select2-choice::after,
.sm th .select2-container.select2-sm .select2-choice::after { right: 8px; }
.sm td .select2-container.select2-sm .select2-choice abbr,
.sm th .select2-container.select2-sm .select2-choice abbr { right: 24px; }

/* The chevron: the native select's, drawn in the text colour so it follows the theme. */
.sm .select2-container.select2-sm .select2-choice .select2-arrow { display: none; }
.sm .select2-container.select2-sm .select2-choice::after {
    content: "";
    position: absolute;
    top: 50%;
    right: 12px;
    width: 12px;
    height: 8px;
    margin-top: -4px;
    background: var(--ink-soft);
    -webkit-mask: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E") no-repeat center / 12px 8px;
    mask: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E") no-repeat center / 12px 8px;
    pointer-events: none;
    transition: transform .15s ease;
}
.sm .select2-container.select2-sm.select2-dropdown-open .select2-choice::after { transform: rotate(180deg); }

/* The clear cross (a blank first option: "nothing chosen"), in the text colour. */
.sm .select2-container.select2-sm .select2-choice abbr {
    top: 50%;
    right: 32px;
    width: 18px;
    height: 18px;
    margin-top: -9px;
    border-radius: 50%;
    background: transparent;
}
.sm .select2-container.select2-sm .select2-choice abbr::before {
    content: "";
    position: absolute;
    inset: 4px;
    background: var(--ink-faint);
    -webkit-mask: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'%3E%3Cpath stroke='%23000' stroke-width='1.6' stroke-linecap='round' d='M1 1l8 8M9 1 1 9'/%3E%3C/svg%3E") no-repeat center / 10px 10px;
    mask: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'%3E%3Cpath stroke='%23000' stroke-width='1.6' stroke-linecap='round' d='M1 1l8 8M9 1 1 9'/%3E%3C/svg%3E") no-repeat center / 10px 10px;
}
.sm .select2-container.select2-sm .select2-choice abbr:hover { background: var(--line-soft); }
.sm .select2-container.select2-sm .select2-choice abbr:hover::before { background: var(--ink); }

/* Focused and open: the field focus of the other controls. The ring is for the
   keyboard only, as a select's is (sm-select2.js sets data-sm-keyboard). */
.sm .select2-container.select2-sm.select2-container-active .select2-choice,
.sm .select2-container.select2-sm.select2-dropdown-open .select2-choice,
.sm .select2-container-multi.select2-sm.select2-container-active .select2-choices {
    border-color: var(--pool);
    box-shadow: 0 0 0 3px rgba(11, 110, 117, .12);
}
[data-sm-keyboard] .sm .select2-container.select2-sm.select2-container-active:not(.select2-dropdown-open) .select2-choice,
[data-sm-keyboard] .sm .select2-container-multi.select2-sm.select2-container-active .select2-choices {
    outline: 3px solid var(--pool);
    outline-offset: 2px;
}
.sm .select2-container.select2-sm.select2-dropdown-open .select2-choice {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
    background: var(--card);
}
.sm .select2-container.select2-sm.select2-dropdown-open.select2-drop-above .select2-choice {
    border-radius: 0 0 var(--r) var(--r);
}

/* Unavailable reads as unavailable, as a disabled field does. */
.sm .select2-container.select2-sm.select2-container-disabled .select2-choice,
.sm .select2-container-multi.select2-sm.select2-container-disabled .select2-choices {
    background: var(--line-soft);
    color: var(--ink-faint);
    border-color: var(--line);
    cursor: not-allowed;
}
/* High contrast turns --line-soft into a rule colour (#767676): the native disabled
   field's own fill there, or the text would sit at about 2.5:1. */
.set-high-contrast-mode .sm .select2-container.select2-sm.select2-container-disabled .select2-choice,
.set-high-contrast-mode .sm .select2-container-multi.select2-sm.select2-container-disabled .select2-choices {
    background: #EFF3F3;
}

/* A field the form rejected. */
.sm .has-error .select2-container.select2-sm .select2-choice,
.sm .has-error .select2-container.select2-sm .select2-choices,
.sm .select2-container.select2-sm.select2-sm-invalid .select2-choice,
.sm .select2-container.select2-sm.select2-sm-invalid .select2-choices { border-color: var(--overdue); }

/* ---- The list ---- */
.sm .select2-drop.select2-sm {
    margin-top: -1px;
    border: 1px solid var(--pool);
    border-top: 0;
    border-radius: 0 0 var(--r) var(--r);
    background: var(--card);
    color: var(--ink);
    box-shadow: var(--shadow);
    font-family: var(--font);
    font-size: var(--base-font, 14px);
}
.sm .select2-drop.select2-sm.select2-drop-above {
    margin-top: 1px;
    border-top: 1px solid var(--pool);
    border-bottom: 0;
    border-radius: var(--r) var(--r) 0 0;
}
/* A list wider than its box (one in a table cell): an edge where it runs past the box. */
.sm .select2-drop.select2-sm.select2-drop-auto-width {
    border-top: 1px solid var(--pool);
    border-top-right-radius: var(--r);
}
.sm .select2-drop.select2-sm.select2-drop-auto-width.select2-drop-above {
    border-bottom: 1px solid var(--pool);
    border-bottom-right-radius: var(--r);
}
.sm .select2-drop.select2-sm .select2-search { padding: 8px 8px 0; }
/* 16px: a smaller one makes iOS zoom the page on focus */
.sm .select2-drop.select2-sm .select2-search input {
    min-height: 40px;
    padding: 8px 32px 8px 10px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--card) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14'%3E%3Ccircle cx='6' cy='6' r='4.5' fill='none' stroke='%236B7F85' stroke-width='1.6'/%3E%3Cpath stroke='%236B7F85' stroke-width='1.6' stroke-linecap='round' d='m9.5 9.5 3 3'/%3E%3C/svg%3E") no-repeat right 10px center / 14px 14px;
    color: var(--ink);
    font-family: var(--font);
    font-size: 16px;
    box-shadow: none;
}
.sm .select2-drop.select2-sm .select2-search input:focus { border-color: var(--pool); outline: none; }
.sm .select2-drop.select2-sm .select2-search input.select2-active {
    background-image: url("/js/select2/select2-spinner.gif");
    background-size: 16px 16px;
}
.sm .select2-drop.select2-sm .select2-results {
    max-height: 300px;
    margin: 6px 0;
    padding: 0 6px;
}
.sm .select2-drop.select2-sm .select2-results .select2-result-label {
    padding: 8px 10px;
    border-radius: 4px;
    line-height: 1.35;
}
/* the entry under the pointer or the arrow keys (on opening, the current choice) */
.sm .select2-drop.select2-sm .select2-results .select2-highlighted {
    background: var(--pool);
    color: var(--card);
}
.sm .select2-drop.select2-sm .select2-results .select2-highlighted .select2-opt-info { color: var(--card); }
.sm .select2-drop.select2-sm .select2-results .select2-match { text-decoration: underline; font-weight: 600; }
/* optgroup headings */
.sm .select2-drop.select2-sm .select2-results .select2-result-with-children > .select2-result-label {
    color: var(--ink-soft);
    font-size: calc(var(--base-font, 14px) - 2px);
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: uppercase;
}
.sm .select2-drop.select2-sm .select2-results .select2-result-sub { padding-left: 0; }
.sm .select2-drop.select2-sm .select2-results .select2-disabled {
    background: transparent;
    color: var(--ink-faint);
}
.sm .select2-drop.select2-sm .select2-results .select2-no-results,
.sm .select2-drop.select2-sm .select2-results .select2-searching,
.sm .select2-drop.select2-sm .select2-results .select2-selection-limit,
.sm .select2-drop.select2-sm .select2-results .select2-ajax-error {
    padding: 8px 10px;
    background: transparent;
    color: var(--ink-faint);
}

/* ---- Multi-selects: chosen entries as chips ---- */
.sm .select2-container-multi.select2-sm.form-control { height: auto; }
.sm .select2-container-multi.select2-sm .select2-choices {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
    min-height: var(--tap);
    padding: 5px 6px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--card);
    box-shadow: none;
    cursor: text;
}
.sm .select2-container-multi.select2-sm .select2-choices .select2-search-choice {
    margin: 0;
    padding: 4px 8px 4px 24px;
    border: 1px solid var(--line);
    border-radius: 4px;
    background: var(--pool-wash);
    color: var(--ink);
    font-size: calc(var(--base-font, 14px) - 1px);
    line-height: 1.3;
    box-shadow: none;
}
.sm .select2-container-multi.select2-sm .select2-choices .select2-search-choice-focus {
    border-color: var(--pool);
}
.sm .select2-container-multi.select2-sm .select2-search-choice-close {
    top: 50%;
    left: 6px;
    width: 12px;
    height: 12px;
    margin-top: -6px;
    background: var(--ink-faint);
    -webkit-mask: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'%3E%3Cpath stroke='%23000' stroke-width='1.6' stroke-linecap='round' d='M1 1l8 8M9 1 1 9'/%3E%3C/svg%3E") no-repeat center / 9px 9px;
    mask: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'%3E%3Cpath stroke='%23000' stroke-width='1.6' stroke-linecap='round' d='M1 1l8 8M9 1 1 9'/%3E%3C/svg%3E") no-repeat center / 9px 9px;
}
.sm .select2-container-multi.select2-sm .select2-search-choice-close:hover { background: var(--overdue); }
.sm .select2-container-multi.select2-sm .select2-choices .select2-search-field input {
    min-height: 30px;
    margin: 0;
    padding: 4px 6px;
    color: var(--ink);
    font-family: var(--font);
    font-size: var(--base-font, 14px);
}

/* A phone or tablet: entries a thumb can hit, and a list that uses the screen. */
@media (pointer: coarse) {
    .sm .select2-drop.select2-sm .select2-results { max-height: 60vh; }
    .sm .select2-drop.select2-sm .select2-results .select2-result-label {
        display: flex;
        align-items: center;
        min-height: var(--tap);
    }
    /* the crosses: a larger target, the same small glyph (centred by its mask) */
    .sm .select2-container.select2-sm .select2-choice abbr {
        width: 28px;
        height: 28px;
        margin-top: -14px;
        right: 30px;
    }
    .sm .select2-container.select2-sm .select2-choice abbr::before { inset: 9px; }
    .sm .select2-container.select2-sm.select2-allowclear .select2-choice { padding-right: 64px; }
    .sm .select2-container-multi.select2-sm .select2-choices .select2-search-choice { padding-left: 30px; }
    .sm .select2-container-multi.select2-sm .select2-search-choice-close {
        left: 2px;
        width: 24px;
        height: 24px;
        margin-top: -12px;
    }
}

/* Windows high contrast: system colours for the chosen entry and the drawn icons. */
@media (forced-colors: active) {
    .sm .select2-drop.select2-sm .select2-results .select2-highlighted {
        background: Highlight;
        color: HighlightText;
    }
    .sm .select2-container.select2-sm .select2-choice::after,
    .sm .select2-container.select2-sm .select2-choice abbr::before,
    .sm .select2-container.select2-sm .select2-choice abbr:hover::before,
    .sm .select2-container-multi.select2-sm .select2-search-choice-close,
    .sm .select2-container-multi.select2-sm .select2-search-choice-close:hover { background: CanvasText; }
}

/*
   The stock sheet swaps its icons for a 2x sprite on high-DPI screens, with
   !important, and that is the one thing here only !important can undo: the sprite's
   corner (a small grey arrow) painted under the clear cross, behind the chip cross's
   mask, and over the search box's magnifier. The one exception to this file's
   no-!important rule, matching the stock rule it overrides.
*/
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 2dppx) {
    .sm .select2-container.select2-sm .select2-choice abbr,
    .sm .select2-container-multi.select2-sm .select2-search-choice-close { background-image: none !important; }
    .sm .select2-drop.select2-sm .select2-search input {
        background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14'%3E%3Ccircle cx='6' cy='6' r='4.5' fill='none' stroke='%236B7F85' stroke-width='1.6'/%3E%3Cpath stroke='%236B7F85' stroke-width='1.6' stroke-linecap='round' d='m9.5 9.5 3 3'/%3E%3C/svg%3E") !important;
        background-position: right 10px center !important;
        background-size: 14px 14px !important;
        background-repeat: no-repeat !important;
    }
    .sm .select2-drop.select2-sm .select2-search input.select2-active {
        background-image: url("/js/select2/select2-spinner.gif") !important;
        background-size: 16px 16px !important;
    }
}

/* On paper: the choice, whole, without the controls around it. */
@media print {
    .sm .select2-container.select2-sm .select2-choice::after,
    .sm .select2-container.select2-sm .select2-choice abbr,
    .sm .select2-container-multi.select2-sm .select2-search-choice-close { display: none; }
    .sm .select2-container.select2-sm .select2-choice > .select2-chosen { white-space: normal; }
}

/* The task editor's "Complete by": the choice and the value it needs on one line, as
   the selects were (the box has no name attribute; sm-select2.js copies it). */
.sm .sm-task-editor .select2-container.select2-sm[data-sm-s2-name^="packet_schedule_complete_by"] {
    display: inline-block;
    width: auto;
    vertical-align: middle;
}
.sm .sm-task-editor .select2-container.select2-sm.full-size { max-width: 420px; }

/* ======================================================================
   Training Matrix toolbar (js-src/training_matrix/components/TrainingMatrixForm.vue).
   Filters side by side and wrapping; Add Staff as the primary action; CSV and XLSX
   as one Download group; the qualifications switch and the legend on one line.
   Kept here rather than in the component so the build does not pull webpack's CSS
   loaders into the shared vendor chunk (that renumbers every bundle).
   ====================================================================== */
.sm .tm-toolbar { margin-bottom: 16px; }
.sm .tm-toolbar__row { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 12px 24px; }
.sm .tm-toolbar__row--sub { align-items: center; margin-top: 12px; }
.sm .tm-toolbar__filters { display: flex; flex-wrap: wrap; gap: 10px; flex: 1 1 480px; min-width: 0; }
.sm .tm-toolbar__filter { flex: 1 1 200px; max-width: 300px; min-width: 0; }
.sm .tm-toolbar__filter select,
.sm .tm-toolbar__filter .select2-container { width: 100% !important; }
.sm .tm-toolbar__location { margin: 6px 0; font-size: 18px; }
.sm .tm-toolbar__actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.sm .tm-toolbar__actions .btn .fa { margin-right: 4px; }
/* The gap spaces the controls; a theme margin on .btn-group would indent the group. */
.sm .tm-toolbar__actions > .btn-group { margin: 0; }
/* The group's label is not a control: no hover, no pointer. */
.sm .tm-toolbar__group-label,
.sm .tm-toolbar__group-label:hover { cursor: default; pointer-events: none; background: var(--surface); color: var(--ink-soft); font-weight: 600; }
.sm .tm-toolbar__check { display: inline-flex; align-items: center; gap: 8px; margin: 0; font-weight: normal; cursor: pointer; }
.sm .tm-toolbar__check input { margin: 0; }
/* The legend as one line: its title and the swatches side by side. */
.sm .tm-toolbar__legend { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 16px; }
.sm .tm-toolbar__legend .color-legend__title { margin: 0 !important; font-weight: 600; }
.sm .tm-toolbar__legend .color-legend__items { margin: 0 !important; }
.sm .tm-toolbar__legend .color-legend__item { margin-bottom: 0 !important; }
@media (max-width: 767.98px) {
    .sm .tm-toolbar__filter { max-width: none; flex-basis: 100%; }
    .sm .tm-toolbar__row--sub { flex-direction: column; align-items: flex-start; }
}

/* ======================================================================
   Record header and actions (action_menu(), functions/webout_fn_action_menu.php):
   the name on the left, the record's actions beside it on the right.
   ====================================================================== */
.sm .sm-recordhead { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px 16px; margin-bottom: 16px; }
.sm .sm-recordhead__title { margin: 0 !important; }
.sm .sm-recordhead__actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.sm .sm-action-menu { display: inline-flex; }
.sm .sm-action-menu .btn .fa,
.sm .sm-recordhead__actions .btn .fa { margin-right: 4px; }
.sm .sm-action-menu .dropdown-item .fa { width: 16px; margin-right: 6px; }

/* Compliance summary: the figure with a bar, and how the items break down. */
.sm .sm-compliance {
    display: flex; flex-wrap: wrap; align-items: center; gap: 16px 32px;
    padding: 16px 20px; margin: 16px 0 24px;
    background: var(--card); border: 1px solid var(--line); border-radius: var(--r);
}
.sm .sm-compliance__figure { display: grid; grid-template-columns: auto 1fr; align-items: baseline; gap: 4px 12px; min-width: 220px; flex: 0 1 320px; }
.sm .sm-compliance__label { grid-column: 1 / -1; font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-faint); }
.sm .sm-compliance__value { font-size: 28px; font-weight: 700; line-height: 1; color: var(--ink); }
.sm .sm-compliance__bar { align-self: center; height: 8px; border-radius: 4px; background: var(--line-soft); overflow: hidden; }
.sm .sm-compliance__bar > span { display: block; height: 100%; background: var(--done); border-radius: 4px; }
.sm .sm-compliance__counts { display: flex; flex-wrap: wrap; gap: 8px 24px; list-style: none; margin: 0; padding: 0; color: var(--ink-soft); }
.sm .sm-compliance__counts li { display: inline-flex; align-items: center; gap: 8px; }
.sm .sm-compliance__count { font-weight: 700; color: var(--ink); }

/* A status: a coloured dot and its name, on one line. */
.sm .sm-status { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.sm .sm-status-dot { display: inline-block; flex: 0 0 10px; width: 10px; height: 10px; border-radius: 50%; }

.sm .sm-section-title { display: flex; align-items: center; gap: 10px; margin-top: 24px; }
.sm .sm-section-title__count {
    display: inline-block; min-width: 24px; padding: 2px 8px; border-radius: 12px;
    font-size: 12px; font-weight: 600; line-height: 1.4; text-align: center;
    background: var(--idle-wash); color: var(--ink-soft);
}

/* ======================================================================
   Day navigation shared by the Handover Book, Centre Log and Reception Book
   (.centre_log_navigation): previous, the date (opens the picker), next, as one
   centred group instead of two buttons pinned to the far edges.
   ====================================================================== */
.sm .centre_log_navigation {
    position: static; display: flex; align-items: center; justify-content: center;
    gap: 8px; padding: 0; margin: 4px 0 16px;
}
.sm .centre_log_navigation .btn { position: static; margin: 0; min-width: var(--tap); padding: 0 12px; }
.sm .centre_log_navigation .has-datepicker {
    display: inline-flex; align-items: center; gap: 8px;
    min-height: var(--tap); padding: 0 16px;
    border: 1px solid var(--line); border-radius: var(--r); background: var(--card);
    cursor: pointer;
}
.sm .centre_log_navigation .has-datepicker:hover { background: var(--pool-wash); }
.sm .centre_log_navigation .has-datepicker label { line-height: 1; margin: 0; font-weight: 600; cursor: pointer; }
.sm .centre_log_navigation .has-datepicker:hover label { text-decoration: none; }

/* ======================================================================
   Handover Book (webout_handover_book.php)
   ====================================================================== */
/* Three cells: where (left), the day (centred on the page, not between its neighbours,
   because the outer columns are equal), the admin action (right). */
.sm .hb-toolbar {
    display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    grid-template-areas: "filters date actions"; align-items: center; gap: 10px 16px; margin-bottom: 12px;
}
.sm .hb-toolbar > .hb-toolbar__filters { grid-area: filters; }
.sm .hb-toolbar__filters { display: flex; flex-wrap: wrap; gap: 10px; flex: 1 1 320px; min-width: 0; }
.sm .hb-toolbar__filters select,
.sm .hb-toolbar__filters .select2-container { flex: 0 1 280px; min-width: 200px; width: auto !important; }
.sm .hb-toolbar__actions { display: flex; flex-wrap: wrap; gap: 10px; grid-area: actions; justify-self: end; }
.sm .hb-toolbar__actions .btn { margin: 0; white-space: nowrap; }
/* The day control sits in the row beside the location, so it drops its own centring and margins. */
.sm .hb-toolbar .centre_log_navigation { grid-area: date; margin: 0; }
/* Not room for three cells side by side: where and the actions on the first row, the day
   centred on its own row beneath. */
@media (max-width: 1199.98px) {
    .sm .hb-toolbar { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "filters actions" "date date"; }
    .sm .hb-toolbar .centre_log_navigation { justify-self: center; }
}
.sm .hb-toolbar .btn .fa,
.sm .hb-actions .btn .fa { margin-right: 4px; }


/* A department's actions in one row. */
.sm .hb-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 10px; margin: 12px 0 16px; }

/* Adding a staff note: a full-width box with its button right beneath it. */
.sm .hb-note-form { margin: 0 0 20px; }
.sm .hb-note-form__label { display: block; margin-bottom: 6px; font-weight: 600; }
.sm .hb-note-form textarea { width: 100%; max-width: none; resize: vertical; }
.sm .hb-note-form__actions { display: flex; justify-content: flex-end; margin-top: 8px; }

@media (max-width: 767.98px) {
    /* Phones: one cell per line, the day centred, the action at the end. */
    .sm .hb-toolbar { grid-template-columns: minmax(0, 1fr); grid-template-areas: "filters" "date" "actions"; }
    .sm .hb-toolbar__filters select,
    .sm .hb-toolbar__filters .select2-container { flex-basis: 100%; }
    .sm .hb-actions { justify-content: flex-start; }
}

/* ======================================================================
   Home page (webout_page_processing.php adds .contentArea--home).
   The greeting and welcome text are each tenant's own content; this only gives
   them a heading's weight and a readable measure.
   ====================================================================== */
/* The :not() chain is specificity arithmetic, not decoration: the generic heading rule is
   .sm h2 plus these five :not()s (0,6,1), and must lose to this one. */
.sm .contentArea--home > h2:nth-child(-n+2):not(.card-title):not(.qe-title):not(.page-title):not(.page_title):not(.panel-title) { margin: 8px 0 10px; font-size: 28px; font-weight: 700; letter-spacing: -.01em; line-height: 1.2; color: var(--ink); }
.sm .contentArea--home > h2:nth-child(-n+2) strong { font-weight: inherit; }
.sm .contentArea--home > .contentArea__date { margin: 0; font-size: 12px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--pool); }
.sm .contentArea--home > .contentArea__date + h2 { margin-top: 4px !important; }
.sm .contentArea--home > p { max-width: 72ch; color: var(--ink-soft); line-height: 1.6; }
.sm .contentArea--home > p:empty { display: none; }

/* ======================================================================
   Page tiles placed in page content (the navigation menu token), as a launcher:
   compact cards with the icon in a tinted square and the name beside it, rather
   than tall squares of underlined capitals. Scoped to .contentArea so the menu
   overlay is untouched. Tiles a tenant has given its own colour or image (an
   inline style from MenuItem.vue) keep it; only their text and icon adapt.
   ====================================================================== */
.sm .contentArea .navigation-menu { max-width: none; margin: 24px 0 8px; }
.sm .contentArea .navigation-menu__row {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 12px; margin: 0;
}
.sm .contentArea .navigation-menu__col { padding: 0; flex: none; max-width: none; }
.sm .contentArea a.navigation-menu__item {
    display: flex; align-items: center; height: 100%; min-height: 68px; padding: 12px 16px;
    border: 1px solid var(--line); border-left: 3px solid var(--line); border-radius: var(--r);
    background-color: var(--card); color: var(--ink);
    font-size: 15px; font-weight: 600; text-transform: none;
    /* !important only to beat SmartAdmin's a[target]:not(.btn){text-decoration:underline!important}:
       the tile is a card, its name is not an inline link. */
    text-decoration: none !important;
    transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.sm .contentArea a.navigation-menu__item:hover,
.sm .contentArea a.navigation-menu__item:focus-visible {
    color: var(--ink); text-decoration: none;
    border-color: var(--line); border-left-color: var(--pool);
    box-shadow: 0 6px 18px rgba(33, 37, 41, .08);
    transform: translateY(-1px);
}
.sm .contentArea a.navigation-menu__item:focus-visible { outline: 2px solid var(--pool); outline-offset: 2px; }
.sm .contentArea .navigation-menu__item-filter { display: none; }
.sm .contentArea .navigation-menu__item-body {
    flex-direction: row; justify-content: flex-start; align-items: center;
    gap: 14px; height: auto; text-align: left; line-height: 1.3;
}
.sm .contentArea .navigation-menu__icon {
    display: grid; place-items: center; flex: 0 0 40px; width: 40px; height: 40px;
    padding: 0; border-radius: 10px; font-size: 18px;
    background: var(--pool-wash); color: var(--pool);
    transition: background-color .15s ease, color .15s ease;
}
.sm .contentArea a.navigation-menu__item:hover .navigation-menu__icon,
.sm .contentArea a.navigation-menu__item:focus-visible .navigation-menu__icon { background: var(--pool); color: #fff; }

/* A tenant's own colour or image: keep it, set white text, and show the filter
   again so an image stays readable under the label. */
.sm .contentArea a.navigation-menu__item[style] { color: #fff; border-color: transparent; background-size: cover; background-position: center; }
.sm .contentArea a.navigation-menu__item[style] .navigation-menu__item-filter { display: block; background: #000; opacity: .25; border-radius: inherit; }
.sm .contentArea a.navigation-menu__item[style] .navigation-menu__icon { background: rgba(255, 255, 255, .18); color: #fff; }
.sm .contentArea a.navigation-menu__item[style]:hover { color: #fff; border-left-color: #fff; }

@media (max-width: 575.98px) {
    .sm .contentArea .navigation-menu__row { grid-template-columns: 1fr 1fr; gap: 8px; }
    .sm .contentArea a.navigation-menu__item { min-height: 60px; padding: 10px 12px; font-size: 14px; }
    .sm .contentArea .navigation-menu__item-body { gap: 10px; }
    .sm .contentArea .navigation-menu__icon { flex-basis: 34px; width: 34px; height: 34px; font-size: 16px; }
}
@media (prefers-reduced-motion: reduce) {
    .sm .contentArea a.navigation-menu__item,
    .sm .contentArea .navigation-menu__icon { transition: none; }
    .sm .contentArea a.navigation-menu__item:hover { transform: none; }
}

/* ======================================================================
   Handover Book shift: Report and Notes as two tabs under the shift header
   (webout_handover_book.php decides the tabs once both are rendered).
   ====================================================================== */
.sm .hb-section-title { margin: 0 0 12px !important; font-size: 16px !important; font-weight: 700; color: var(--ink); }

.sm .hb-tab-count {
    display: inline-block; min-width: 20px; margin-left: 6px; padding: 1px 7px; border-radius: 10px;
    font-size: 12px; font-weight: 700; line-height: 1.5; text-align: center;
    background: var(--pool); color: #fff;
}

/* Notes: department heading, management notes, actions, staff notes, add a note. */
.sm .hb-shift__notes > h3:not(.hb-section-title) { margin: 16px 0 8px !important; font-size: 13px !important; font-weight: 700; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .04em; }
.sm .hb-shift__notes .well.handover-book-report { margin: 0 0 10px; padding: 12px 14px; background: var(--card); border: 1px solid var(--line); border-radius: var(--r); box-shadow: none; }
.sm .hb-shift__notes .no-report { color: var(--ink-faint); font-style: italic; }
.sm .hb-shift__notes .hb-actions { justify-content: flex-start; margin: 0 0 16px; }
/* margin: 0 cancels the theme's .btn + .btn gap (the row's gap spaces them); the primary first. */
.sm .hb-shift__notes .hb-actions .btn { margin: 0; }
.sm .hb-shift__notes .hb-actions .btn-primary { order: -1; }
.sm .hb-shift__notes .handover-book-notes { margin: 0 0 16px; }
.sm .hb-shift__notes .handover-book-notes > .list-group-item { padding: 12px 14px; }
.sm .hb-shift__notes .hb-note-form { margin: 0 0 4px; }

/* Report: one full-width card per section, its fields three to a row. */
.sm .hb-report__head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 6px 16px; }
.sm .hb-report__hint { font-size: 12px; color: var(--ink-faint); }
.sm .hb-report__hint .fa { margin-right: 4px; }
.sm .list-group.hb-report { margin: 0; gap: 12px; }
.sm .list-group.hb-report > .list-group-item {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px 20px;
    padding: 16px 20px; border: 1px solid var(--line); border-radius: var(--r);
}
.sm .list-group.hb-report > .list-group-item + .list-group-item { border-top-width: 1px; }
.sm .list-group.hb-report > .list-group-item > h4 { grid-column: 1 / -1; margin: 0 !important; font-size: 15px !important; font-weight: 700; }
/* width: the theme sizes .form-group to half its row (two per row); here the grid does that.
   display !important: the theme's read-only form-group rule is a long :not(:has(...)) chain
   (it lays label and value side by side) whose specificity this cannot reasonably match. */
.sm .list-group.hb-report > .list-group-item > .form-group { margin: 0; display: block !important; width: auto; min-width: 0; }
.sm .list-group.hb-report .form-control-static { padding: 0; min-height: 0; color: var(--ink); }
.sm .list-group.hb-report .form-control-static i { color: var(--ink-faint); }
.sm .list-group.hb-report .form-group > label { display: block; margin-bottom: 6px; font-weight: 600; color: var(--ink-soft); font-size: 13px; }
.sm .list-group.hb-report textarea,
.sm .list-group.hb-report input[type="number"] { width: 100%; max-width: none; }
.sm .list-group.hb-report textarea { min-height: 80px; resize: vertical; }
/* Update stays in reach on a long report. */
.sm .hb-report__foot {
    position: sticky; bottom: 0; z-index: 2; display: flex; justify-content: flex-end;
    margin-top: 12px; padding: 12px 0; background: var(--surface); border-top: 1px solid var(--line);
}

@media (max-width: 991.98px) {
    .sm .list-group.hb-report > .list-group-item { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 575.98px) {
    .sm .list-group.hb-report > .list-group-item { grid-template-columns: minmax(0, 1fr); padding: 14px; }
}

/* The bar under the shift tab: the Report / Notes switch, and the shift's actions. */
.sm .hb-shift-bar {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px 16px;
    margin: 8px 0 16px; padding-bottom: 12px; border-bottom: 1px solid var(--line);
}
.sm .hb-shift-bar__switch .hb-section-title { margin: 0 !important; }
.sm .hb-shift-bar__actions { display: flex; flex-wrap: wrap; gap: 10px; }
.sm .hb-shift-bar__actions .btn { margin: 0; }
.sm .hb-shift-bar__actions .btn .fa { margin-right: 4px; }

/* Report | Notes as one joined control: a track with the chosen segment raised. */
.sm .nav.hb-segmented {
    display: inline-flex; flex-wrap: nowrap; gap: 2px; margin: 0; padding: 3px;
    border: 1px solid var(--line); border-radius: calc(var(--r) + 3px); background: var(--surface);
}
.sm .nav.hb-segmented > li { list-style: none; margin: 0; }
.sm .nav.hb-segmented > li > a {
    display: inline-flex; align-items: center; gap: 6px; min-height: 36px; padding: 0 16px;
    border: 0; border-radius: var(--r); background: transparent;
    color: var(--ink-soft); font-weight: 600; text-decoration: none;
}
.sm .nav.hb-segmented > li > a:hover { color: var(--ink); text-decoration: none; }
/* The chosen one filled with the primary colour, so which is open reads at a glance. */
.sm .nav.hb-segmented > li > a.active,
.sm .nav.hb-segmented > li > a.active:hover { background: var(--pool); color: #fff; box-shadow: 0 1px 3px rgba(33, 37, 41, .2); }
.sm .nav.hb-segmented > li > a:not(.active):hover { background: var(--card); }
.sm .nav.hb-segmented > li > a.active .hb-tab-count { background: #fff; color: var(--pool); }
.sm .nav.hb-segmented > li > a:focus-visible { outline: 2px solid var(--pool); outline-offset: 1px; }

/* ======================================================================
   Incident Book (webout_centre_log.php): the toolbar reuses .hb-toolbar's three
   cells; records are cards (when/where, the record, its images).
   ====================================================================== */
.sm .cl-toolbar .hb-toolbar__filters select,
.sm .cl-toolbar .hb-toolbar__filters .select2-container { flex: 1 1 0; min-width: 140px; max-width: 280px; }
/* !important: sm-select2.js sets an inline min-width from the longest option, which here
   pushed the two selects onto two lines; side by side they truncate instead. */
.sm .cl-toolbar .hb-toolbar__filters .select2-container { min-width: 140px !important; }

.sm .cl-records { display: flex; flex-direction: column; gap: 10px; margin: 4px 0 16px; }
.sm .cl-record {
    display: grid; grid-template-columns: 120px minmax(0, 1fr) auto; gap: 4px 18px; align-items: start;
    padding: 14px 18px; background: var(--card); border: 1px solid var(--line); border-radius: var(--r);
}
.sm .cl-record:hover { border-color: var(--pool-wash); box-shadow: 0 4px 14px rgba(33, 37, 41, .06); }
.sm .cl-record__when { display: flex; flex-direction: column; gap: 2px; color: var(--ink-soft); font-size: 13px; }
.sm .cl-record__time { font-size: 18px; font-weight: 700; color: var(--ink); line-height: 1.2; }
.sm .cl-record__loc { color: var(--ink-faint); }
.sm .cl-record__head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.sm .cl-record__title { font-weight: 700; font-size: 15px; color: var(--ink); }
.sm .cl-record__title:hover { color: var(--pool); }
.sm .cl-record__meta { margin-top: 2px; font-size: 13px; color: var(--ink-soft); }
.sm .cl-record__excerpt { margin-top: 6px; color: var(--ink-soft); line-height: 1.5; }
.sm .cl-record__images { display: flex; flex-wrap: wrap; gap: 6px; max-width: 230px; }
.sm .cl-record__images img { display: block; width: 70px; height: 70px; object-fit: cover; border: 1px solid var(--line); border-radius: var(--r); }

/* Status, named rather than O / C / CT. */
.sm .cl-status {
    display: inline-block; padding: 2px 9px; border-radius: 999px;
    font-size: 12px; font-weight: 700; line-height: 1.5; white-space: nowrap;
}
.sm .cl-status--open { background: var(--overdue-wash, #fbeae8); color: var(--overdue, #a81f16); }
.sm .cl-status--closed { background: var(--done-wash, #e6f2ea); color: var(--done, #17703f); }
.sm .cl-status--task { background: var(--idle-wash, #eef2f3); color: var(--ink-soft); }

.sm .cl-empty { margin: 8px 0 16px; padding: 28px 16px; text-align: center; color: var(--ink-faint); background: var(--card); border: 1px dashed var(--line); border-radius: var(--r); }
.sm .cl-downloads { display: flex; flex-wrap: wrap; gap: 10px; margin: 8px 0 16px; }
.sm .cl-downloads .btn { margin: 0; }
.sm .cl-downloads .btn .fa,
.sm .cl-toolbar .btn .fa { margin-right: 4px; }

@media (max-width: 767.98px) {
    .sm .cl-record { grid-template-columns: minmax(0, 1fr); }
    .sm .cl-record__when { flex-direction: row; flex-wrap: wrap; gap: 4px 10px; }
    .sm .cl-record__images { max-width: none; }
}

/* List screens' actions as a button group (views/locations/index.view.php). */
.sm .sm-list-actions .btn .fa { margin-right: 4px; }
.sm .sm-list-actions__delete:not(:disabled) { color: var(--overdue, #a81f16); }
.sm .sm-list-actions__delete:disabled { opacity: .55; }
/* A list's heading with its action group beside it (webout_standard_shift_setup.php). */
.sm .sm-list-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 16px; margin: 4px 0 12px; }
.sm .sm-list-head__title { margin: 0 !important; font-size: 16px !important; font-weight: 700; }

/* ======================================================================
   Login report (views/reports/login/report.view.php)
   ====================================================================== */
.sm .lr-filters { padding: 16px; margin: 4px 0 12px; background: var(--card); border: 1px solid var(--line); border-radius: var(--r); }
.sm .lr-filters__row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px 16px; }
.sm .lr-filters__field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.sm .lr-filters__field--location { flex: 1 1 220px; max-width: 300px; }
.sm .lr-filters__field--period { flex: 0 1 160px; }
.sm .lr-filters__field--date { flex: 1 1 240px; max-width: 360px; }
.sm .lr-filters__field .input-group,
.sm .lr-filters__field .form-control { margin: 0; }
.sm .lr-filters__label { margin: 0; padding: 0; border: 0; width: auto; font-size: 12px; font-weight: 600; letter-spacing: .03em; text-transform: uppercase; color: var(--ink-faint); }
.sm .lr-filters__submit { margin-left: auto; }
.sm .lr-filters__submit .btn .fa { margin-right: 4px; }

/* Which columns to show: a row of toggle chips. */
.sm .lr-columns { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 14px 0 0; padding: 14px 0 0; border: 0; border-top: 1px solid var(--line-soft, #eaf0f0); min-width: 0; }
.sm .lr-columns > legend { float: left; margin: 0 8px 0 0; }
.sm .lr-chip { display: inline-flex; align-items: center; gap: 8px; margin: 0; padding: 6px 12px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); font-weight: 500; cursor: pointer; white-space: nowrap; }
.sm .lr-chip input { margin: 0; }
.sm .lr-chip:has(input:checked) { border-color: var(--pool); background: var(--pool-wash); color: var(--ink); }

/* How fresh the stats are, and the manual refresh: a note, not an alert. */
.sm .lr-freshness { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin: 0 0 20px; font-size: 13px; color: var(--ink-soft); }
.sm .lr-freshness .btn { margin: 0; }
.sm .lr-freshness .btn .fa { margin-right: 4px; }
.sm .lr-freshness__hint { color: var(--ink-faint); }

.sm .lr-results-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 16px; margin: 0 0 12px; }
.sm .lr-results-head__title { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; }
.sm .lr-results-head__title h3 { margin: 0 !important; }
.sm .lr-results-head__range { font-weight: 600; color: var(--ink-soft); }
.sm .lr-results-head .btn .fa { margin-right: 4px; }

@media (max-width: 767.98px) {
    .sm .lr-filters__field--location,
    .sm .lr-filters__field--date { max-width: none; flex-basis: 100%; }
    .sm .lr-filters__submit { margin-left: 0; width: 100%; }
    .sm .lr-filters__submit .btn { width: 100%; }
}

/* Login report results: a summary, then users with no logins marked in the row. */
.sm .lr-summary { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; margin: 0 0 12px; padding: 0; }
.sm .lr-summary > li { display: inline-flex; align-items: baseline; gap: 6px; padding: 8px 14px; background: var(--card); border: 1px solid var(--line); border-radius: var(--r); color: var(--ink-soft); }
.sm .lr-summary__value { font-size: 18px; font-weight: 700; color: var(--ink); }
.sm .lr-summary > li.lr-summary__item--ok { border-left: 3px solid var(--done, #17703f); }
.sm .lr-summary > li.lr-summary__item--none { border-left: 3px solid var(--overdue, #a81f16); }
.sm .lr-table td { vertical-align: middle; }
/* A border, not box-shadow: BS5 paints hover/striping with an inset box-shadow on each cell. */
.sm .lr-table tbody tr.lr-row--no-login > td:first-child { border-left: 3px solid var(--overdue, #a81f16); }
.sm .lr-name { font-weight: 600; color: var(--ink); }
.sm .lr-tag { display: inline-block; margin-left: 8px; padding: 1px 8px; border-radius: 999px; font-size: 11px; font-weight: 700; background: var(--overdue-wash, #fbeae8); color: var(--overdue, #a81f16); vertical-align: 1px; }
.sm .lr-num { font-variant-numeric: tabular-nums; font-weight: 600; }
.sm .lr-num--zero { color: var(--overdue, #a81f16); }
.sm .lr-total { font-weight: 600; color: var(--ink-soft); }
.sm .lr-total__value { margin-left: 8px; font-size: 16px; font-weight: 700; color: var(--ink); }

/* ======================================================================
   Table kit inside a bordered table box (.table-responsive): the search / count /
   export bar is the box's header strip, padded off the border with a divider
   under it, rather than jammed against the edges and the column headings.
   The pager likewise sits inside the box with room around it.
   ====================================================================== */
.sm .table-responsive > .sm-tablekit > .sm-tablekit__bar {
    margin: 0; padding: 10px 12px; gap: 10px;
    background: var(--surface); border-bottom: 1px solid var(--line);
}
.sm .table-responsive > .sm-tablekit > .sm-tablekit__pager { margin: 0; padding: 10px 12px; border-top: 1px solid var(--line); }
.sm .table-responsive > .sm-tablekit > .sm-tablekit__empty { padding: 14px 12px; }

/* Search with a magnifier, compact enough for a toolbar. */
.sm .sm-tablekit__search {
    min-height: 36px; padding-left: 34px;
    background: var(--card) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%237B8E94' stroke-width='2.4' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-3.5-3.5'/%3E%3C/svg%3E") no-repeat 12px center;
}
.sm .sm-tablekit__count { white-space: nowrap; }

/* Export: a compact secondary button with a download icon (FontAwesome 4, f019). */
.sm .sm-tablekit__bar [data-sm-tablekit-export] { min-height: 36px; padding: 0 14px; margin: 0; white-space: nowrap; }
.sm .sm-tablekit__bar [data-sm-tablekit-export]::before { content: "\f019"; font-family: FontAwesome; margin-right: 6px; font-weight: normal; }
/* A second row in a report filter card (e.g. Total Hours: who, then when). */
.sm .lr-filters__row--when { margin-top: 12px; }
.sm .sm-recordhead__title small { display: block; margin-top: 4px; font-size: 14px; font-weight: 500; color: var(--ink-soft); }
/* A full-width Build Report under the filter row (Total Hours). */
.sm .lr-filters__build { display: flex; align-items: center; justify-content: center; width: 100%; margin-top: 14px; }
.sm .lr-filters__build .fa { margin-right: 6px; }
/* Every filter on one line where there is room (1200px and up): the selects share the
   width equally, Period is narrower and Date wider, with a floor so a range's two dates
   are never clipped. A flex row rather than a grid so a site without departments or job
   roles still fills the line. Narrower screens wrap, Date first to go to a second line. */
.sm .lr-filters__row--grid > .lr-filters__field { flex: 1 1 170px; max-width: none; min-width: 0; }
.sm .lr-filters__row--grid > .lr-filters__field--date { flex: 1 1 260px; }
@media (min-width: 1200px) {
    .sm .lr-filters__row--grid { flex-wrap: nowrap; }
    .sm .lr-filters__row--grid > .lr-filters__field { flex: 1 1 0; }
    .sm .lr-filters__row--grid > .lr-filters__field--period { flex: .7 1 0; }
    .sm .lr-filters__row--grid > .lr-filters__field--date { flex: 1.5 1 0; min-width: 250px; }
}

/* ======================================================================
   Link cards: a grid of things to open, each a card with an icon, a name, what
   opening it does, and a chevron (views/locations/completion_report_matrices).
   ====================================================================== */
.sm .sm-cards-head { margin: 4px 0 14px; }
.sm .sm-cards-head__title { margin: 0 0 2px !important; font-size: 16px !important; font-weight: 700; }
.sm .sm-cards-head__hint { margin: 0; color: var(--ink-soft); }
.sm .sm-link-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; margin-bottom: 16px; }
.sm .sm-link-card {
    display: flex; align-items: center; gap: 14px; min-height: 72px; padding: 14px 16px;
    background: var(--card); border: 1px solid var(--line); border-left: 3px solid var(--line); border-radius: var(--r);
    color: var(--ink); text-decoration: none !important;
    transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.sm .sm-link-card:hover,
.sm .sm-link-card:focus-visible { color: var(--ink); border-left-color: var(--pool); box-shadow: 0 6px 18px rgba(33, 37, 41, .08); transform: translateY(-1px); }
.sm .sm-link-card:focus-visible { outline: 2px solid var(--pool); outline-offset: 2px; }
.sm .sm-link-card__icon { display: grid; place-items: center; flex: 0 0 40px; width: 40px; height: 40px; border-radius: 10px; background: var(--pool-wash); color: var(--pool); font-size: 18px; transition: background-color .15s ease, color .15s ease; }
.sm .sm-link-card:hover .sm-link-card__icon,
.sm .sm-link-card:focus-visible .sm-link-card__icon { background: var(--pool); color: #fff; }
.sm .sm-link-card__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
.sm .sm-link-card__title { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sm .sm-link-card__meta { font-size: 13px; color: var(--ink-soft); }
.sm .sm-link-card__go { color: var(--ink-faint); transition: transform .15s ease, color .15s ease; }
.sm .sm-link-card:hover .sm-link-card__go { color: var(--pool); transform: translateX(2px); }
@media (prefers-reduced-motion: reduce) {
    .sm .sm-link-card, .sm .sm-link-card__go, .sm .sm-link-card__icon { transition: none; }
    .sm .sm-link-card:hover { transform: none; }
    .sm .sm-link-card:hover .sm-link-card__go { transform: none; }
}
/* A field that needs more room in a report filter row (a multi-select of items or locations). */
.sm .lr-filters__row--grid > .lr-filters__field--wide { flex: 2 1 280px; }
@media (min-width: 1200px) {
    .sm .lr-filters__row--grid > .lr-filters__field--wide { flex: 2.2 1 0; }
}
/* A tall multi-select (many items picked) must not drag the other fields down to its bottom edge. */
.sm .lr-filters__row--grid { align-items: flex-start; }

/* A view switch (sm-actions.js, a select with no 'Actions' placeholder): the current view filled. */
.sm .sm-list-actions--switch .btn.active { --bs-btn-active-bg: var(--pool); --bs-btn-active-border-color: var(--pool); --bs-btn-active-color: #fff; background: var(--pool); border-color: var(--pool); color: #fff; }
.sm .sm-list-actions { margin-bottom: 12px; }

/* A list header column that held an "Actions" select (col-sm-3/col-sm-4) is too
   narrow for the button group sm-actions.js puts there: size it to the buttons
   and let the other columns in the row share what is left. */
.sm .row > [class*="col-"]:has(> .sm-list-actions) { flex: 0 0 auto; width: auto; max-width: 100%; }
.sm .row:has(> [class*="col-"] > .sm-list-actions) > [class*="col-"]:not(:has(> .sm-list-actions)) { flex: 1 1 0; width: auto; min-width: 0; }
.sm .sm-list-actions { flex-wrap: wrap; }

/* The row-tick column sm-actions.js moves to the front of a list table. */
.sm table .sm-check-col { width: 44px; padding-left: 12px; padding-right: 4px; text-align: left; vertical-align: middle; }
.sm table .sm-check-col input[type=checkbox] { margin: 0; vertical-align: middle; }

/* A simple record form in one card: labels above full-width fields, Save + Cancel
   at the foot (views/asset_groups/edit.view.php). */
.sm .sm-formcard { padding: 20px; background: var(--card); border: 1px solid var(--line); border-radius: var(--r); }
.sm .sm-formcard__field { display: flex; flex-direction: column; gap: 6px; margin: 0 0 16px; }
.sm .sm-formcard__label { margin: 0; padding: 0; border: 0; width: auto; font-size: 13px; font-weight: 600; color: var(--ink); }
.sm .sm-formcard__optional { margin-left: 6px; font-size: 12px; font-weight: 400; color: var(--ink-faint); }
.sm .sm-formcard .form-control { width: 100%; max-width: none; margin: 0; }
.sm .sm-formcard textarea.form-control { min-height: 110px; resize: vertical; }
.sm .sm-formcard__footer { display: flex; flex-wrap: wrap; gap: 10px; padding-top: 16px; margin-top: 4px; border-top: 1px solid var(--line); }
.sm .sm-formcard__footer .btn { margin: 0; }
.sm .sm-formcard__footer .btn .fa { margin-right: 4px; }

/* Communications in the menu tree, under Homepage (app-ui.php). The rail item it
   replaces stays only for the phone bottom bar. */
.sm .sm-nav__badge {
    margin-left: auto;
    min-width: 20px;
    padding: 1px 7px;
    border-radius: 10px;
    background: var(--overdue);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    line-height: 18px;
    text-align: center;
}
.sm .sm-nav__badge[hidden] { display: none; }
.sm .sm-nav__link--unread .sm-nav__text { color: #fff; font-weight: 600; }
@media (min-width: 768px) {
    .sm .sidebar-menu__item--phone { display: none; }
}
