/* =========================================================================
   H&T 1.0 — UI LAYER
   -------------------------------------------------------------------------
   Loaded LAST, after app.css / theme.css / mobile.css / theme-modes.css, so
   on equal specificity this file wins. That is deliberate: it is the layer
   that makes every screen behave the same way, and it must be able to settle
   an argument with any of the four below it.

   What lives here, and nothing else:

     1. PAGE FURNITURE every screen shares — the head block, the toolbar
        above a list, the empty state, the sticky save bar. A view should
        never hand-build one of these; helpers.php renders them.
     2. POLISH on components app.css already owns — depth on a card, a real
        hit area on a row action, a table header that stays put while you
        scroll. Shapes and colours only; nothing here moves a grid track or
        renames a colour's job.
     3. The behaviour classes assets/ui.js switches on.

   RULES.
     · Colour comes from the tokens in theme-modes.css. No literal hex for
       anything that has to differ between light and dark — the two
       exceptions are shadows and glass, which are declared as tokens here
       and re-declared for dark at the foot of the file.
     · Anything that overrides a mobile.css rule repeats its media query.
       mobile.css turns list tables into cards below 760px, and every rule
       here that touches a table is written so that still happens.
     · Motion is opt-out: the whole file is disabled under
       prefers-reduced-motion at the end.
   ========================================================================= */

:root {
  /* Depth. Four steps, used consistently: 1 = resting card, 2 = hover,
     3 = something floating over the page, 4 = a bar pinned to an edge. */
  --sh-1: 0 1px 2px rgba(15, 23, 42, .05), 0 1px 3px rgba(15, 23, 42, .04);
  --sh-2: 0 4px 10px rgba(15, 23, 42, .07), 0 2px 4px rgba(15, 23, 42, .04);
  --sh-3: 0 12px 28px rgba(15, 23, 42, .12), 0 4px 8px rgba(15, 23, 42, .06);
  --sh-4: 0 -6px 20px rgba(15, 23, 42, .10);

  /* A pane that sits over the page and lets a little of it through. */
  --glass: rgba(255, 255, 255, .82);

  /* One radius scale, so nothing is 11px next to a 12px. */
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;

  /* One easing curve for the whole app. */
  --ease: cubic-bezier(.32, .72, .35, 1);
}


/* =========================================================================
   1 · PAGE HEAD
   Icon, title, one line of explanation, and this page's own actions. It
   replaces the "<h2> then a grey paragraph then a form" opening that every
   master screen had written out by hand in a slightly different order.
   ========================================================================= */

.pagehead {
  display: flex; align-items: flex-start; gap: 14px; flex-wrap: wrap;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 16px 18px; margin-bottom: 18px; box-shadow: var(--sh-1);
  position: relative; overflow: hidden;
}
/* A hairline of the page's own colour along the top edge, so two admin
   screens are told apart before either title is read. */
.pagehead::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: var(--g-spectrum, var(--blue));
}
.pagehead > .k-ico { margin-top: 2px; }
.ph-txt { flex: 1 1 320px; min-width: 0; }
.ph-txt h2 { margin: 0; font-size: 17.5px; font-weight: 800; letter-spacing: -.01em; line-height: 1.25; }
.ph-txt p {
  margin: 4px 0 0; font-size: 12.5px; color: var(--muted);
  line-height: 1.55; max-width: 95ch;
}
.ph-txt p b { color: var(--ink-1); font-weight: 700; }
.ph-act { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-left: auto; }

/* When the head carries figures rather than buttons. */
.ph-stats { display: flex; gap: 8px; flex-wrap: wrap; }
.ph-stat {
  border: 1px solid var(--line); border-radius: 10px; padding: 6px 12px;
  background: var(--sf-2); text-align: right; min-width: 84px;
}
.ph-stat i { display: block; font-style: normal; font-size: 9.5px; font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.ph-stat b { font-size: 16px; font-weight: 800; font-variant-numeric: tabular-nums; }


/* =========================================================================
   2 · TOOLBAR — the strip above a list
   Search on the left, filters in the middle, the count on the right. One
   shape for all of them, so the eye finds the search box in the same place
   on every screen.
   ========================================================================= */

.toolbar {
  display: flex; align-items: center; gap: 8px 10px; flex-wrap: wrap;
  margin-bottom: 12px;
}
.toolbar > .spacer { flex: 1 1 auto; }
.toolbar .tb-l {
  font-size: 10px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); white-space: nowrap;
}

/* ---- the search box ---- */
.tsearch { position: relative; flex: 0 1 280px; min-width: 190px; }
.tsearch > svg {
  position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; color: var(--muted); pointer-events: none;
}
.tsearch input[type=search] {
  width: 100%; padding: 8px 30px 8px 33px; border-radius: 999px;
  border: 1px solid var(--bd-4); background: var(--sf-1);
  font-size: 13px; -webkit-appearance: none; appearance: none;
}
.tsearch input[type=search]::-webkit-search-cancel-button { display: none; }
.tsearch input[type=search]:focus { border-color: var(--blue); background: var(--sf-1); }
.tsearch .ts-x {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  width: 24px; height: 24px; border: 0; border-radius: 50%; background: none;
  color: var(--muted); font-size: 17px; line-height: 1; cursor: pointer; display: none;
}
.tsearch.has-q .ts-x { display: block; }
.tsearch .ts-x:hover { background: var(--sf-10); color: var(--ink); }

/* ---- the running count the search writes ---- */
.tcount {
  font-size: 11.5px; color: var(--muted); font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.tcount b { color: var(--ink); }
.tcount.filtered { color: var(--blue-dark); font-weight: 700; }

/* ---- a counted chip, for "12 records" beside a heading ---- */
.cchip {
  display: inline-flex; align-items: baseline; gap: 4px; white-space: nowrap;
  padding: 2px 9px; border-radius: 999px; font-size: 11px; font-weight: 600;
  background: var(--sf-10); color: var(--ink-2); border: 1px solid transparent;
}
.cchip b { font-weight: 800; font-variant-numeric: tabular-nums; }
.cchip.good  { background: var(--green-soft);  color: var(--on-green-2); border-color: var(--edge-green-2); }
.cchip.warn  { background: var(--amber-soft);  color: var(--amber-ink);  border-color: var(--edge-amber-1); }
.cchip.bad   { background: var(--red-soft);    color: var(--red-ink);    border-color: var(--edge-red-2); }
.cchip.info  { background: var(--blue-soft);   color: var(--blue-dark);  border-color: var(--tint-blue-11); }

/* ---- a segmented filter: two or three mutually exclusive views ---- */
.seg {
  display: inline-flex; padding: 3px; gap: 2px; border-radius: 999px;
  background: var(--sf-9); border: 1px solid var(--line);
}
.seg > * {
  padding: 5px 13px; border-radius: 999px; border: 0; background: none; cursor: pointer;
  font: 700 12px var(--font); color: var(--muted); white-space: nowrap; text-decoration: none;
  transition: background .16s var(--ease), color .16s var(--ease);
}
.seg > *:hover { color: var(--ink); text-decoration: none; }
.seg > .on { background: var(--card); color: var(--ink); box-shadow: var(--sh-1); }
.seg > .on.good { color: var(--on-green-2); }
.seg > .on.bad  { color: var(--red-ink); }


/* =========================================================================
   3 · CARDS
   The card was a flat white rectangle with a hairline. It keeps that — the
   figures on it are what should be looked at — but gains a shadow so it
   reads as a surface above the page rather than a cut-out, and a header row
   so a title, a chip and a button can share the top line without every view
   inventing its own flex box.
   ========================================================================= */

/* NOTE. theme.css draws the card's 3px top accent WITH ITS BOX-SHADOW —
   `inset 0 3px 0 var(--ca)` — so anything here that sets box-shadow on .card
   has to carry that inset through or the accent disappears from every card in
   the app. That is why this file adds no resting shadow of its own: the one
   theme.css already gives a card is the right one, and the only rule below
   that touches box-shadow re-declares the inset alongside it. */

/* The head line of a card: title · chips · spacer · actions. */
.card-h {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: -2px 0 13px;
}
.card-h > h2, .card-h > h3 { margin: 0 !important; }
.card-h > h3 { text-transform: none; letter-spacing: 0; color: var(--ink); font-size: 14px; }
.card-h .spacer { flex: 1 1 auto; }
.card-h + .toolbar { margin-top: -4px; }

/* theme.css puts the card's accent colour on a short bar in front of its
   title, but keys that off `.card > h2` — and the title is a step further
   down now that it shares a line with chips and buttons. Same bar, one level
   deeper, so a card with a head line still reads as the same object. */
.card > .card-h > h2 { position: relative; }
.card > .card-h > h2::before {
  content: ""; display: inline-block; vertical-align: 1px;
  width: 4px; height: 14px; border-radius: 3px; margin-right: 8px;
  background: var(--ca);
}
/* Unless the head already opens with an icon — that is the same signal
   twice, and the bar just crowds the icon. */
.card > .card-h:has(> .k-ico) > h2::before { display: none; }

/* A card carrying a note under its title. */
.card-note {
  font-size: 12.5px; color: var(--muted); line-height: 1.55;
  margin: -6px 0 13px; max-width: 95ch;
}
.card-note b { color: var(--ink-1); }
.card-note a { font-weight: 600; }

/* The card being edited already got a blue ring in app.css; give it a label
   so it says WHY it is ringed, and let ui.js scroll the page to it. */
/* The inset accent is repeated on purpose — see the note at the head of §3. */
.card.editing { box-shadow: inset 0 3px 0 var(--ca), 0 0 0 3px var(--blue-soft), var(--sh-2); scroll-margin-top: 84px; }

/* A card that groups danger. It opts out of the accent wheel and takes red
   instead — the wheel is there to tell sections apart, and this section is
   telling you something. */
.card.danger { --ca: var(--c-rose); border-color: var(--edge-red-2); }
/* The one being edited takes the blue the rest of the app uses for "this is
   the row you chose". */
.card.editing { --ca: var(--c-blue); }


/* =========================================================================
   4 · TABLES
   Same table, easier to read down: zebra banding, a header that stays put
   inside a tall list, a hover that spans the row, and columns you can sort
   by clicking. Everything is scoped above 760px, because below that
   mobile.css has already turned the table into one card per row.
   ========================================================================= */

@media screen and (min-width: 761px) {

  /* Banding. Applied to every list table — .num columns are read across, and
     a 40-row rate list without banding is where the eye loses its line.

     The selector is wrapped in :where() so it contributes NO specificity:
     :nth-child() would otherwise outrank `table.data tr.sel > td` and paint
     over every row that is trying to say something (selected, total,
     highlighted, unfolded). At zero specificity it colours the rows nobody
     else has an opinion about and loses every argument, which is exactly
     what banding should do.

     A header row is all <th>, so `> td` never matches it — the head keeps
     its own colour without being excluded by name. */
  :where(table.data > tbody > tr:nth-child(even)) > td { background: var(--sf-2); }
  table.data > tbody > tr:hover > td { background: var(--tint-blue-1); }

  /* A header that scrolls away takes the meaning of every column with it. */
  .table-wrap.sticky { max-height: 68vh; overflow: auto; border-radius: var(--r-sm); }
  .table-wrap.sticky table.data > tbody > tr:first-child > th,
  .table-wrap.sticky table.data > tr:first-child > th,
  .table-wrap.sticky table.data > thead th {
    position: sticky; top: 0; z-index: 3;
    background: var(--sf-9);
    box-shadow: inset 0 -1px 0 var(--bd-4);
  }

  /* Click a heading to sort by it. ui.js does the sorting; this says so. */
  table.data th.sort { cursor: pointer; user-select: none; white-space: nowrap; }
  table.data th.sort::after {
    content: "↕"; margin-left: 5px; font-size: 9px; opacity: .3;
  }
  table.data th.sort:hover { color: var(--ink); }
  table.data th.sort.on { color: var(--blue-dark); }
  table.data th.sort.on::after { content: "↑"; opacity: 1; }
  table.data th.sort.on.desc::after { content: "↓"; }
}

/* ---- the row-actions column ----

   app.css declares `.actions { display: inline-flex }` for the little DIV of
   buttons the master lists used to wrap them in. The class now sits on the
   CELL, so that declaration has to be taken back: an inline-flex <td> is not
   a table cell any more and the whole row falls apart. It stays a cell, and
   the buttons inside it are an inline run that never wraps. */
table.data td.actions {
  display: table-cell;
  white-space: nowrap;
  vertical-align: middle;
}
table.data td.actions > * { vertical-align: middle; }
/* Only the buttons this file puts there are spaced — the older lists put a
   <div class="actions"> inside the cell and space themselves. */
table.data td.actions > .ibtn + .ibtn,
table.data td.actions > .ibtn + form,
table.data td.actions > form + .ibtn,
table.data td.actions > form + form { margin-left: 5px; }

/* Each button is a square with the icon in it; the label is its tooltip and
   its screen-reader name, and comes back as visible text on a phone, where
   the row has become a card and a 30px square would be unlabelled. */
.actions .ibtn {
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  width: 30px; height: 30px; padding: 0; flex: 0 0 30px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--sf-1); color: var(--muted); text-decoration: none;
  transition: color .14s var(--ease), border-color .14s var(--ease), background .14s var(--ease);
}
.ibtn svg { width: 15px; height: 15px; flex: 0 0 15px; }
.ibtn-l { display: none; }
.actions .ibtn:hover { text-decoration: none; border-color: var(--blue); color: var(--blue-dark); background: var(--blue-soft); }
.actions .ibtn.edit:hover  { border-color: var(--green); color: var(--on-green-2); background: var(--green-soft); }
.actions .ibtn.del,
.actions button.ibtn {
  font: inherit; cursor: pointer;
  width: 30px; height: 30px; padding: 0;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--sf-1); color: var(--muted);
  display: inline-flex; align-items: center; justify-content: center;
}
.actions .ibtn.del:hover, .actions button.ibtn.del:hover {
  border-color: var(--red); color: var(--red-ink); background: var(--red-soft);
}

/* A table row that is a link to somewhere. */
table.data tr.go { cursor: pointer; }

/* "no rows matched" — written by ui.js into the table itself. */
tr.tfilter-none > td {
  padding: 26px 12px !important; text-align: center; color: var(--muted);
  font-size: 12.5px; background: var(--sf-2) !important;
}


/* =========================================================================
   5 · EMPTY STATE
   A screen with nothing on it yet should say what is missing and offer the
   one button that fixes it — not print the word "None" in grey.
   ========================================================================= */

.empty {
  text-align: center; padding: 34px 20px;
  border: 1px dashed var(--bd-4); border-radius: var(--r-md); background: var(--sf-2);
}
.empty-ico {
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; border-radius: 50%; margin-bottom: 10px;
  background: var(--sf-10); color: var(--fill-3);
}
.empty-ico svg { width: 23px; height: 23px; }
.empty b { display: block; font-size: 14.5px; font-weight: 800; }
.empty p { margin: 5px auto 0; font-size: 12.5px; color: var(--muted); max-width: 56ch; line-height: 1.6; }
.empty-act { margin-top: 14px; display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; }


/* =========================================================================
   6 · FORMS
   ========================================================================= */

input[type=text], input[type=number], input[type=password], input[type=date],
input[type=datetime-local], input[type=search], input[type=tel], input[type=email],
select, textarea, .ss-input {
  transition: border-color .14s var(--ease), box-shadow .14s var(--ease), background .14s var(--ease);
}
input[type=text]:hover, input[type=number]:hover, input[type=password]:hover,
input[type=date]:hover, select:hover, textarea:hover {
  border-color: var(--bd-5);
}
/* The focus ring is the field's own outline rather than the browser's, so a
   field inside a table does not get a 2px box drawn over its neighbours. */
input[type=text]:focus, input[type=number]:focus, input[type=password]:focus,
input[type=date]:focus, input[type=datetime-local]:focus, input[type=search]:focus,
input[type=tel]:focus, input[type=email]:focus, select:focus, textarea:focus, .ss-input:focus {
  outline: none; border-color: var(--blue); box-shadow: 0 0 0 3px var(--kaw);
}
input:disabled, select:disabled, textarea:disabled {
  background: var(--sf-9); color: var(--muted); cursor: not-allowed;
}
::placeholder { color: var(--ink-7); opacity: 1; }

/* A required field says so in the label; mark the field itself only once it
   has been submitted empty, never while it is still being filled in. */
input:required:invalid:not(:placeholder-shown) { border-color: var(--edge-red-1); }

label.f { display: flex; align-items: center; gap: 5px; }
label.f .req { font-size: 13px; line-height: 1; }
/* A hint under a field — the note that used to be a loose grey div. */
.fhint { font-size: 11.5px; color: var(--muted); margin-top: 4px; line-height: 1.5; }
.fhint b { color: var(--ink-1); }

/* Checkbox / radio rows: the whole label is the hit area. */
.checkrow { display: flex; flex-wrap: wrap; gap: 6px 8px; }
.checkrow label, label.cbox {
  display: inline-flex; align-items: center; gap: 7px; margin: 0;
  padding: 5px 11px 5px 9px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--sf-1); font-size: 12.5px; cursor: pointer; white-space: nowrap;
  transition: border-color .14s var(--ease), background .14s var(--ease), color .14s var(--ease);
}
.checkrow label:hover, label.cbox:hover { border-color: var(--bd-5); background: var(--sf-2); }
.checkrow label:has(input:checked), label.cbox:has(input:checked) {
  border-color: var(--blue); background: var(--blue-soft); color: var(--blue-dark); font-weight: 700;
}
.checkrow input[type=checkbox], .checkrow input[type=radio],
label.cbox input[type=checkbox], label.cbox input[type=radio] {
  accent-color: var(--blue); width: 15px; height: 15px; margin: 0; cursor: pointer;
}

/* A labelled group of those, with its own heading. */
.cgroup { margin-bottom: 10px; }
.cgroup > .cg-h {
  display: flex; align-items: center; gap: 8px;
  font-size: 10px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 6px;
}
.cgroup > .cg-h::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.cg-all {
  border: 0; background: none; padding: 0; cursor: pointer; text-decoration: underline;
  font: 700 10px var(--font); letter-spacing: .04em; text-transform: uppercase; color: var(--link);
}

/* ---- a real on/off switch, replacing the bare checkbox on Settings ---- */
.switch { gap: 10px; }
.switch input[type=checkbox] {
  appearance: none; -webkit-appearance: none; margin: 0; cursor: pointer;
  width: 38px; height: 22px; flex: 0 0 38px; border-radius: 999px;
  background: var(--fill-2); border: 1px solid var(--bd-4); position: relative;
  transition: background .18s var(--ease), border-color .18s var(--ease);
}
.switch input[type=checkbox]::after {
  content: ""; position: absolute; top: 1px; left: 1px; width: 18px; height: 18px;
  border-radius: 50%; background: #fff; box-shadow: var(--sh-1);
  transition: transform .18s var(--ease);
}
.switch input[type=checkbox]:checked { background: var(--green); border-color: var(--green); }
.switch input[type=checkbox]:checked::after { transform: translateX(16px); }
.switch input[type=checkbox]:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.switch > span { font-size: 12.5px; color: var(--muted); min-width: 24px; }
.switch input:checked + span { color: var(--on-green-2); }


/* =========================================================================
   7 · BUTTONS
   ========================================================================= */

/* theme.css gives every button variant its own coloured gradient AND its own
   coloured shadow, so this file sets neither — it adds the press, and two new
   variants that did not exist. */
.btn {
  transition: background .15s var(--ease), border-color .15s var(--ease),
              box-shadow .15s var(--ease), transform .1s var(--ease);
}
.btn:active { transform: translateY(1px); }
.btn[disabled], .btn[disabled]:hover { transform: none; }
/* A ghost button — an action that is available but not being recommended. */
.btn.ghost {
  background: none; border-color: transparent; color: var(--muted); box-shadow: none;
}
.btn.ghost:hover { background: var(--sf-10); color: var(--ink); border-color: transparent; }
/* The one action a screen exists for. */
.btn.lg { padding: 10px 20px; font-size: 14.5px; border-radius: 10px; }


/* =========================================================================
   8 · STICKY SAVE BAR
   A long settings form whose only Save button is 900px below the field you
   just changed is a form people give up on. ui.js reveals this the moment
   anything on the form changes.
   ========================================================================= */

.savebar {
  position: sticky; bottom: 0; z-index: 30;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin: 18px 0 0; padding: 12px 16px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--card); box-shadow: var(--sh-4);
  transition: border-color .2s var(--ease), background .2s var(--ease);
}
.savebar .sb-note { font-size: 12.5px; color: var(--muted); flex: 1 1 200px; }
.savebar .sb-dirty { display: none; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 700; color: var(--amber-ink); }
.savebar .sb-dirty::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--amber); flex: 0 0 8px; }
.savebar.dirty { border-color: var(--edge-amber-1); background: var(--tint-amber-1); }
.savebar.dirty .sb-dirty { display: inline-flex; }
.savebar.dirty .sb-note { display: none; }


/* =========================================================================
   9 · FLASH MESSAGES
   They carry their meaning in an icon as well as a colour, and they can be
   dismissed — a success banner that will not go away is the app shouting
   over the page it just changed.
   ========================================================================= */

.flash {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 14px; border-radius: var(--r-sm); box-shadow: var(--sh-1);
  animation: flash-in .28s var(--ease) both;
}
@keyframes flash-in { from { opacity: 0; transform: translateY(-6px); } }
.flash::before {
  content: ""; flex: 0 0 18px; width: 18px; height: 18px; margin-top: 1px;
  border-radius: 50%; background: currentColor; opacity: .9;
  -webkit-mask: center / 13px no-repeat; mask: center / 13px no-repeat;
}
.flash.success::before {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 13 5 5 9-11'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 13 5 5 9-11'/%3E%3C/svg%3E");
}
.flash.error::before, .flash.info::before {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M12 6v8M12 17.6v.4'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M12 6v8M12 17.6v.4'/%3E%3C/svg%3E");
}
.flash .fl-x {
  margin-left: auto; flex: 0 0 auto; border: 0; background: none; cursor: pointer;
  color: inherit; opacity: .5; font-size: 18px; line-height: 1; padding: 0 2px;
}
.flash .fl-x:hover { opacity: 1; }


/* =========================================================================
   10 · BANNERS
   ========================================================================= */

/* Most of the forty-odd banners in the app are still the original shape —
   `<b>headline</b>` followed by a bare run of text, where app.css's
   `display:block` on the <b> is the only thing putting the two on separate
   lines. Turning .banner into a flex row would make that text a sibling flex
   item and stand it BESIDE its own headline, on every one of them.

   So the row layout is opted into by structure, not applied by name: a banner
   only becomes a flex row once it carries a .bn-body. Everything below the
   :has() rule is safe for both shapes. */
.banner { box-shadow: var(--sh-1); line-height: 1.55; }
.banner b { margin-bottom: 2px; }
.banner:has(.bn-body) { display: flex; gap: 12px; align-items: flex-start; }
.banner > svg { flex: 0 0 19px; width: 19px; height: 19px; margin-top: 1px; }
.banner .bn-body { flex: 1 1 auto; min-width: 0; }
.banner .bn-act { margin-left: auto; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.banner .bn-act form { margin: 0; }
.banner.info { background: var(--tint-blue-1); border-color: var(--tint-blue-11); color: var(--on-blue-3); }
@media screen and (max-width: 760px) {
  .banner:has(.bn-body) { flex-wrap: wrap; }
  .banner .bn-act { margin-left: 0; flex: 1 1 100%; }
  .banner .bn-act .btn { flex: 1 1 auto; justify-content: center; }
}


/* =========================================================================
   11 · TOPBAR / SHELL POLISH
   ========================================================================= */

/* The bar is sticky, so the page runs underneath it. Frosting it says so —
   and it keeps theme.css's spectrum bottom rule and its shadow, both of which
   this rule deliberately leaves alone. */
.topbar {
  background: var(--glass);
  -webkit-backdrop-filter: saturate(180%) blur(10px);
  backdrop-filter: saturate(180%) blur(10px);
}
.topbar h1 { letter-spacing: -.01em; }
.themebtn, .menu-btn { transition: background .15s var(--ease), color .15s var(--ease), border-color .15s var(--ease); }
.bellwrap { padding: 4px; border-radius: var(--r-sm); }
.bellwrap:hover { background: var(--sf-10); }

/* The sidebar scrollbar, which on Windows draws a light grey channel down
   the middle of a navy panel. */
.sidebar { scrollbar-width: thin; scrollbar-color: rgba(148, 163, 184, .35) transparent; }
.sidebar::-webkit-scrollbar { width: 8px; }
.sidebar::-webkit-scrollbar-track { background: transparent; }
.sidebar::-webkit-scrollbar-thumb { background: rgba(148, 163, 184, .3); border-radius: 8px; }
.sidebar::-webkit-scrollbar-thumb:hover { background: rgba(148, 163, 184, .5); }

/* Everything that scrolls inside the page gets the same slim bar. */
.table-wrap, .scroll-tbl, .dtree-wrap, .flt-panel, .ss-list, .rkbar, .drill-rows, .cotree {
  scrollbar-width: thin; scrollbar-color: var(--fill-2) transparent;
}
.table-wrap::-webkit-scrollbar, .scroll-tbl::-webkit-scrollbar,
.dtree-wrap::-webkit-scrollbar, .flt-panel::-webkit-scrollbar,
.ss-list::-webkit-scrollbar, .rkbar::-webkit-scrollbar,
.drill-rows::-webkit-scrollbar, .cotree::-webkit-scrollbar { width: 9px; height: 9px; }
.table-wrap::-webkit-scrollbar-thumb, .scroll-tbl::-webkit-scrollbar-thumb,
.dtree-wrap::-webkit-scrollbar-thumb, .flt-panel::-webkit-scrollbar-thumb,
.ss-list::-webkit-scrollbar-thumb, .rkbar::-webkit-scrollbar-thumb,
.drill-rows::-webkit-scrollbar-thumb, .cotree::-webkit-scrollbar-thumb {
  background: var(--fill-2); border-radius: 9px; border: 2px solid transparent; background-clip: content-box;
}


/* =========================================================================
   12 · KPI TILES
   ========================================================================= */

/* Same rule as the cards: theme.css draws the tile's left accent bar with
   `inset 3px 0 0 var(--ka)` in its box-shadow, and already has a hover shadow
   that keeps it. All that is missing is the lift. */
.kpi { transition: box-shadow .18s var(--ease), transform .18s var(--ease); }
a.kpi:hover, a.kpi-link:hover { transform: translateY(-2px); }
.k-value { letter-spacing: -.02em; }


/* =========================================================================
   13 · BACK TO TOP
   The register and the audit trail run to hundreds of rows.
   ========================================================================= */

.totop {
  position: fixed; right: 20px; bottom: 20px; z-index: 45;
  width: 42px; height: 42px; border-radius: 50%; cursor: pointer;
  display: none; align-items: center; justify-content: center;
  border: 1px solid var(--line); background: var(--card); color: var(--muted);
  box-shadow: var(--sh-3); font-size: 17px; line-height: 1;
  padding-bottom: 3px;
}
.totop.show { display: flex; animation: flash-in .2s var(--ease) both; }
.totop:hover { color: var(--blue-dark); border-color: var(--blue); }


/* =========================================================================
   14 · SMALL THINGS
   ========================================================================= */

/* A dotted underline that says "there is a tooltip on this". */
.hint { border-bottom: 1px dotted var(--bd-5); cursor: help; }

/* A key/value list that reads as a definition list rather than a grid of
   floating text. */
.dl .item {
  padding: 8px 11px; border: 1px solid var(--line); border-radius: 10px; background: var(--sf-2);
}
.dl .item .v { overflow-wrap: anywhere; }

/* Anything the user must not lose: a code, an id, a GST number. */
.mono { letter-spacing: -.01em; }

/* An inactive record is dimmed by an inline opacity in the views; give it a
   label too, because "faint" is not a status. */
.pill.off { background: var(--sf-10); color: var(--muted); border: 1px dashed var(--bd-5); }

/* How full something is: a bar and its figure on one line, with the figure
   coloured so "94%" is read as a warning without the bar having to be. The
   colour is a second reading of the same fact, never the only one. */
.fill { display: flex; align-items: center; gap: 8px; justify-content: flex-end; }
.fill-b {
  flex: 1 1 auto; min-width: 40px; height: 6px; border-radius: 4px;
  background: var(--sf-10); overflow: hidden;
}
.fill-b > span { display: block; height: 100%; border-radius: 4px; background: var(--fill-3); }
.fill > b { flex: 0 0 auto; font-variant-numeric: tabular-nums; font-size: 12px; }
.fill.good .fill-b > span { background: var(--green); }
.fill.warn .fill-b > span { background: var(--amber); }
.fill.bad  .fill-b > span { background: var(--red); }
.fill.warn > b { color: var(--amber-ink); }
.fill.bad  > b { color: var(--red-ink); font-weight: 800; }
@media screen and (max-width: 760px) {
  .fill { justify-content: flex-start; }
}

/* A numbered procedure — "how the rate is chosen", "before handover". The
   number is drawn as a chip rather than left to the browser's marker, which
   sits outside the box and does not line up with anything. */
ol.steps, ul.steps { list-style: none; margin: 0; padding: 0; counter-reset: step; }
ol.steps > li, ul.steps > li {
  position: relative; padding: 0 0 0 30px; margin-bottom: 9px;
  font-size: 12.5px; line-height: 1.6; color: var(--ink-1);
}
ol.steps > li:last-child, ul.steps > li:last-child { margin-bottom: 0; }
ol.steps > li::before {
  counter-increment: step; content: counter(step);
  position: absolute; left: 0; top: 1px;
  width: 20px; height: 20px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--blue-soft); color: var(--blue-dark);
  font-size: 10.5px; font-weight: 800;
}
ul.steps > li::before {
  content: ""; position: absolute; left: 6px; top: 8px;
  width: 7px; height: 7px; border-radius: 50%; background: var(--fill-3);
}
ol.steps.check > li::before { background: var(--green-soft); color: var(--on-green-2); }
ol.steps code, ul.steps code {
  background: var(--sf-9); border: 1px solid var(--bd-3); border-radius: 5px;
  padding: 1px 5px; font-family: var(--mono); font-size: 11.5px;
}

/* Section rules inside a long card. */
.hr { height: 1px; background: var(--line); border: 0; margin: 18px 0; }
.hr-t {
  display: flex; align-items: center; gap: 10px; margin: 20px 0 12px;
  font-size: 10.5px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
}
.hr-t::after { content: ""; flex: 1; height: 1px; background: var(--line); }

/* Focus, everywhere, once. */
a:focus-visible, button:focus-visible, summary:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--blue); outline-offset: 2px; border-radius: 4px;
}


/* =========================================================================
   14b · MASTERS HUB — the menu of destinations

   Ten masters were a seven-column table, which is the shape of a data set;
   this screen is a menu. Each master is a tile: what it is, how many rows it
   holds, and how much of that is live. The table is still there behind a
   fold for anyone who wants to read the counts down a column, and it is what
   prints.
   ========================================================================= */

.mgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(224px, 1fr)); gap: 12px; }

.mtile {
  display: grid; grid-template-columns: 40px 1fr; grid-auto-rows: min-content;
  gap: 4px 12px; align-content: start;
  padding: 13px 14px 11px; color: var(--ink); text-decoration: none;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--card);
  transition: transform .16s var(--ease), box-shadow .16s var(--ease), border-color .16s var(--ease);
}
.mtile:hover {
  text-decoration: none; transform: translateY(-2px);
  box-shadow: var(--sh-3); border-color: var(--bd-4);
}
.mtile > .k-ico { grid-row: span 2; width: 40px; height: 40px; flex: 0 0 40px; }
.mtile > .k-ico svg { width: 20px; height: 20px; }
.mt-b { min-width: 0; }
.mt-b b { display: block; font-size: 13.5px; font-weight: 800; line-height: 1.25; }
.mt-b i {
  display: block; font-style: normal; font-size: 11px; color: var(--muted);
  line-height: 1.4; margin-top: 2px;
}
.mt-n {
  grid-column: 2; display: flex; align-items: baseline; gap: 5px;
  font-size: 21px; font-weight: 800; font-variant-numeric: tabular-nums; letter-spacing: -.02em;
  margin-top: 6px;
}
.mt-n em { font-style: normal; font-size: 10px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); }
.mt-bar {
  grid-column: 1 / -1; display: block; height: 4px; border-radius: 3px;
  background: var(--sf-10); overflow: hidden; margin-top: 8px;
}
.mt-bar > span { display: block; height: 100%; border-radius: 3px; background: var(--green); }
.mt-f {
  grid-column: 1 / -1; display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 7px; font-size: 10.5px; font-variant-numeric: tabular-nums;
}
.mt-f b { font-weight: 700; }
.mt-f b.ok  { color: var(--on-green-2); }
.mt-f b.off { color: var(--muted); }
.mt-f em {
  margin-left: auto; font-style: normal; font-weight: 800; color: var(--muted);
  display: inline-flex; align-items: center; gap: 4px; letter-spacing: .04em; text-transform: uppercase;
}
.mt-f em svg { width: 12px; height: 12px; }
.mtile:hover .mt-f em { color: var(--blue-dark); }

/* The same list as a table, folded away. */
.mtable { margin-top: 14px; }
.mtable > summary {
  cursor: pointer; list-style: none; display: inline-flex; align-items: center; gap: 7px;
  font-size: 11.5px; font-weight: 700; color: var(--muted);
  padding: 5px 11px; border: 1px solid var(--line); border-radius: 999px; background: var(--sf-2);
}
.mtable > summary::-webkit-details-marker { display: none; }
.mtable > summary::before { content: "▸"; font-size: 10px; transition: transform .15s var(--ease); }
.mtable[open] > summary::before { transform: rotate(90deg); }
.mtable > summary:hover { color: var(--ink); border-color: var(--bd-5); }

/* ---- the Admin & System list in the right rail ---- */
.tool-list { display: flex; flex-direction: column; gap: 6px; }
.tool {
  display: flex; align-items: center; gap: 11px; padding: 9px 11px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--sf-1);
  color: var(--ink); text-decoration: none;
  transition: border-color .14s var(--ease), background .14s var(--ease), transform .14s var(--ease);
}
.tool:hover { text-decoration: none; border-color: var(--blue); background: var(--sf-2); transform: translateX(2px); }
.tool > span:nth-child(2) { flex: 1; min-width: 0; }
.tool b { display: block; font-size: 12.5px; font-weight: 700; }
.tool i { display: block; font-style: normal; font-size: 11px; color: var(--muted); line-height: 1.4; margin-top: 1px; }
.tool em { font-style: normal; font-size: 17px; color: var(--fill-3); font-weight: 700; }
.tool:hover em { color: var(--blue-dark); }

/* A chip in the page head that carries an icon. */
.pagehead .cchip svg { width: 13px; height: 13px; }


/* =========================================================================
   14c · AUDIT TRAIL

   A stage change is one fact — "it went from here to there" — so it is drawn
   as one thing rather than printed as two raw codes with an arrow between
   them. The person is an initial and a name, which is how the rest of the
   app shows a user.
   ========================================================================= */

.hop { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.hop > i { font-style: normal; color: var(--fill-3); font-size: 13px; line-height: 1; }

.who { display: inline-flex; align-items: center; gap: 7px; min-width: 0; }
.who-a {
  flex: 0 0 20px; width: 20px; height: 20px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--sf-10); color: var(--ink-2);
  font-size: 10px; font-weight: 800;
}


/* =========================================================================
   14d · DATA CLEANUP — the gate and the section bands

   The whole screen hangs off one rule: nothing runs until DELETE has been
   typed. The gate is the save bar wearing red, pinned to the bottom of the
   window so it is never below the buttons it unlocks.
   ========================================================================= */

.gate-l { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.gate-l label.f { margin: 0; white-space: nowrap; }
#gate.dirty { border-color: var(--edge-red-2); background: var(--red-soft); }
#gate.dirty .gate-l label.f { color: var(--on-red-2); }
#gate #confirmBox { border-color: var(--edge-red-1); }
#gate.dirty #confirmBox { border-color: var(--red); background: var(--sf-1); }

/* A band naming the section the rows under it belong to. */
table.data tr.section-row > td {
  background: var(--sf-9); font-weight: 800; letter-spacing: .07em;
  text-transform: uppercase; font-size: 10.5px; color: var(--ink-2);
  border-top: 1px solid var(--bd-4); padding: 7px 12px;
}

.preset { display: flex; flex-direction: column; box-shadow: var(--sh-1); }
.preset > .btn { margin-top: auto; align-self: flex-start; }
.preset.danger h3 { color: var(--on-red-2); }


/* =========================================================================
   14e · JUMP BAR
   A long screen's section headings, at the top, as links. Settings is four
   scrolls long and its six groups were only findable by scrolling past them.
   ========================================================================= */

.jump { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.jump a {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 12px 6px 7px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--sf-1); color: var(--ink); font-size: 12.5px; font-weight: 600;
  text-decoration: none; white-space: nowrap;
  transition: border-color .14s var(--ease), background .14s var(--ease), transform .14s var(--ease);
}
.jump a:hover { text-decoration: none; border-color: var(--blue); background: var(--sf-2); transform: translateY(-1px); }
.jump a .k-ico { width: 24px; height: 24px; flex: 0 0 24px; }
.jump a .k-ico svg { width: 13px; height: 13px; }
.jump a b {
  font-size: 10.5px; font-weight: 800; color: var(--muted);
  background: var(--sf-10); border-radius: 999px; padding: 1px 7px; font-variant-numeric: tabular-nums;
}


/* =========================================================================
   14f · THE PER-USER ACCESS PANEL (Users & Roles)

   Forty module checkboxes cannot live in a table cell, and they cannot live
   on a second page either — module access is edited while looking at the row
   it belongs to. So the cell holds a summary that says what the user has
   ("6 modules" / "Role default") and unfolds into the full editor over the
   rows below it.
   ========================================================================= */

details.acc > summary {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 10px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--sf-1); font-size: 11.5px; white-space: nowrap;
}
details.acc > summary::-webkit-details-marker { display: none; }
details.acc > summary:hover { border-color: var(--blue); background: var(--blue-soft); }
.acc-n { font-weight: 700; color: var(--ink-1); }
.acc-e { color: var(--link); font-weight: 700; text-decoration: underline; }
details.acc[open] > summary { border-color: var(--blue); background: var(--blue-soft); }

.acc-form {
  margin-top: 10px; padding: 14px; min-width: 420px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--sf-2); box-shadow: var(--sh-2);
}
.acc-form .checkrow label { background: var(--sf-1); font-size: 11.5px; padding: 4px 10px 4px 8px; }
.acc-form .hr-t { margin-top: 14px; }

@media screen and (max-width: 900px) {
  .acc-form { min-width: 0; }
}


/* =========================================================================
   14g · THE ADD / EDIT FORM ON A MASTER SCREEN

   It used to sit above the list and take a third of the screen whether or
   not anybody was adding anything — so the list, which is what people come
   to this screen to read, started below the fold. It is a <details> now:
   folded shut by default, opened by the "+ Add" button in the page head,
   and already open (and ringed, and focused) when the page arrives with
   ?edit=. No JavaScript is needed for it to open; ui.js only adds the
   scroll and the cursor.
   ========================================================================= */

details.formcard { padding: 0; overflow: hidden; }
details.formcard > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: 11px;
  padding: 14px 18px; background: var(--sf-2);
  transition: background .14s var(--ease);
}
details.formcard > summary::-webkit-details-marker { display: none; }
details.formcard > summary:hover { background: var(--sf-9); }
details.formcard[open] > summary { border-bottom: 1px solid var(--line); }
.fc-t { flex: 1 1 auto; min-width: 0; }
.fc-t b { display: block; font-size: 14px; font-weight: 800; }
.fc-t i { display: block; font-style: normal; font-size: 11.5px; color: var(--muted); margin-top: 1px; }
/* The twisty is drawn, so it lines up with the other folds in the app and
   turns rather than swapping glyph. */
.fc-c {
  flex: 0 0 auto; font-size: 17px; line-height: 1; color: var(--muted);
  transition: transform .18s var(--ease);
}
details.formcard[open] .fc-c { transform: rotate(90deg); color: var(--blue-dark); }
.fc-body { padding: 16px 18px 18px; }

/* Editing is not adding: the card says so, in the colour the rest of the
   app uses for "this is the row you chose". */
details.formcard.editing > summary { background: var(--blue-soft); }
details.formcard.editing .fc-t b { color: var(--on-blue-3); }

@media screen and (max-width: 760px) {
  details.formcard > summary { padding: 12px 13px; }
  .fc-body { padding: 13px 13px 15px; }
}


/* =========================================================================
   15 · PHONE
   Everything above is written desktop-first; these are the places where a
   thumb needs more room than a pointer.
   ========================================================================= */

@media screen and (max-width: 900px) {
  .pagehead { padding: 14px; gap: 12px; }
  .ph-txt h2 { font-size: 16px; }
  .ph-act { margin-left: 0; flex: 1 1 100%; }
  .ph-act .btn { flex: 1 1 auto; justify-content: center; }
  .savebar { border-radius: var(--r-md) var(--r-md) 0 0; margin-left: -16px; margin-right: -16px; padding-bottom: calc(12px + env(safe-area-inset-bottom)); }
  .savebar .btn { flex: 1 1 auto; justify-content: center; }
  .totop { right: 14px; bottom: calc(14px + env(safe-area-inset-bottom)); }
}

@media screen and (max-width: 760px) {
  .tsearch { flex: 1 1 100%; }
  .tsearch input[type=search] { padding-left: 38px; }
  .tsearch > svg { left: 13px; }
  .toolbar { gap: 8px; }
  .toolbar > .spacer { display: none; }
  .seg { width: 100%; }
  .seg > * { flex: 1 1 0; text-align: center; }

  /* Below 760px mobile.css has turned the row into a card, and the actions
     cell into a block. A 30px icon square in a card is unlabelled and barely
     hittable, so the cell becomes a wrapping row of real buttons: the label
     comes back and the icon is its prefix.

     The selector repeats mobile.css's own specificity because that is what it
     is arguing with — `table.data td.actions { display: table-cell }` above
     must NOT reach down here and put the card's buttons back in a cell. */
  table.data.rt td.rt-cell.actions {
    display: flex; flex-wrap: wrap; gap: 6px; white-space: normal;
  }
  table.data.rt td.rt-cell.actions::before { flex: 1 1 100%; }
  table.data.rt td.rt-cell.actions > * + * { margin-left: 0; }
  table.data.rt td.actions form { display: contents; }
  table.data.rt td.actions .ibtn {
    width: auto; flex: 1 1 42%; height: auto; min-height: 38px; padding: 0 12px;
    font: 700 12.5px var(--font);
  }
  table.data.rt td.actions .ibtn .ibtn-l { display: inline; }
  .empty { padding: 26px 14px; }
  .card-h { gap: 8px; }
}


/* =========================================================================
   16 · DARK
   Only the two things this file declares that are not already tokens.
   Shadows go from "a dark smudge under the card" to "the card is lighter
   than what is behind it", which is how depth reads on a dark ground.
   ========================================================================= */

:root[data-theme="dark"] {
  --sh-1: 0 1px 2px rgba(0, 0, 0, .5), inset 0 1px 0 rgba(255, 255, 255, .03);
  --sh-2: 0 4px 12px rgba(0, 0, 0, .55), inset 0 1px 0 rgba(255, 255, 255, .04);
  --sh-3: 0 14px 34px rgba(0, 0, 0, .62), inset 0 1px 0 rgba(255, 255, 255, .05);
  --sh-4: 0 -8px 24px rgba(0, 0, 0, .5);
  --glass: rgba(13, 20, 38, .82);
}
:root[data-theme="dark"] .switch input[type=checkbox]::after { background: #e2e8f0; }
:root[data-theme="dark"] .empty-ico { background: var(--sf-9); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --sh-1: 0 1px 2px rgba(0, 0, 0, .5), inset 0 1px 0 rgba(255, 255, 255, .03);
    --sh-2: 0 4px 12px rgba(0, 0, 0, .55), inset 0 1px 0 rgba(255, 255, 255, .04);
    --sh-3: 0 14px 34px rgba(0, 0, 0, .62), inset 0 1px 0 rgba(255, 255, 255, .05);
    --sh-4: 0 -8px 24px rgba(0, 0, 0, .5);
    --glass: rgba(13, 20, 38, .82);
  }
  :root:not([data-theme="light"]) .switch input[type=checkbox]::after { background: #e2e8f0; }
  :root:not([data-theme="light"]) .empty-ico { background: var(--sf-9); }
}


/* =========================================================================
   16b · THE LOGO

   One drawing, four sizes. Everything here is about giving the artwork a
   surface it can actually be seen on: the emblem is navy and blue, so on the
   navy rail and on the blue rake header it needs either a light tile under it
   or to be flattened to white. Both treatments live here so a replacement
   picture inherits them without being re-cut.
   ========================================================================= */

/* -- The rail. A white tile, because the logo's own ring is navy and all but
      disappears if it is laid straight on the navy — that was measured, not
      guessed. 46px rather than the 38 the old glyph used: the artwork is a
      drawing of a train, not a line icon, and below about 44 it turns to
      porridge. The file behind it is the lockup with the wordmark cropped
      away, for the same reason — the name is already spelled out in words an
      inch to the right, so the tile only has to carry the picture. -- */
.brand .logo.brandmark {
  width: 46px; height: 46px; flex: 0 0 46px;
  background: #fff;
  padding: 2px;
  overflow: hidden;
  box-shadow: 0 4px 14px rgba(15, 38, 92, .45), inset 0 0 0 1px rgba(255, 255, 255, .55);
  transition: transform .16s ease, box-shadow .16s ease;
}
.brand .logo.brandmark img { width: 100%; height: 100%; object-fit: contain; display: block; }
.brand .logo.brandmark:hover { transform: translateY(-1px); box-shadow: 0 6px 18px rgba(59, 130, 246, .5); }

/* -- The login card. The full lockup this time: it is the one screen with the
      room for it and the one screen where the name is the point. -- */
.login-card .brandfull {
  display: block; width: 172px; max-width: 64%; height: auto;
  margin: 0 auto 16px;
}
/* The card goes dark in dark mode and the artwork is navy on nothing, so it
   gets a light plate to sit on rather than being inverted — inverting would
   turn the blue stripe orange. */
:root[data-theme="dark"] .login-card .brandfull {
  background: #fff; border-radius: 14px; padding: 10px 14px;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .login-card .brandfull {
    background: #fff; border-radius: 14px; padding: 10px 14px;
  }
}

/* =========================================================================
   17 · PRINT & REDUCED MOTION
   ========================================================================= */

@media print {
  .pagehead, .card, .kpi { box-shadow: none !important; }
  .toolbar, .tsearch, .savebar, .totop, .flash .fl-x, .ph-act { display: none !important; }
  .pagehead::before { display: none; }
  .table-wrap.sticky { max-height: none; overflow: visible; }
  .topbar { background: #fff; }
}

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