/*
 * The app shell.
 *
 * Screen styles only — the individual screens are still plain markup and get
 * their own styling with the tasks that build them.
 */

:root {
    --navy: #12203a;
    --navy-deep: #0d1830;
    --navy-line: #24344f;
    --ink: #1b2436;
    --muted: #6b7789;
    --page: #f4f6f9;
    --card: #ffffff;
    --accent: #3d7dfb;
    --accent-soft: rgba(61, 125, 251, 0.16);
    --danger: #d5493f;

    --sidebar-width: 244px;
    --sidebar-collapsed: 68px;
    --radius: 8px;
}

* {
    box-sizing: border-box;
}

html,
body {
    margin: 0;
    padding: 0;
}

body {
    background: var(--page);
    color: var(--ink);
    font: 15px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    -webkit-font-smoothing: antialiased;
}

/* ---------- shell ---------- */

body.app {
    display: flex;
    min-height: 100vh;
}

.app-main {
    flex: 1;
    min-width: 0;
}

/* No width cap. Every screen uses the whole window.

   The tables are what drove this: the register, the catalogue and the
   goods-received grid all carry eight or nine columns, and a capped page
   squeezed them into a fraction of a wide monitor while leaving the rest
   empty. */
.app-main-inner {
    padding: 32px 36px 64px;
}

/* ---------- sidebar ---------- */

.sidebar {
    display: flex;
    flex-direction: column;
    width: var(--sidebar-width);
    flex: 0 0 var(--sidebar-width);
    min-height: 100vh;
    position: sticky;
    top: 0;
    background: var(--navy);
    color: #cfd8e6;
    transition: width 140ms ease, flex-basis 140ms ease;
}

.sidebar.is-collapsed {
    width: var(--sidebar-collapsed);
    flex-basis: var(--sidebar-collapsed);
}

/* One rule hides every text label, so collapsing needs no per-element care. */
.sidebar.is-collapsed .sidebar-label {
    display: none;
}

.sidebar.is-collapsed .sidebar-brand,
.sidebar.is-collapsed .nav-link,
.sidebar.is-collapsed .sidebar-toggle,
.sidebar.is-collapsed .user-chip {
    justify-content: center;
}

.sidebar.is-collapsed .sidebar-toggle svg {
    transform: rotate(180deg);
}

.sidebar-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 20px 16px;
    color: #fff;
    font-weight: 600;
    letter-spacing: 0.01em;
    text-decoration: none;
    border-bottom: 1px solid var(--navy-line);
}

.sidebar-brand-mark {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    flex: 0 0 30px;
    border-radius: 7px;
    background: var(--accent);
    color: #fff;
    font-weight: 700;
}

.sidebar-nav {
    flex: 1;
    overflow-y: auto;
    padding: 12px 8px;
}

.sidebar-nav ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

.nav-link {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 10px 12px;
    margin-bottom: 2px;
    border-radius: var(--radius);
    color: #cfd8e6;
    text-decoration: none;
    white-space: nowrap;
}

.nav-link:hover {
    background: rgba(255, 255, 255, 0.06);
    color: #fff;
}

.nav-link.is-active {
    background: var(--accent-soft);
    color: #fff;
    font-weight: 600;
    box-shadow: inset 3px 0 0 var(--accent);
}

/* Sections whose routes do not exist yet: visible, but plainly not usable. */
.nav-link.is-unavailable {
    color: #6f7d94;
    cursor: not-allowed;
}

.nav-link.is-unavailable:hover {
    background: none;
    color: #6f7d94;
}

.nav-icon {
    width: 19px;
    height: 19px;
    flex: 0 0 19px;
}

.nav-badge {
    margin-left: auto;
    min-width: 21px;
    padding: 1px 6px;
    border-radius: 11px;
    background: var(--accent);
    color: #fff;
    font-size: 12px;
    font-weight: 600;
    text-align: center;
}

/* ---------- sidebar foot ---------- */

.sidebar-foot {
    padding: 10px 8px 14px;
    border-top: 1px solid var(--navy-line);
    background: var(--navy-deep);
}

.sidebar-toggle {
    display: flex;
    align-items: center;
    gap: 11px;
    width: 100%;
    padding: 9px 12px;
    border: 0;
    border-radius: var(--radius);
    background: none;
    color: #9fb0c8;
    font: inherit;
    cursor: pointer;
}

.sidebar-toggle:hover {
    background: rgba(255, 255, 255, 0.06);
    color: #fff;
}

.user-chip {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    margin-top: 4px;
    border-radius: var(--radius);
    background: rgba(255, 255, 255, 0.04);
}

.user-avatar {
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    flex: 0 0 32px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    font-size: 13px;
    font-weight: 700;
}

.user-chip-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.user-name,
.user-role {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.user-name {
    color: #fff;
    font-weight: 600;
}

.user-role {
    color: #8d9db5;
    font-size: 12px;
}

.sidebar-signout {
    margin: 4px 0 0;
}

.sidebar-signout-button {
    width: 100%;
    padding: 8px 12px;
    border: 0;
    border-radius: var(--radius);
    background: none;
    color: #8d9db5;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.sidebar-signout-button:hover {
    background: rgba(255, 255, 255, 0.06);
    color: #fff;
}

/* ---------- mobile ---------- */

/*
 * Under 900px the sidebar is always icons-only and the toggle is hidden.
 * The stored preference is left alone, so a desktop that was expanded stays
 * expanded when the window grows back.
 */
@media (max-width: 900px) {
    .sidebar,
    .sidebar.is-collapsed {
        width: var(--sidebar-collapsed);
        flex-basis: var(--sidebar-collapsed);
    }

    .sidebar .sidebar-label {
        display: none;
    }

    .sidebar .sidebar-brand,
    .sidebar .nav-link,
    .sidebar .user-chip {
        justify-content: center;
    }

    .sidebar-toggle,
    .sidebar-signout {
        display: none;
    }

    .app-main-inner {
        padding: 24px 20px 48px;
    }
}

/* ---------- page furniture ---------- */

h1 {
    margin: 0 0 20px;
    font-size: 24px;
    letter-spacing: -0.01em;
}

a {
    color: var(--accent);
}

table {
    width: 100%;
    border-collapse: collapse;
    background: var(--card);
    border-radius: var(--radius);
    overflow: hidden;
    box-shadow: 0 1px 2px rgba(16, 24, 40, 0.06);
}

/* The item list carries nine columns since the last-issue figures went on it.
   Squeezing those into the page width wraps item names onto three lines and
   makes the numbers hard to read across, so it scrolls sideways on its own —
   the same treatment .receive-table gets, and for the same reason. */
.item-table {
    display: block;
    overflow-x: auto;
    white-space: nowrap;
}

th,
td {
    padding: 11px 14px;
    text-align: left;
    border-bottom: 1px solid #e7ebf1;
}

th {
    background: #fafbfd;
    color: var(--muted);
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

tr:last-child td {
    border-bottom: 0;
}

td form {
    display: inline;
}

button,
input[type="text"],
input[type="email"],
input[type="password"] {
    font: inherit;
}

input[type="text"],
input[type="email"],
input[type="password"] {
    padding: 9px 11px;
    border: 1px solid #d3dae4;
    border-radius: var(--radius);
    background: #fff;
}

button {
    padding: 8px 14px;
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: var(--accent);
    color: #fff;
    cursor: pointer;
}

td button {
    padding: 5px 10px;
    background: #fff;
    border-color: #d3dae4;
    color: var(--ink);
    font-size: 13px;
}

.placeholder {
    padding: 22px 24px;
    border: 1px dashed #c8d1de;
    border-radius: var(--radius);
    background: var(--card);
    color: var(--muted);
}

/* ---------- sign-in ---------- */

body.bare {
    display: grid;
    place-items: center;
    min-height: 100vh;
    background: var(--navy);
}

.signin {
    width: 340px;
    padding: 30px;
    border-radius: 12px;
    background: var(--card);
    box-shadow: 0 18px 40px rgba(8, 15, 30, 0.35);
}

.signin h1 {
    margin-bottom: 6px;
    font-size: 20px;
}

.signin p.hint {
    margin: 0 0 22px;
    color: var(--muted);
    font-size: 13px;
}

.signin label {
    display: block;
    margin-bottom: 5px;
    font-size: 13px;
    font-weight: 600;
}

.signin input {
    width: 100%;
    margin-bottom: 15px;
}

.signin button {
    width: 100%;
    padding: 10px;
    font-weight: 600;
}

.signin .error {
    margin: 0 0 16px;
    padding: 9px 12px;
    border-radius: var(--radius);
    background: #fdecea;
    color: var(--danger);
    font-size: 13px;
}

/* ---------- catalogue ---------- */

.page-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
}

.button-link {
    padding: 8px 14px;
    border-radius: var(--radius);
    background: var(--accent);
    color: #fff;
    font-size: 14px;
    text-decoration: none;
}

.filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-bottom: 18px;
}

/* Direct children only.
 *
 * A report's filter panel carries both .filters and .filters-report, so an
 * unscoped rule here reaches into its fields — and this one did real damage:
 * `flex: 1 1 220px` on a control inside .filter-field, which is a *column*
 * flexbox, makes 220px a height rather than a width. The item search box on
 * every report was 200px tall and empty, and because a grid row is as tall as
 * its tallest cell it dragged the whole panel down with it. */
.filters > input[type="search"] {
    flex: 1 1 220px;
    min-width: 180px;
    padding: 9px 11px;
    border: 1px solid #d3dae4;
    border-radius: var(--radius);
    font: inherit;
}

.filters > select {
    padding: 9px 10px;
    border: 1px solid #d3dae4;
    border-radius: var(--radius);
    background: #fff;
    font: inherit;
}

.clear-filters {
    font-size: 14px;
}

/* ---------- a report's filter panel ---------- */

/* The catalogue's filter bar is a row of unlabelled dropdowns, which works
   when there are four of them and each is self-evident. A report has nine,
   two of them dates, and "11/07/2026" beside "11/08/2026" with no labels is
   unreadable — so this is a labelled grid on a card instead. */
.filters-report {
    display: grid;
    /* Narrower columns fit more per row, so a six-filter report is one band
       across the top rather than a panel filling the screen above the thing
       somebody opened the page to read.
       The row is as tall as its tallest cell, so height is won by stopping
       any one cell wrapping — see .filter-field-check below. */
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    /* start, not end. With `end` a cell that grows for any reason pushes its
       whole row down and leaves a band of empty space above every field
       beside it — which is exactly what the item search did. Aligning to the
       top means a tall cell hangs below rather than shoving everything down. */
    align-items: start;
    gap: 8px 12px;
    margin: 0 0 14px;
    padding: 10px 12px;
    border: 1px solid #e2e7ee;
    border-radius: var(--radius);
    background: var(--card);
}

.filter-field {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.filter-field > label {
    /* A fixed line box, so a one-word label and a two-word label put their
       inputs on the same line. */
    height: 15px;
    overflow: hidden;
    color: var(--muted);
    font-size: 11px;
    font-weight: 600;
    line-height: 15px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    white-space: nowrap;
}

.filter-field input[type="date"],
.filter-field input[type="search"],
.filter-field select {
    /* An explicit height, not a padding that happens to add up to one. Every
       control in this panel is the same height whatever else is going on
       around it, so no single field can set the height of its row. */
    box-sizing: border-box;
    /* flex: none so no rule from elsewhere can turn a flex-basis into a
       height in this column layout. */
    flex: none;
    width: 100%;
    height: 32px;
    padding: 0 8px;
    border: 1px solid #d3dae4;
    border-radius: var(--radius);
    background: #fff;
    font: inherit;
    font-size: 13px;
}

/* The item search needs room for a code and a name, and its results panel
   drops out underneath — absolutely, so a list of matches never pushes the
   rest of the form around. */
.filter-field-wide {
    grid-column: span 2;
    position: relative;
    /* Nothing in here may grow the cell. The picker is out of flow and the
       "narrowed to one item" line only appears once something is chosen. */
    align-self: start;
}

.filter-field-wide .picker {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 5;
}

/* Two columns wide, because its label is a sentence rather than a word. In
   one column the label wrapped to two lines and the hint took a third, and
   since a grid row is as tall as its tallest cell that one checkbox was
   setting the height of everything beside it. */
.filter-field-check {
    grid-column: span 2;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px;
    /* Pushed down past where the labels sit, so the tick lines up with the
       inputs beside it rather than floating above them. */
    padding-top: 17px;
}

.filter-field-check > label {
    text-transform: none;
    letter-spacing: 0;
    font-size: 14px;
    font-weight: 400;
    color: var(--ink);
}

.filter-field-check .hint {
    flex-basis: 100%;
    margin: 0;
    line-height: 1.35;
}

.filter-field-actions {
    flex-direction: row;
    align-items: center;
    gap: 10px;
    padding-top: 17px;
}

.filters-report button {
    height: 32px;
    padding: 0 14px;
    font-size: 13px;
}

.filters-report .filter-field-actions a {
    font-size: 13px;
}

/* On a narrow screen the two-column span is a promise nothing can keep. */
@media (max-width: 700px) {
    .filter-field-check,
    .filter-field-wide {
        grid-column: span 1;
    }
}

/* A control that reads as a link because it does what a link does, but has to
   be a button because it changes the form rather than going anywhere. */
.link-button {
    padding: 0;
    border: 0;
    background: none;
    color: var(--accent);
    font: inherit;
    text-decoration: underline;
    cursor: pointer;
}

td.code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 13px;
}

th.numeric,
td.numeric {
    text-align: right;
    white-space: nowrap;
}

.badge {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 6px;
    border-radius: 4px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.03em;
    vertical-align: middle;
}

.badge-gik {
    background: #efe6ff;
    color: #6435c9;
}

/*
 * Status is read at a glance down the column, so each band carries its own
 * colour rather than relying on the words alone.
 */
.pill {
    display: inline-block;
    padding: 2px 9px;
    border-radius: 11px;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
}

.pill-in-stock {
    background: #e3f5ea;
    color: #1c7a45;
}

.pill-reorder {
    background: #fff3d9;
    color: #8a5d00;
}

.pill-low {
    background: #ffe9e5;
    color: #b23a2b;
}

.pill-out {
    background: #fbe3e0;
    color: #8f221a;
}

.pager {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-top: 16px;
    color: var(--muted);
    font-size: 14px;
}

/* ---------- record form ---------- */

/* Forms fill the window too.
   The width is used inside each fieldset rather than by putting fieldsets
   side by side. Side by side looks tidier and is a trap: a fieldset holding a
   six-column line-items table gets a third of the page, scrolls sideways, and
   the columns that fall off the end are the prices — so the buyer opens the
   form to type a price and cannot see the box.

   Laying the fields out within a full-width fieldset cannot hide anything,
   because nothing is ever narrower than the page. */
.record-form {
    max-width: 100%;
}

@media (min-width: 1000px) {
    .record-form fieldset {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
        gap: 0 24px;
        align-items: start;
    }

    /* Everything that is not a single labelled box takes the whole row: the
       legend, any table, the pickers and the notes that explain the fieldset
       rather than one field in it. */
    .record-form fieldset > legend,
    .record-form fieldset > table,
    .record-form fieldset > .picker,
    .record-form fieldset > .hint,
    .record-form fieldset > .placeholder,
    .record-form fieldset > .field-error,
    .record-form fieldset > .field:has(textarea),
    .record-form fieldset > .field:has(table) {
        grid-column: 1 / -1;
    }
}

.record-form fieldset {
    margin: 0 0 22px;
    padding: 18px 20px;
    border: 1px solid #e2e7ee;
    border-radius: var(--radius);
    background: var(--card);
}

/* A legend sits *on* the fieldset's top border by default, so the border line
   runs straight through the words. That is the browser's doing, not a choice
   anybody made here.

   Floating it takes it out of that special position — the browser only treats
   the first non-floated legend as the one to hang on the border — so it
   becomes ordinary content at the top of the card, with the border unbroken
   behind it. */
.record-form legend {
    float: left;
    width: 100%;
    margin: 0 0 16px;
    padding: 0 0 10px;
    border-bottom: 1px solid #eceff4;
    color: var(--muted);
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

/* The float has to be cleared, or the fields sit beside the title instead of
   under it on narrow screens. */
.record-form fieldset::after {
    content: '';
    display: block;
    clear: both;
}

.field {
    margin-bottom: 14px;
}

.field:last-child {
    margin-bottom: 0;
}

.field label {
    display: block;
    margin-bottom: 5px;
    font-size: 13px;
    font-weight: 600;
}

.field input,
.field select,
.field textarea {
    width: 100%;
    padding: 9px 11px;
    border: 1px solid #d3dae4;
    border-radius: var(--radius);
    background: #fff;
    font: inherit;
}

.field-check {
    display: flex;
    align-items: center;
    gap: 9px;
}

.field-check input {
    width: auto;
}

.field-check label {
    margin: 0;
    font-weight: 400;
}

.field .hint {
    margin: 5px 0 0;
    color: var(--muted);
    font-size: 12px;
}

.form-actions {
    display: flex;
    align-items: center;
    gap: 14px;
}

/* ---------- stock card ---------- */

.breadcrumb {
    margin: 0 0 10px;
    font-size: 14px;
}

.subtitle {
    margin: 0;
    color: var(--muted);
    font-size: 14px;
}

/* Small print, and it has to look like small print.
 *
 * There was no base rule for this until now: a hint inside a form got one,
 * and a hint anywhere else rendered at full body size in body colour. On a
 * report screen — where most of the explaining happens — that meant three
 * paragraphs of ordinary text competing with the table they were about.
 */
.hint {
    margin: 4px 0 0;
    color: var(--muted);
    font-size: 12px;
    line-height: 1.45;
}

/* Hints are a second line under something, not a phrase beside it. */
span.hint {
    display: block;
}

.page-actions {
    display: flex;
    gap: 8px;
    flex-shrink: 0;
}

.button-secondary {
    background: #fff;
    border: 1px solid #d3dae4;
    color: var(--ink);
}

.badge-large {
    margin-left: 8px;
    padding: 3px 9px;
    font-size: 12px;
    vertical-align: middle;
}

/* The banner repeats what the status pill says, in words, where it cannot be
   missed. Only Low and Out raise one. */
.banner {
    margin: 4px 0 20px;
    padding: 12px 16px;
    border-radius: var(--radius);
    border-left: 4px solid;
}

.banner-low {
    background: #ffe9e5;
    border-color: #d5493f;
    color: #8f2b20;
}

.banner-out {
    background: #fbe3e0;
    border-color: #a52a1e;
    color: #7d1d14;
}

/* A draft order. Not a problem — just not sent yet. */
.banner-draft {
    background: #f4f6f9;
    border-color: #8a94a6;
    color: #4b5468;
}

.stock-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 16px;
}

/* Sized to be read, not to fill the width. Three figures stretched across a
   wide screen turned a summary into a billboard and pushed the table — the
   thing somebody opened the page for — below the fold. */
.stat {
    flex: 0 1 auto;
    min-width: 120px;
    padding: 8px 14px;
    border: 1px solid #e2e7ee;
    border-radius: var(--radius);
    background: var(--card);
}

.stat-label {
    display: block;
    margin-bottom: 1px;
    color: var(--muted);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.stat-value {
    display: block;
    font-size: 19px;
    font-weight: 600;
    letter-spacing: -0.01em;
}

.stat-value small {
    color: var(--muted);
    font-size: 14px;
    font-weight: 400;
}

h2 {
    margin: 30px 0 12px;
    font-size: 17px;
}

.detail {
    margin: 0;
    padding: 4px 20px;
    border: 1px solid #e2e7ee;
    border-radius: var(--radius);
    background: var(--card);
}

.detail > div {
    display: flex;
    gap: 16px;
    padding: 9px 0;
    border-bottom: 1px solid #eef1f5;
}

.detail > div:last-child {
    border-bottom: 0;
}

.detail dt {
    flex: 0 0 190px;
    color: var(--muted);
    font-size: 14px;
}

.detail dd {
    margin: 0;
    min-width: 0;
    word-break: break-word;
}

@media (max-width: 640px) {
    .detail > div {
        flex-direction: column;
        gap: 2px;
    }

    .detail dt {
        flex-basis: auto;
    }
}

/* ---------- flash messages ---------- */

.flash {
    margin: 0 0 20px;
    padding: 12px 16px;
    border-radius: var(--radius);
    border-left: 4px solid;
}

.flash-success {
    background: #e3f5ea;
    border-color: #1c7a45;
    color: #146034;
}

/* ---------- requisition forms ---------- */

/* Alpine sets x-cloak on elements that must not flash up before it starts. */
[x-cloak] {
    display: none !important;
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

.picker {
    margin-top: 6px;
    border: 1px solid #d3dae4;
    border-radius: var(--radius);
    background: #fff;
    overflow: hidden;
    box-shadow: 0 6px 18px rgba(16, 24, 40, 0.1);
}

.picker-option {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 9px 12px;
    border: 0;
    border-bottom: 1px solid #eef1f5;
    border-radius: 0;
    background: none;
    color: var(--ink);
    text-align: left;
    cursor: pointer;
}

.picker-option:last-child {
    border-bottom: 0;
}

.picker-option:hover {
    background: #f4f7fc;
}

.picker-code {
    flex: 0 0 110px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 13px;
    color: var(--muted);
}

.picker-name {
    flex: 1;
}

.picker-stock {
    flex: 0 0 auto;
}

.record-form td input.qty,
.record-form td input.cost {
    width: 110px;
    text-align: right;
}

.record-form td input.unit {
    width: 110px;
}

.record-form td .hint {
    margin-top: 3px;
    color: #b23a2b;
}

.field-error {
    margin: 10px 0 0;
    color: var(--danger);
    font-size: 14px;
}

.add-line {
    margin: 14px 0 0;
}

.form-actions .button-secondary {
    background: #fff;
    border: 1px solid #d3dae4;
    color: var(--ink);
}

/* ---------- approvals inbox ---------- */

.flash-warning {
    background: #fff3d9;
    border-color: #8a5d00;
    color: #6b4800;
}

.date-filter {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 14px;
    color: var(--muted);
}

.date-filter input {
    padding: 8px 10px;
    border: 1px solid #d3dae4;
    border-radius: var(--radius);
    font: inherit;
}

th.tick,
td.tick {
    width: 34px;
}

/* The two kinds are told apart at a glance, not by reading the words. */
.pill-type-requisition {
    background: #e6efff;
    color: #2a52a0;
}

.pill-type-purchase-request {
    background: #f3e8ff;
    color: #6435c9;
}

.badge-own {
    background: #eceff4;
    color: var(--muted);
}

td.decide {
    white-space: nowrap;
}

td.decide .hint {
    color: var(--muted);
    font-size: 13px;
}

button.approve-one {
    background: #1c7a45;
    color: #fff;
    border-color: transparent;
}

.link-button {
    padding: 0;
    border: 0;
    background: none;
    color: var(--accent);
    font: inherit;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 13px;
    text-decoration: underline;
    cursor: pointer;
}

.bulk-bar {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
    padding: 10px 14px;
    border-radius: var(--radius);
    background: var(--accent-soft);
}

.hidden-form {
    display: none;
}

/* ---------- drawer and dialog ---------- */

.drawer-backdrop {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: flex;
    background: rgba(11, 18, 32, 0.45);
}

.drawer {
    margin-left: auto;
    width: min(440px, 100%);
    height: 100%;
    padding: 26px 28px;
    overflow-y: auto;
    background: var(--card);
    box-shadow: -12px 0 32px rgba(8, 15, 30, 0.25);
}

.dialog {
    margin: auto;
    width: min(480px, calc(100% - 32px));
    padding: 24px 26px;
    border-radius: 12px;
    background: var(--card);
    box-shadow: 0 20px 44px rgba(8, 15, 30, 0.35);
}

.dialog h2,
.drawer h2 {
    margin-top: 0;
}

.dialog textarea {
    width: 100%;
    padding: 9px 11px;
    border: 1px solid #d3dae4;
    border-radius: var(--radius);
    font: inherit;
}

button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* ---------- request status pills ---------- */

.pill-status-draft {
    background: #eceff4;
    color: #56637a;
}

.pill-status-submitted {
    background: #fff3d9;
    color: #8a5d00;
}

.pill-status-approved,
.pill-status-fulfilled {
    background: #e3f5ea;
    color: #1c7a45;
}

.pill-status-partially-issued {
    background: #e6efff;
    color: #2a52a0;
}

.pill-status-po-created {
    background: #f3e8ff;
    color: #6435c9;
}

.pill-status-rejected {
    background: #ffe9e5;
    color: #b23a2b;
}

.pill-status-cancelled {
    background: #eceff4;
    color: #8a94a6;
}

/* ---------- purchase order status pills ---------- */

.pill-status-issued {
    background: #e6efff;
    color: #2a52a0;
}

.pill-status-partially-delivered {
    background: #fff3d9;
    color: #8a5d00;
}

.pill-status-delivered {
    background: #e3f5ea;
    color: #1c7a45;
}

.pill-status-closed {
    background: #eceff4;
    color: #56637a;
}

/* ---------- which side of the house ---------- */

/* RIFI and STIA keep separate stores and separate books. The pill appears on
   every row and every document so nobody has to open one to find out whose it
   is. Deliberately different in weight from the status pills beside it — this
   answers "whose", not "how far along". */
.pill-org-rifi {
    background: #e8eef7;
    color: #33455f;
}

.pill-org-stia {
    background: #e3f5ea;
    color: #1c7a45;
}

/* ---------- the two big buttons on a dashboard ---------- */

/* Somebody opens their dashboard to do one of two things, so those two are
   the largest elements on the page rather than links in a row. */
.action-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 16px;
    margin: 0 0 28px;
}

.action-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 20px 22px;
    border: 1px solid #e2e7ee;
    border-left: 4px solid var(--accent);
    border-radius: var(--radius);
    background: var(--card);
    box-shadow: 0 1px 2px rgba(16, 24, 40, 0.06);
    color: inherit;
    text-decoration: none;
}

.action-card:hover {
    border-color: var(--accent);
    box-shadow: 0 2px 10px rgba(16, 24, 40, 0.09);
}

.action-card-title {
    font-size: 17px;
    font-weight: 600;
    color: var(--accent);
}

.action-card-note {
    color: var(--muted);
}

/* A report whose task has not landed. Shown so the reader knows it is coming,
   greyed and unclickable so nobody hunts for the link that is not there. */
.action-card-unavailable {
    border-left-color: #d7dde6;
    box-shadow: none;
}

.action-card-unavailable .action-card-title {
    color: var(--muted);
}

/* ---------- when a request is refused ---------- */

/* Deliberately calm. Whatever happened, the person reading this did not do
   anything wrong, and a page shouting at them does not help. */
.problem {
    max-width: 34rem;
    margin: 48px auto;
    padding: 32px;
    border-radius: var(--radius);
    background: var(--card);
    box-shadow: 0 1px 2px rgba(16, 24, 40, 0.06);
    text-align: center;
}

.problem-status {
    margin: 0 0 4px;
    color: var(--muted);
    font-size: 44px;
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.02em;
}

.problem h1 {
    margin-bottom: 12px;
}

.problem-explanation {
    margin: 0 0 6px;
}

.problem-what-to-do {
    margin: 0 0 24px;
    color: var(--muted);
}

.problem .form-actions {
    justify-content: center;
}

body.bare .problem {
    margin-top: 12vh;
}

/* ---------- deliveries ---------- */

.pill-type-procurement {
    background: #e6efff;
    color: #2a52a0;
}

.pill-type-donation {
    background: #f3e8ff;
    color: #6435c9;
}

.pill-condition-good {
    background: #e3f5ea;
    color: #1c7a45;
}

.pill-condition-damaged {
    background: #fff3d9;
    color: #8a5d00;
}

.pill-condition-rejected {
    background: #ffe9e5;
    color: #b23a2b;
}

/* ---------- how a request turned out ---------- */

/* One vocabulary for two aggregates — see App\Enum\RequestOutcome. The
   colours run cool for "nothing needed from you" and warm for "somebody is
   waiting", so a column of these reads as a workload at a glance. */
.pill-outcome-draft {
    background: #eceff4;
    color: #56637a;
}

.pill-outcome-waiting {
    background: #fff3d9;
    color: #8a5d00;
}

.pill-outcome-approved {
    background: #e8eef7;
    color: #33455f;
}

.pill-outcome-rejected {
    background: #ffe9e5;
    color: #b23a2b;
}

.pill-outcome-completed {
    background: #e3f5ea;
    color: #1c7a45;
}

.pill-outcome-cancelled {
    background: #eceff4;
    color: #56637a;
}

/* A summary that breaks figures down by side and then totals them. Narrower
   than a data table because every cell after the first is a count. */
.summary-table {
    margin-bottom: 22px;
}

.summary-table th {
    white-space: nowrap;
}

.summary-table tr.grand th,
.summary-table tr.grand td {
    border-top: 2px solid var(--ink);
    font-weight: 600;
    background: #fafbfd;
}

/* ---------- the asset register ---------- */

/* Equipment condition shares "good" with a delivery line, which is right —
   both mean the same thing to whoever is looking at the object. The rest are
   the asset register's own. */
.pill-condition-fair {
    background: #fff3d9;
    color: #8a5d00;
}

.pill-condition-poor {
    background: #ffe9e5;
    color: #b23a2b;
}

.pill-condition-under-repair {
    background: #eceff4;
    color: #56637a;
}

/* Insurance runs on a traffic light. "Not insured" is deliberately grey and
   not a warning: plenty of equipment is never insured, and colouring all of it
   red would train people to ignore the ones that matter. */
.pill-insurance-ok {
    background: #e3f5ea;
    color: #1c7a45;
}

.pill-insurance-soon {
    background: #fff3d9;
    color: #8a5d00;
}

.pill-insurance-expired {
    background: #ffe9e5;
    color: #b23a2b;
}

.pill-insurance-not-tracked {
    background: #eceff4;
    color: #56637a;
}

/* The six things that can happen to a piece of equipment. Each opens its own
   small form below the row of buttons, so the page never carries six forms at
   once and nobody posts the wrong one. */
.asset-actions {
    margin: 0 0 28px;
}

.asset-actions > .form-actions {
    flex-wrap: wrap;
}

.asset-actions .record-form {
    margin-top: 16px;
    padding: 18px 20px;
    border: 1px solid #e2e7ee;
    border-left: 4px solid var(--accent);
    border-radius: var(--radius);
    background: #fbfcfe;
}

/* The goods-received grid is wide — seven columns of numbers and pickers —
   so it scrolls on its own rather than pushing the page sideways. */
.receive-table {
    display: block;
    overflow-x: auto;
    white-space: nowrap;
}

/* A line with nothing left owed on it. Still shown, because a store keeper
   checking a pallet needs the whole order in front of them. */
.receive-table .is-complete {
    color: var(--muted);
}

.receive-table .is-complete input,
.receive-table .is-complete select {
    background: #f4f6f9;
}

/* The item picker sits inside a table cell, so it needs its own room. */
.field-inline {
    margin-top: 6px;
    white-space: normal;
}

.field-inline .item-search {
    width: 100%;
    min-width: 180px;
}

/* ---------- what an order comes to ---------- */

/* Right-aligned against the lines table above it, the way a printed order
   reads. */
.totals {
    width: auto;
    min-width: 280px;
    margin: 0 0 24px auto;
}

.totals th {
    text-align: left;
    font-weight: 400;
    color: var(--muted);
}

.totals .grand th,
.totals .grand td {
    border-top: 2px solid var(--ink);
    font-weight: 600;
    font-size: 1.05rem;
}

/* A field the form shows but does not let you change — the supplier on an
   order that already exists. */
.static-value {
    margin: 0;
    padding: 9px 0;
    font-weight: 600;
}

/* Cancelling an order is not undoable, so the button does not look like the
   ones beside it. */
.button-danger {
    background: #b23a2b;
}

.button-danger:hover {
    background: #932e21;
}

/* ---------- what has happened ---------- */

.timeline {
    margin: 0 0 20px;
    padding: 4px 20px;
    list-style: none;
    border: 1px solid #e2e7ee;
    border-radius: var(--radius);
    background: var(--card);
}

.timeline li {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 18px;
    padding: 11px 0;
    border-bottom: 1px solid #eef1f5;
}

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

.timeline-what {
    flex: 0 0 170px;
    font-weight: 600;
}

.timeline-who {
    flex: 1;
    min-width: 120px;
}

.timeline-when {
    color: var(--muted);
    font-size: 14px;
}

.timeline-approved .timeline-what {
    color: #1c7a45;
}

.timeline-rejected .timeline-what {
    color: #b23a2b;
}

@media (max-width: 640px) {
    .timeline-what {
        flex-basis: 100%;
    }
}

/* ---------- required fields ---------- */

/*
 * The asterisk is decoration; the `required` attribute on the input is what
 * actually tells a screen reader the field is needed. The title gives a
 * sighted person the word if the symbol is not obvious.
 */
.req {
    color: var(--danger);
    font-weight: 700;
    margin-left: 2px;
}

.required-note {
    margin: 0 0 16px;
    color: var(--muted);
    font-size: 13px;
}

/* ---------- a refused form ---------- */

.flash-error {
    background: #ffe9e5;
    border-color: var(--danger);
    color: #8f2b20;
}

/* ---------- two halves of one report ---------- */

/* By donor and by programme are the same screen asking a different question,
   so they are two panels at the top rather than an entry in the filter row.
   A dropdown would have buried the choice among five others that only narrow
   the results; this one changes what the report is. */
.mode-switch {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 12px;
    margin: 0 0 20px;
}

.mode-switch-option {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 14px 16px;
    background: var(--card);
    border: 1px solid #dde2ea;
    border-radius: var(--radius);
    text-decoration: none;
    color: var(--ink);
}

.mode-switch-option span {
    color: var(--muted);
    font-size: 13px;
}

.mode-switch-option:hover {
    border-color: var(--accent);
}

.mode-switch-option.is-active {
    border-color: var(--accent);
    background: var(--accent-soft);
}

.mode-switch-option.is-active span {
    color: var(--ink);
}

/* ---------- one donor, or one programme ---------- */

/* Each group is its own block with its own total, because the reader is
   almost always looking for one of them and printing that page alone. */
.attribution-group {
    margin: 0 0 28px;
}

.attribution-group h2 {
    margin: 0;
    font-size: 17px;
}

/* Records that should not exist. Listed one per line rather than run
   together, since each is a separate thing somebody has to go and fix. */
.fault-list {
    margin: 6px 0 0;
    padding-left: 20px;
}

.fault-list li {
    margin: 2px 0;
}

/* ---------- where a line came from ---------- */

/* Six kinds across the two halves. Goods coming in are green, money going out
   is blue, equipment is purple — so a reader scanning the column sees the
   shape of a donor's giving without reading every word. */
.pill-kind-opening-stock,
.pill-kind-donation {
    background: #e3f5ea;
    color: #1c7a45;
}

.pill-kind-stock-issued,
.pill-kind-purchased {
    background: #e8eef7;
    color: #33455f;
}

.pill-kind-equipment-received,
.pill-kind-equipment-bought {
    background: #efe6ff;
    color: #6435c9;
}

/* ---------- text that is there but not the point ---------- */

/* Used all over for a secondary line inside a cell — a phone number under a
   contact name, "never" under a date. Was in the markup before it was in
   here, which is why it is defined this late. */
.muted {
    color: var(--muted);
}

.plain-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.plain-list li {
    color: var(--muted);
    font-size: 13px;
}

/* ---------- an account that has been switched off ---------- */

/* Faded rather than hidden. There are a couple of dozen accounts here, and
   "who used to work here" is a question the list should answer without
   anybody having to find a filter first. */
.row-inactive td {
    color: var(--muted);
}

.row-inactive td:first-child {
    font-style: italic;
}

/* ---------- moving between the six lookup lists ---------- */

.tab-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin: 0 0 20px;
    border-bottom: 1px solid #dde2ea;
}

.tab {
    padding: 8px 14px;
    color: var(--muted);
    text-decoration: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
}

.tab:hover {
    color: var(--ink);
}

.tab.is-active {
    color: var(--ink);
    font-weight: 600;
    border-bottom-color: var(--accent);
}

/* ---------- the actions that are not part of the form ---------- */

/* Resetting a password and switching an account off are not edits, so they
   are not fields. Below the form, each with its own button and its own
   sentence saying what will happen. */
.danger-zone {
    margin: 32px 0 0;
    padding: 20px 22px;
    background: var(--card);
    border: 1px solid #dde2ea;
    border-radius: var(--radius);
}

.danger-zone h2 {
    margin: 0 0 4px;
    font-size: 17px;
}

.danger-action {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    align-items: flex-start;
    justify-content: space-between;
    padding: 16px 0;
    border-top: 1px solid #eef1f5;
}

.danger-action > div {
    flex: 1 1 340px;
}

.danger-action .hint {
    margin: 4px 0 0;
}

/* ---------- a password, shown once ---------- */

/* Large and spaced out on purpose. Somebody is reading this down a phone or
   copying it onto a sticky note, and a password in ordinary body text is one
   that gets read out wrong. */
.credential-card {
    max-width: 460px;
    margin: 0 0 20px;
    padding: 20px 24px;
    background: var(--card);
    border: 1px solid var(--accent);
    border-radius: var(--radius);
}

.credential-label {
    margin: 0;
    font-size: 12px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
}

.credential-value {
    margin: 2px 0 16px;
    font-size: 18px;
}

.credential-value:last-child {
    margin-bottom: 0;
}

.credential-password {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 26px;
    letter-spacing: 0.04em;
    user-select: all;
}

/* ---------- a donor's mark ---------- */

/* The preview and the facts side by side, so an admin can check the image
   against the filename and the date without scrolling between them. */
.logo-preview {
    display: flex;
    flex-wrap: wrap;
    gap: 24px;
    align-items: flex-start;
    margin: 0 0 20px;
    padding: 20px 22px;
    background: var(--card);
    border: 1px solid #dde2ea;
    border-radius: var(--radius);
}

/* Chequered, because a logo is very often a PNG with a transparent
   background and on a plain white card there is no way to tell transparent
   from white. It matters: the report prints on white, and a logo with a
   white box baked into it shows up here rather than in a donor's inbox. */
.logo-preview img {
    max-width: 320px;
    max-height: 160px;
    padding: 8px;
    border: 1px solid #eef1f5;
    border-radius: 4px;
    background-color: #fff;
    background-image:
        linear-gradient(45deg, #eef1f5 25%, transparent 25%, transparent 75%, #eef1f5 75%),
        linear-gradient(45deg, #eef1f5 25%, transparent 25%, transparent 75%, #eef1f5 75%);
    background-size: 16px 16px;
    background-position: 0 0, 8px 8px;
}

.logo-facts {
    flex: 1 1 220px;
}

.logo-facts p {
    margin: 0 0 12px;
}

.logo-facts p:last-child {
    margin-bottom: 0;
}

/* ---------- a quantity typed into a table ---------- */

/* The handover form asks for a number on every row, and a browser's default
   number field is a different height and colour from every other control in
   this application. Wide enough for four digits: nobody issues more, and a
   box that fits its contents is one nobody has to scroll inside. */
.quantity {
    box-sizing: border-box;
    width: 5.5rem;
    height: 32px;
    padding: 0 8px;
    border: 1px solid #d3dae4;
    border-radius: var(--radius);
    background: #fff;
    font: inherit;
    font-size: 14px;
    text-align: right;
}

.quantity:disabled {
    background: #f4f6f9;
    color: var(--muted);
}

/* ---------- a figure somebody overrode ---------- */

/* Every other movement in this system has a document behind it — a waybill, a
   requisition reference, a donation note. A correction has nothing but
   somebody's word, so it does not get to look like the others. Marked on the
   stock card, on the report, and anywhere else a movement is listed. */
.banner-corrected {
    background: #fff3d9;
    border-color: #e0b34a;
    color: #6b4a00;
}

.row-corrected td {
    background: #fffaf0;
}

/* ---------- why somebody asked ---------- */

/* On the approvals rows, where the decision is made. Cut by the column rather
   than by chopping the string, so the whole of it is there on hover and
   nothing is lost — a reason can run to two thousand characters and the
   useful part is almost always the first line. */
.reason {
    display: block;
    max-width: 34ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ---------- what is being asked for, in the approvals drawer ---------- */

/* Tighter than a page table: it sits in a narrow panel beside a decision,
   and the approver is scanning it rather than studying it. */
.drawer-lines {
    margin: 8px 0 20px;
    font-size: 13px;
}

.drawer-lines th,
.drawer-lines td {
    padding: 6px 8px;
}

.drawer-lines .code {
    display: block;
    font-size: 12px;
    color: var(--muted);
}

/* ---------- a column nobody has to fill in ---------- */

/* The form marks what is required with an asterisk and says nothing about
   the rest, which leaves a column of empty boxes looking like work. This is
   the other half of that sentence. */
.th-optional {
    display: block;
    font-size: 10px;
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
    color: var(--muted);
}
