/* ── Data table ───────────────────────────────────────────────────────────── */
.table-container { overflow-x: auto; }
/* .table-wrapper is the same box under the name Warranty and Payment Plans
   use; it had no rule, so their tables widened the whole page (#2071). */
.table-wrapper { overflow-x: auto; }

.data-table {
    width: 100%; min-width: 900px; border-collapse: collapse;
    font-size: 0.9rem;
}

/* Repairs table: constrain description column so table fits on laptop */
#page-repairs .data-table td:nth-child(4) {
    max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.data-table th {
    text-align: left; padding: 0.6rem 0.75rem;
    background: var(--bg-input); color: var(--text-muted);
    font-weight: 600; font-size: 0.8rem; text-transform: uppercase;
    letter-spacing: 0.03em; border-bottom: 1px solid var(--border);
    user-select: none;
    white-space: nowrap;
}
/* Only headers a sorter has wired up look clickable — every header used to show
   a pointer cursor whether clicking it did anything or not. */
.data-table th.sortable-col { cursor: pointer; }
.data-table th.sortable-col:hover { color: var(--text); background: var(--bg-hover); }
.data-table th.sorted-asc::after  { content: " \25B2"; font-size: 0.6rem; }
.data-table th.sorted-desc::after { content: " \25BC"; font-size: 0.6rem; }

.data-table td {
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid var(--border);
    max-width: 300px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.data-table tr:hover td { background: var(--bg-hover); }
.data-table tr.row-overdue td { background: var(--danger-soft); }
.data-table tr.row-overdue td:first-child { box-shadow: inset 3px 0 0 var(--danger); }
.data-table tr.row-overdue:hover td { background: var(--danger-soft); filter: brightness(0.96); }

.data-table td.editable { cursor: pointer; }
.data-table td.editable:hover { background: rgba(79, 140, 255, 0.1); }

.data-table td.editable .edit-input {
    width: 100%; padding: 0.2rem 0.35rem;
    font-size: 0.9rem; font-family: inherit;
    background: var(--bg-input); color: var(--text);
    border: 1px solid var(--accent); border-radius: 3px;
    outline: none;
}

.data-table input[type="checkbox"] {
    width: 16px; height: 16px; cursor: pointer;
}

/* ── Table pagination ─────────────────────────────────────────────────────── */
.table-footer {
    display: flex; justify-content: space-between; align-items: center;
    padding: 0.75rem 0; font-size: 0.85rem; color: var(--text-muted);
}
.pagination { display: flex; gap: 0.3rem; }
.pagination button {
    padding: 0.3rem 0.6rem; background: var(--bg-input);
    border: 1px solid var(--border); border-radius: 4px;
    color: var(--text); font-size: 0.85rem; cursor: pointer;
}
.pagination button:hover { background: var(--bg-hover); }
.pagination button.active { background: var(--accent); color: #fff; border-color: var(--accent); }

@keyframes highlight-pulse {
    0%   { background-color: rgba(var(--success-rgb, 52,199,89), 0.25); }
    100% { background-color: transparent; }
}
.highlight-pulse { animation: highlight-pulse 3s ease-out forwards; }

@keyframes toast-in {
    from { transform: translateY(1rem); opacity: 0; }
    to   { transform: translateY(0);    opacity: 1; }
}

/* ── Modal ────────────────────────────────────────────────────────────────── */
.modal-overlay {
    position: fixed; inset: 0;
    background: rgba(0, 0, 0, 0.5);
    display: flex; align-items: center; justify-content: center;
    z-index: 1000;
}
.modal-content {
    background: var(--bg-card); border: 1px solid var(--border);
    border-radius: var(--radius); padding: 1.5rem;
    width: 90%; max-width: 560px; max-height: 80vh; overflow-y: auto;
}
@media (max-width: 768px) {
    .modal-overlay { align-items: flex-end; }
    .modal-content {
        width: 100%; max-width: none;
        border-radius: var(--radius) var(--radius) 0 0;
        max-height: 90vh;
    }
}
.modal-content h2 { margin-bottom: 1rem; }
.modal-actions {
    display: flex; justify-content: flex-end; gap: 0.5rem;
    margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--border);
}
/* A modal holding a table widens to fit it, rather than a 560px box with a
   900px table scrolling sideways inside a scroll box inside the modal. */
.modal-content:has(table) { max-width: min(1100px, 95vw); }
.modal-content .data-table { min-width: 0; }
.modal-content .data-table td {
    max-width: none; overflow: visible; white-space: normal; overflow-wrap: break-word;
}
/* One scrollbar: the modal's own. A nested scroll box hid the buttons. */
.modal-content .scroll-y { max-height: none; overflow: visible; }
/* Column headings and buttons stay in view while the list scrolls. The
   negative offsets cancel the modal's 1.5rem padding so neither leaves a gap. */
.modal-content .data-table thead th { position: sticky; top: -1.5rem; z-index: 1; }
/* A phone still can't fit a wide table: it scrolls sideways on its own so the
   buttons below don't slide off with it. */
@media (max-width: 768px) {
    .modal-content:has(table) { max-width: none; }
    .modal-content .data-table { display: block; overflow-x: auto; }
}
.modal-content .modal-actions,
.modal-content .modal-footer {
    position: sticky; bottom: -1.5rem; z-index: 2;
    display: flex; justify-content: flex-end; flex-wrap: wrap; gap: 0.5rem;
    margin: 1.25rem -1.5rem -1.5rem; padding: 1rem 1.5rem;
    background: var(--bg-card); border-top: 1px solid var(--border);
}
.modal-panel .modal-actions {
    position: sticky; bottom: calc(-1 * var(--sp-5)); z-index: 2;
    margin: var(--sp-4) calc(-1 * var(--sp-5)) calc(-1 * var(--sp-5));
    padding: var(--sp-3) var(--sp-5);
    background: var(--surface-raised); border-top: 1px solid var(--border);
}

/* ── Status badges ────────────────────────────────────────────────────────── */
.badge {
    display: inline-block; padding: 0.15rem 0.5rem;
    border-radius: 10px; font-size: 0.75rem; font-weight: 600;
    text-transform: uppercase;
}
.badge-active  { background: rgba(22, 163, 74, 0.12);  color: var(--success); }
.badge-draft   { background: rgba(107, 100, 96, 0.12); color: var(--text-muted); }
.badge-pending { background: rgba(161, 98, 7, 0.12);   color: var(--warning); }
.badge-error   { background: rgba(198, 40, 40, 0.12);  color: var(--danger); }

/* ── Invoice header ──────────────────────────────────────────────────────── */
.invoice-header { margin-bottom: 1rem; }

/* History section sits below the upload/preview area */
#po-history { margin-top: var(--sp-4); border-top: 1px solid var(--border); padding-top: var(--sp-4); }
/* A strip, each label beside its value: a line or two on a desktop and about
   four on a phone, where a grid of label-over-value cells ran to eight. */
.invoice-header-grid {
    display: flex; flex-wrap: wrap; align-items: baseline;
    gap: 0.25rem 1.5rem;
}
.invoice-header-grid > div {
    display: flex; align-items: baseline; gap: 0.4rem; min-width: 0;
}
.invoice-header-grid .label {
    font-size: 0.7rem; font-weight: 600; white-space: nowrap;
    text-transform: uppercase; letter-spacing: 0.03em;
    color: var(--text-muted);
}
/* Invoice # and Supplier order no. are boxes on the review (#2798). */
.invoice-header-grid .po-number-in { text-overflow: ellipsis; width: 10rem; max-width: 100%; min-width: 0; height: 2rem; padding-block: 0; }
.invoice-header-grid .value {
    font-size: 0.95rem; color: var(--text); overflow-wrap: anywhere;
}
/* Subtotal, VAT, Total and Items are one group (.invoice-header-money), so
   when the strip has no room for one line the four go to the next together.
   Each pair used to wrap wherever the line ran out, which could leave Total
   on a line away from Subtotal and VAT. In a strip under 31rem (a phone, or
   an upright tablet beside the sidebar) the group would cost a row, so there
   it stands aside and the pairs wrap one by one, as they always did. */
.invoice-header-grid > .invoice-header-money { flex-wrap: wrap; gap: 0.25rem 1.5rem; }
.invoice-header-money > div { display: flex; align-items: baseline; gap: 0.4rem; min-width: 0; }
#po-invoice-header { container: po-invoice / inline-size; }
@container po-invoice (max-width: 31rem) {
    .invoice-header-grid > .invoice-header-money { display: contents; }
}

/* ── Cache enrichment badges ─────────────────────────────────────────────── */
.cache-badge {
    font-size: 1rem;
    line-height: 1;
    cursor: default;
    user-select: none;
}
.cache-hit   { color: var(--success, #16a34a); }
.cache-stale { color: var(--warning, #a16207); }
.cache-miss  { color: var(--text-muted, #6b6460); }

.btn-fetch-item {
    background: none;
    border: 1px solid var(--border, #d0ccc8);
    border-radius: 4px;
    color: var(--text-muted, #6b6460);
    cursor: pointer;
    font-size: 0.85rem;
    padding: 1px 6px;
    transition: color 0.15s, border-color 0.15s;
}
.btn-fetch-item:hover:not(:disabled) {
    color: var(--accent, #4f8cff);
    border-color: var(--accent, #4f8cff);
}
.btn-fetch-item:disabled { opacity: 0.4; cursor: not-allowed; }

/* ── PO review line-item image ───────────────────────────────────────────── */
.po-img-thumb {
    width: 34px;
    height: 34px;
    object-fit: cover;
    border: 1px solid var(--border, #d0ccc8);
    border-radius: 4px;
    cursor: pointer;
    vertical-align: middle;
    background: var(--bg-card, #fff);
}
.po-img-thumb:hover { border-color: var(--accent, #4f8cff); }

.btn-img-add {
    background: none;
    border: 1px dashed var(--border, #d0ccc8);
    border-radius: 4px;
    color: var(--text-muted, #6b6460);
    cursor: pointer;
    font-size: 0.85rem;
    line-height: 1;
    padding: 8px 7px;
    opacity: 0.65;
    transition: opacity 0.15s, border-color 0.15s;
}
.btn-img-add:hover { opacity: 1; border-color: var(--accent, #4f8cff); }
.btn-img-add:disabled { opacity: 0.35; cursor: not-allowed; }

/* ── Price change indicator ──────────────────────────────────────────────── */
.price-change-up   { color: var(--danger, #c62828);  font-size: 0.8rem; margin-left: 3px; cursor: default; }
.price-change-down { color: var(--success, #16a34a); font-size: 0.8rem; margin-left: 3px; cursor: default; }

/* ── Sale price indicator ──────────────────────────────────────────────────── */
.sale-badge {
    display: inline-block;
    background: rgba(190, 18, 60, 0.12);
    color: var(--ink-rose);
    border-radius: 3px;
    font-size: 0.7rem;
    font-weight: 700;
    padding: 1px 4px;
    margin-left: 4px;
    letter-spacing: 0.03em;
    cursor: default;
    user-select: none;
}

/* ── Multi-pack split indicator ──────────────────────────────────────────── */
.split-badge {
    display: inline-block;
    background: rgba(109, 40, 217, 0.12);
    color: var(--ink-violet);
    border-radius: 3px;
    font-size: 0.75rem;
    font-weight: 600;
    padding: 1px 4px;
    cursor: default;
    user-select: none;
}
.btn-split-dismiss {
    background: none;
    border: none;
    color: var(--text-muted);
    font-size: 0.7rem;
    cursor: pointer;
    padding: 0 2px;
    margin-left: 2px;
    line-height: 1;
    vertical-align: middle;
    opacity: 0.6;
}
.btn-split-dismiss:hover { opacity: 1; color: var(--danger, #c62828); }

.split-badge--off {
    background: rgba(107, 100, 96, 0.12);
    color: var(--text-muted);
}
.btn-split-undo {
    background: none;
    border: none;
    color: var(--ink-violet);
    font-size: 0.85rem;
    cursor: pointer;
    padding: 0 2px;
    margin-left: 2px;
    opacity: 0.6;
    vertical-align: middle;
}
.btn-split-undo:hover { opacity: 1; }

/* ── Settings page ───────────────────────────────────────────────────────── */
.settings-section { margin-bottom: 0.5rem; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.settings-section-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: 0.65rem 1rem; cursor: pointer; user-select: none;
    background: var(--surface); gap: 0.5rem;
}
.settings-section-header:hover { background: var(--surface-hover, var(--bg)); }
.settings-section-header h2 {
    margin: 0; font-size: 0.875rem; font-weight: 700; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: 0.05em; flex: 1;
}
.settings-chevron { color: var(--text-muted); transition: transform 0.2s; flex-shrink: 0; }
.settings-section.collapsed .settings-chevron { transform: rotate(-90deg); }
.settings-body { padding: 0.75rem 1rem 1rem; }
.settings-section.collapsed .settings-body { display: none; }
/* Admins-only section for anyone else (Settings > Connections): greyed, shut. */
.settings-section--locked { opacity: 0.5; }
.settings-section--locked .settings-section-header { cursor: not-allowed; }
.settings-section--locked .settings-section-header:hover { background: var(--surface); }
.settings-section--locked .settings-section-header h2::after { content: ' (admins only)'; text-transform: none; letter-spacing: 0; font-weight: 400; }
/* Tiles: Settings opens on a grid of groups (the dashboard's module cards);
   a tile shows only its group's sections, under a back bar. */
.settings-tiles { margin: 0 0 1rem; }
.settings-tiles[hidden], .settings-group-bar[hidden] { display: none; }
.settings-tile { font: inherit; color: inherit; text-align: left; width: 100%; }
.settings-tile .module-card-blurb { margin-bottom: 0.25rem; }
.settings-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.settings-section--other { display: none; }
.settings-group-bar { display: flex; align-items: center; gap: 0.75rem; margin: 0 0 1rem; flex-wrap: wrap; }
.settings-group-title { margin: 0; font-size: 1.1rem; }
/* Module tickboxes (settings.js _moduleCheckboxes), in Add User and each
   user's module editor. On a phone three columns clipped the third at the
   card's edge; the Add User copy also sat inside .form-group, whose label
   rule made every name bold uppercase, so names wrapped to three lines and
   the centred ticks drifted off them. There: as many 8rem columns as fit
   (two at 375px, one at 320px), names in sentence case, each tick on its
   name's first line, and the Add User card's own border and padding gone. */
.module-picker-grid { grid-template-columns: repeat(3, 1fr); }
.module-picker .module-pick { align-items: center; }
/* Names in sentence case everywhere, as the per-user editor always had
   (Neil, 29 Sep): the .form-group label rule made Add User's shout. */
.module-picker .module-pick { font-weight: 400; text-transform: none; letter-spacing: normal; }
@media (max-width: 768px) {
    .module-picker-grid { grid-template-columns: repeat(auto-fill, minmax(min(8rem, 100%), 1fr)); }
    .module-picker .module-pick { align-items: flex-start; min-width: 0; margin-bottom: 0; line-height: 1.3; font-weight: 400; text-transform: none; letter-spacing: normal; overflow-wrap: anywhere; }
    .module-picker .module-pick input { margin: calc((0.875rem * 1.3 - 1rem) / 2) 0 0; }
    .users-add-card { padding: 0; border: 0; border-radius: 0; background: none; }
}
/* Phone only; shown in the media block below (#1827). */
.users-sort-phone { display: none; }

/* The Users table on a phone (#1789). As a table it was 928px wide in a
   261px box at 375px: every row, and the module editor under it, had to be
   swiped sideways. There each user is a card instead: name on top, then one
   labelled line per field (data-label in settings.js _renderUsers), then
   Reset PW and Delete; the editor opens full width under its card. Headers,
   and so sorting, are desktop only. The card round it loses its own box, as
   Add User's does. */
@media (max-width: 768px) {
    .users-list-card { padding: 0; border: 0; border-radius: 0; background: none; }
    .users-table { display: block; min-width: 0; }
    .users-table thead { display: none; }
    .users-table tbody { display: block; }
    .users-table tr[data-username] { display: block; padding: 0.5rem 0; border-top: 1px solid var(--border); }
    .users-table tr[data-username] > td { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; min-height: 2rem; padding: 0.1rem 0; border: 0; max-width: none; }
    .users-table tr[data-username] > td[data-label]::before { content: attr(data-label); color: var(--text-muted); font-size: 0.8rem; font-weight: 600; }
    .users-table tr[data-username] > td:first-child { font-weight: 600; }
    .users-table tr[data-username] > td:last-child { justify-content: flex-end; gap: 0.5rem; }
    .users-table tr[data-username]:hover > td { background: none; }
    .users-sort-phone { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem; }
    .users-sort-phone label { font-size: 0.8rem; font-weight: 600; color: var(--text-muted); }
    .users-sort-phone select { flex: 1; min-width: 0; width: auto; }
    .users-table tr[data-editor-for] { display: block; }
    .users-table tr[data-editor-for] > td { display: block; max-width: none; white-space: normal; }
}
.settings-help, .settings-help-text { margin: 0 0 0.75rem; font-size: 0.875rem; color: var(--text-muted); }

.connection-row {
    display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;
}
.connection-label {
    font-weight: 600; min-width: 5rem;
}
.connection-value {
    flex: 1; font-size: 0.9rem; padding: 0.25rem 0.6rem;
    border-radius: var(--radius); font-weight: 500;
}
.connection-value.checking   { color: var(--text-muted); }
.connection-value.connected  { color: var(--success); background: rgba(22, 163, 74, 0.1); }
.connection-value.disconnected { color: var(--danger); background: rgba(198, 40, 40, 0.1); }

.form-row {
    display: flex; gap: 1.5rem; flex-wrap: wrap; margin-bottom: 1rem;
}
.form-group {
    display: flex; flex-direction: column; gap: 0.3rem; flex: 1; min-width: 180px;
}
.form-group label { font-size: 0.85rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; }
/* A hint outside a group too: Settings' Bottecchia note sits between two (#2873). */
.form-hint { font-size: 0.78rem; color: var(--text-muted); }
.form-group .form-hint { margin: 0; font-size: 0.78rem; color: var(--text-muted); }
.form-group .input-inline, .form-group input[type="text"], .form-group input[type="number"], .form-group input[type="password"] {
    padding: 0.45rem 0.65rem; background: var(--bg-input); border: 1px solid var(--border);
    border-radius: var(--radius); color: var(--text); font-size: 0.9rem; font-family: inherit;
}
/* On a phone a box in a .form-group is typed in at full size (#2198). The
   rule above outranks style.css's phone rule, so these boxes stayed at 0.9rem
   (15.3px). iPhone Safari zooms the whole page in when a box under 16px is
   tapped and leaves it zoomed: after Edit bike on a job ticket, everything
   ran past the right edge of the screen. */
@media (max-width: 768px) {
    .form-group .input-inline, .form-group input[type="text"], .form-group input[type="number"], .form-group input[type="password"] {
        font-size: 1rem;
    }
}
.form-group input:focus {
    outline: none; border-color: var(--accent);
}
.checkbox-label {
    display: flex; align-items: center; gap: 0.5rem; font-size: 0.9rem; cursor: pointer;
    font-weight: normal !important; text-transform: none !important; letter-spacing: 0 !important;
    color: var(--text) !important;
}
.input-table-cell {
    width: 100%; padding: 0.3rem 0.5rem;
    background: var(--bg-input); border: 1px solid var(--border);
    border-radius: 3px; color: var(--text); font-size: 0.875rem; font-family: inherit;
    box-sizing: border-box;
}
.input-table-cell:focus { outline: none; border-color: var(--accent); }
textarea.input-table-cell { line-height: 1.45; }

/* Settings → Collection Rules: a saved name reads as a label until Rename. */
.rule-collection-cell { display: flex; align-items: center; gap: 0.25rem; }
.rule-collection-cell .rule-collection[readonly] {
    background: transparent; border-color: transparent; font-weight: 600;
    cursor: default; text-overflow: ellipsis;
}
.rule-collection-cell .rule-rename-btn { flex: none; }

/* ── Split-detected row highlight ────────────────────────────────────────── */
tr.row-split-detected td {
    background: rgba(109, 40, 217, 0.07);
}
tr.row-split-detected:hover td {
    background: rgba(109, 40, 217, 0.10);
}

/* ── PO review: cost gone up, retail kept from Shopify ───────────────────── */
tr.row-cost-up td[data-key="cost"] {
    background: rgba(198, 40, 40, 0.08);
    font-weight: 600;
}
.po-retail-live   { cursor: help; border-bottom: 1px dotted var(--text-muted); }
.po-retail-needed { color: var(--danger, #c62828); font-size: 0.8rem; font-style: italic; }

/* ── PO review: where the stock goes (#570) ─────────────────────────────── */
/* width: auto overrides the global select { width: 100% }. A percentage width
   gives the table column nothing to size itself by, so on a screen too narrow
   for the table the picker shrank until neither shop's name could be read, and
   the note and split button after it were cut off by the cell (#709). */
select.po-line-location { font-size: 0.8rem; padding: 2px 4px; width: auto; max-width: 11rem; }
/* A line on a location of its own, not the PO's */
select.po-line-location--own { border-color: var(--accent); background: var(--accent-soft); }
/* Chrome paints a select's open list in the select's own background colour
   over white, so a translucent tint (po-line-location--own's --accent-soft)
   left the dark theme's pale option text unreadable. Options get an opaque
   colour pair of their own whatever the select is tinted. */
select option { background: var(--bg-input); color: var(--text); }
/* Why a line starts where it does ("For Boyle repair #2481 (needs 1).") as text
   under the picker, readable without hover on the iPad (#736). width: 0 with
   min-width: 100% keeps a long note from widening the column; it wraps instead. */
.po-location-note {
    display: block; width: 0; min-width: 100%;
    margin-top: 0.2rem; white-space: normal; line-height: 1.3;
    font-size: 0.75rem; color: var(--ink-blue);
}
/* A Suggest split note folded to one line (#2830): the shops and quantities
   show; the why and the shops' stock open on a tap. Same width rule as the
   note, so it never widens the column. */
.po-location-why { display: block; width: 0; min-width: 100%; }
.po-location-why > summary { cursor: pointer; list-style: none; }
.po-location-why > summary::-webkit-details-marker { display: none; }
.po-location-why > summary::before { content: '▸ '; font-size: 0.7rem; }
.po-location-why[open] > summary::before { content: '▾ '; }
/* Decided once, not per PO (#2830): with the review's sync choices and the
   Markup box answered in Settings, the review doesn't ask. The controls stay
   in the page for a shop that turns them back on. */
#po-preview.po-preview--quiet .action-bar-flags,
#po-preview.po-preview--quiet .po-fill-group { display: none; }
/* Restock rows flat, new products as cards (#2831). The table keeps Img, SKU,
   Name, Qty, To come, Location, Cost, Retail and Margin (#2926: what is
   checked on every line); what else a new product needs is on the card
   under its row, and a known SKU's corrections open with ▸. The
   Products with sizes and Sales channels boxes, the Group sizes button and
   the other columns are the old layout's (Settings › Suppliers). Split shows
   once a line is a pack. */
#po-preview.po-preview--cards #po-items-table [data-key="_cache"],
#po-preview.po-preview--cards #po-items-table :is(th,td)[data-key="variant_group"],
#po-preview.po-preview--cards #po-items-table :is(th,td)[data-key="option_value"],
#po-preview.po-preview--cards #po-items-table :is(th,td)[data-key="sale_pct"],
#po-preview.po-preview--cards #po-items-table [data-key="_nfr"],
#po-preview.po-preview--cards #po-items-table [data-key="_fetch"],
#po-preview.po-preview--cards #po-items-table.po-no-split [data-key="_split"],
#po-preview.po-preview--cards .po-variant-groups,
#po-preview.po-preview--cards .po-channels,
#po-preview.po-preview--cards #po-group-sizes-btn,
#po-preview:not(.po-preview--cards) #po-items-table [data-key="_open"] { display: none; }
#po-items-table th[data-key="_open"] { width: 1.6rem; }
.po-row-open {
    background: none; border: 1px solid var(--border, #d0ccc8); border-radius: 4px;
    color: var(--text-muted, #6b6460); cursor: pointer; font-size: 0.85rem;
    line-height: 1; padding: 2px 6px; min-width: 1.6rem;
}
.po-row-open:hover { color: var(--accent, #4f8cff); border-color: var(--accent, #4f8cff); }
#po-items-table tr.row-open > td { border-bottom: none; }
#po-items-table tr.row-detail > td { padding: 0 0.4rem 0.5rem 2.4rem; border-top: none; }
#po-items-table tr.row-detail.row-not-for-resale > td { color: var(--text-muted); font-style: italic; }
.po-card {
    display: flex; flex-direction: column; gap: 0.45rem;
    padding: 0.5rem 0.75rem; border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--bg-hover, rgba(0, 0, 0, 0.02)); font-size: 0.88rem;
}
.po-card--new { border-left: 3px solid var(--accent, #4f8cff); }
.po-card-fields { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 1.1rem; }
.po-card-field { display: inline-flex; align-items: center; gap: 0.35rem; white-space: nowrap; }
.po-card-field input[type="number"] { width: 6rem; }
.po-card-field input[type="text"] { width: 7rem; }
.po-card-field .po-card-group { width: 3.2rem; text-transform: uppercase; }
.po-card-edit { padding: 2px 6px; border: 1px solid var(--border); border-radius: 4px; background: var(--bg); color: inherit; }
.po-card-nfr input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; }
.po-card-fetch, .po-card-pack { font-size: 0.82rem; padding: 2px 8px; }
.po-card-note { margin: 0; color: var(--text-muted); font-size: 0.82rem; }
.po-card-group .po-vg-row { padding: 0; }
.po-card-channels { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem 1rem; }
.po-card-channels label { display: inline-flex; align-items: center; gap: 0.35rem; white-space: nowrap; cursor: pointer; }
.po-card-channels input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; }
.po-card-label { font-size: 0.78rem; font-weight: 700; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; }
.po-card-looks { padding: 0.4rem 0.6rem; border-radius: var(--radius); background: rgba(237, 160, 0, 0.12); }

/* PO review: what needs a person, as counts above the table (#2832). Each
   count is a button that filters the table to its lines; pressed, it is
   filled in. Nothing to do is one quiet line. Only in the cards layout. */
.po-exceptions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.6rem; margin-bottom: var(--sp-2); }
.po-exceptions-lead { font-weight: 700; margin-right: 0.2rem; }
.po-exception {
    background: rgba(237, 160, 0, 0.14); border: 1px solid rgba(237, 160, 0, 0.6); border-radius: 999px;
    color: inherit; cursor: pointer; font-size: 0.88rem; padding: 3px 12px;
}
.po-exception:hover { border-color: var(--accent, #4f8cff); }
.po-exception--on { background: var(--accent, #4f8cff); border-color: var(--accent, #4f8cff); color: #fff; }
.po-exceptions-all { background: none; border: none; color: var(--accent, #4f8cff); cursor: pointer; font-size: 0.85rem; padding: 3px 6px; text-decoration: underline; }
.po-exceptions-none { color: var(--success, #2e7d32); font-size: 0.9rem; margin: 0; }
#po-items-table tr.po-row-filtered-out { display: none; }
.po-card-looks-lead { margin: 0 0 0.25rem; font-weight: 600; }
.po-card-looks ul { margin: 0 0 0.35rem; padding-left: 1.2em; }
.po-card-looks li { margin: 0.15rem 0; }
.po-card-looks li .btn { margin-left: 0.35rem; }
@media (max-width: 600px) {
    #po-items-table tr.row-detail > td { padding-left: 0.75rem; }
    .po-card-fields { gap: 0.4rem 0.75rem; }
}
/* Split a line between the shops (#703), styled like the table's Fetch button */
.btn-line-split,
.btn-line-unsplit {
    background: none;
    border: 1px solid var(--border, #d0ccc8);
    border-radius: 4px;
    color: var(--text-muted, #6b6460);
    cursor: pointer;
    font-size: 0.85rem;
    margin-left: 0.25rem;
    padding: 1px 6px;
}
.btn-line-split:hover,
.btn-line-unsplit:hover { color: var(--accent, #4f8cff); border-color: var(--accent, #4f8cff); }

/* ── PO review: not for resale, and sales channels (#348) ────────────────── */
/* The heading wraps onto two lines ("Not for / resale"), so the column is
   no wider than those words need; it holds only a tick box. */
#po-items-table th[data-key="_nfr"] { white-space: normal; min-width: 5rem; line-height: 1.2; }
/* A tighter review table. Sixteen columns at the big tables' padding came to
   1486px, so on a laptop Margin, Not for resale and the fetch and delete
   buttons were off the right edge. Half the side padding and a little less
   above and below; on a laptop Name gives up some width too, so the table
   fits beside the collapsed sidebar without scrolling sideways. */
#po-items-table .data-table th,
#po-items-table .data-table td { padding-inline: 0.4rem; }
#po-items-table .data-table td { padding-block: 0.3rem; }
#po-items-table .data-table th:first-child,
#po-items-table .data-table td:first-child { padding-left: 0.75rem; }
@media (max-width: 1500px) {
    #po-items-table .data-table td[data-key="name"] { max-width: 15rem; }
    /* With Retail and Margin on the row (#2926) the review needed about
       1013px, so on a 1280 laptop with the menu open, or at 1024, the × and
       part of Margin were off the right edge (#2933). On a laptop the name
       wraps instead of pushing the row wide, Margin puts its % under its €,
       the To come heading takes two lines, a long shop name is cut in its
       picker, and the table may be narrower than the big tables' 900px. */
    #po-preview.po-preview--cards #po-items-table .data-table { min-width: 0; }
    #po-preview.po-preview--cards #po-items-table .data-table td[data-key="name"] { white-space: normal; min-width: 8rem; overflow-wrap: anywhere; }
    #po-preview.po-preview--cards #po-items-table .data-table td[data-key="_margin"] { white-space: normal; max-width: 5.5rem; }
    #po-preview.po-preview--cards #po-items-table .data-table th[data-key="short_qty"] { white-space: normal; line-height: 1.2; }
    #po-preview.po-preview--cards #po-items-table select.po-line-location { max-width: 7.5rem; }
}
/* A price being typed keeps its column's width: the box was as wide as a
   number input likes to be, about three times the Retail column (#2933). */
#po-items-table .data-table td.editable .edit-input[type="number"] { width: 5rem; }
/* A line that gets nothing in Shopify reads as set aside. */
tr.row-not-for-resale td { color: var(--text-muted); font-style: italic; }
.po-channels {
    margin: 0.5rem 0 1rem; padding: 0.6rem 0.75rem;
    border: 1px solid var(--border); border-radius: var(--radius);
}
.po-channels h3 {
    margin: 0 0 0.4rem; font-size: 0.8rem; font-weight: 700; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: 0.04em;
}
.po-channels-all {
    display: inline-flex; align-items: center; gap: 0.4rem; margin-bottom: 0.35rem;
    font-size: 0.9rem; font-weight: 600; cursor: pointer;
}
.po-channels-all input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; }
.po-channels-list { display: flex; flex-wrap: wrap; gap: 0.35rem 1.25rem; }
.po-channels-list label {
    display: inline-flex; align-items: center; gap: 0.4rem;
    font-size: 0.9rem; white-space: nowrap; cursor: pointer;
}
.po-channels-list input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; }
.po-channels-note { margin: 0.45rem 0 0; font-size: 0.82rem; color: var(--text-muted); }

/* ── PO review: sizes of one product (#614) ──────────────────────────────── */
.po-vg-badge {
    display: inline-block; min-width: 1.4em; text-align: center;
    background: rgba(79, 140, 255, 0.12); color: var(--ink-blue);
    border-radius: 3px; font-size: 0.75rem; font-weight: 700; padding: 1px 4px;
}
.po-vg-badge--bad { background: rgba(198, 40, 40, 0.12); color: var(--danger, #c62828); }
.po-vg-badge--shop { background: transparent; border: 1px solid var(--border); color: var(--text-muted); font-weight: 600; }
.po-vg-option { color: var(--text-muted); font-size: 0.8rem; }
.po-variant-groups {
    margin: 0.5rem 0 1rem; padding: 0.6rem 0.75rem;
    border: 1px solid var(--border); border-radius: var(--radius);
}
.po-variant-groups h3 {
    margin: 0 0 0.4rem; font-size: 0.8rem; font-weight: 700; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: 0.04em;
}
.po-vg-help { margin: 0 0 0.5rem; font-size: 0.82rem; color: var(--text-muted); }
.po-vg-row {
    display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem; padding: 0.3rem 0;
}
.po-vg-row + .po-vg-row { border-top: 1px solid var(--border); }
.po-vg-row .input-table-cell { width: auto; }
.po-vg-row .po-vg-title-input { flex: 1 1 16rem; min-width: 0; }
/* The option box is the 7rem it was meant to be: an input will not go under
   its own width without min-width: 0, so it drew at 192px. */
.po-vg-row .po-vg-option-input { flex: 0 0 7rem; min-width: 0; }
.po-vg-values { font-size: 0.85rem; }
/* In a wide panel the rows line up as columns: the option, the list of
   sizes and the button at the end each have one width, so the title boxes
   start and end together from one row to the next. In a panel narrower than
   61rem the fixed widths cost a line, so each part is as wide as its words.
   The panel's own width, not the screen's, which cannot see the sidebar. */
.po-variant-groups { container: po-vg / inline-size; }
@container po-vg (min-width: 61rem) {
    .po-vg-row > .po-vg-option { flex: 0 0 7rem; }
    .po-vg-values { flex: 0 1 12rem; min-width: 5rem; }
    .po-vg-row > .btn { flex: 0 0 auto; min-width: 13rem; justify-content: center; }
    /* A row that also asks which size the product has now needs the room. */
    .po-vg-first ~ .po-vg-values { flex-basis: 5rem; }
}
.po-vg-problem { flex-basis: 100%; color: var(--danger, #c62828); font-size: 0.8rem; }
/* v2: sizes added to a product already in Shopify */
.po-vg-parent { flex: 1 1 16rem; min-width: 0; font-size: 0.85rem; }
.po-vg-first {
    display: inline-flex; align-items: center; gap: 0.3rem;
    font-size: 0.8rem; color: var(--text-muted);
}
.po-vg-row .po-vg-first-input { flex: 0 0 5rem; width: 5rem; }
/* "Add to a product in Shopify": one scrollbar, not two. The list had its
   own (50vh) inside a pop-up that scrolled as well (80vh). The pop-up is a
   column that does not scroll, and the list takes what is left of it, so the
   search box above and Cancel below stay put while the list moves. */
.modal-content:has(#po-parent-results) { display: flex; flex-direction: column; overflow: hidden; }
.modal-content:has(#po-parent-results) > * { flex: 0 0 auto; }
.modal-content:has(#po-parent-results) > .modal-actions { position: static; }
/* Named through the pop-up, so it outranks the rule above it. */
.modal-content:has(#po-parent-results) > .po-parent-results { flex: 1 1 auto; min-height: 4rem; }
.po-parent-results { overflow-y: auto; margin: 0.5rem 0 0; scrollbar-width: thin; }
.po-parent-choice {
    display: flex; flex-direction: column; align-items: flex-start; gap: 0.1rem;
    width: 100%; margin-bottom: 0.35rem; padding: 0.45rem 0.6rem; text-align: left;
    /* A button does not take the page's font unless told to: these drew at 13px. */
    font: inherit; font-size: 0.9rem; line-height: 1.3;
    background: transparent; color: inherit; cursor: pointer;
    border: 1px solid var(--border); border-radius: var(--radius);
}
.po-parent-choice:last-child { margin-bottom: 0; }
.po-parent-choice:hover:not(:disabled) { border-color: var(--accent); background: var(--accent-soft); }
.po-parent-choice:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.po-parent-choice:disabled { opacity: 0.55; cursor: not-allowed; }
.po-parent-title { font-weight: 600; }
.po-parent-note { font-size: 0.8rem; color: var(--text-muted); }

/* ── Button size variants ────────────────────────────────────────────────── */
.btn-sm { padding: 0.3rem 0.65rem; font-size: 0.82rem; }

/* ── Repair status/priority badge colours ────────────────────────────────── */
.badge-blue   { background: rgba(79,  140, 255, 0.12); color: var(--ink-blue); }
.badge-yellow { background: rgba(161,  98,   7, 0.12); color: var(--ink-amber); }
.badge-orange { background: rgba(194,  65,  12, 0.12); color: var(--ink-orange); }
.badge-green  { background: rgba(22,  163,  74, 0.12); color: var(--ink-green); }
.badge-grey   { background: rgba(107, 100,  96, 0.12); color: var(--text-muted); }
.badge-red    { background: rgba(198,  40,  40, 0.12); color: var(--ink-red); }
.badge-purple { background: rgba(109,  40, 217, 0.12); color: var(--ink-violet); }
.badge-teal   { background: rgba( 15, 118, 110, 0.12); color: var(--ink-teal); }

/* ── Repairs toolbar ─────────────────────────────────────────────────────── */
/* One sticky bar over the list: title and "?", search, Location, Status,
   Payment, the Filters button, the actions, then the count and its chips,
   which wrap onto a second line when the row is full. Due, Pinned, Archived
   and Customer history sit in the panel behind Filters. The bar used to be a
   title row and three filter rows, and an iPad in portrait saw four jobs. */
.repair-toolbar {
    position: sticky; top: 0; z-index: 20;
    container: repair-toolbar / inline-size;
    display: flex; align-items: center; flex-wrap: wrap; gap: 0.45rem 0.6rem;
    padding: 0.5rem 0.75rem;
    background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius);
    margin-bottom: 0.75rem;
}
.repair-toolbar-title {
    order: 1; margin: 0; font-size: 1.2rem; line-height: 1.2; white-space: nowrap;
    display: flex; align-items: center; gap: 0.35rem;
}
/* The sidebar (the phone bar, on a phone) already names the page, so the
   word is for screen readers and the bar keeps the room; the "?" stays. */
.repair-title-text {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.repair-toolbar .repair-search-wrap { order: 2; }
.repair-toolbar-selects { order: 3; display: flex; align-items: center; gap: 0.4rem; min-width: 0; }
.repair-filters-btn { order: 4; white-space: nowrap; min-height: 38px; display: inline-flex; align-items: center; gap: 0.35rem; }
.repair-toolbar .repair-filter-summary { order: 5; }
.repair-toolbar-actions { order: 6; margin-left: auto; flex-wrap: nowrap; }
.repair-toolbar-actions .btn { min-height: 38px; white-space: nowrap; }
/* "+ New repair", the page's main action (#2228): its words at every width,
   a finger high, at the right-hand end of the bar's first line. It was a "+"
   the size and shape of the 📄 beside it on a tablet and a phone. On a
   desktop it is 6px taller than the controls, let into the bar's padding so
   the bar is no taller for it. */
.repair-toolbar-actions .repair-new-btn { min-height: 44px; margin-block: -3px; justify-content: center; }

.repair-filter-group {
    display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
}
.repair-filter-label {
    font-size: 0.78rem; font-weight: 600; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: 0.04em; white-space: nowrap;
}
.repair-filter-field { display: flex; align-items: center; gap: 0.4rem; }
/* A long option ("Part paid (money owed)") must not push the bar wider than a phone. */
.repair-filter-field select { min-width: 0; max-width: 100%; }
/* In the bar each dropdown's first choice names it ("All locations"), so its
   label is for screen readers only; in the panel it shows. */
.repair-toolbar-selects .repair-filter-label {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.repair-toolbar-selects .repair-filter-field { flex: 0 1 9.5rem; min-width: 0; }
.repair-toolbar-selects select { width: 100%; min-width: 0; min-height: 38px; }
/* The 📌 Pinned toggle: .btn-secondary off, .btn-primary on, like 🗄 Archived. */
.repair-pinned-toggle { white-space: nowrap; }

/* How many filters behind the button are set. */
.repair-filters-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 1.25rem; height: 1.25rem; padding: 0 0.3rem; border-radius: 999px;
    background: var(--accent); color: #fff; font-size: 0.72rem; font-weight: 700; line-height: 1;
}

/* The panel behind Filters drops from the bar's right edge. */
.repair-filters-panel {
    position: absolute; top: calc(100% + 4px); right: 0; z-index: 30;
    width: min(460px, 100%);
    display: flex; flex-direction: column; gap: 0.85rem;
    padding: 0.85rem 1rem;
    background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
}
.repair-filters-panel[hidden] { display: none; }
.repair-filters-section { display: flex; flex-direction: column; gap: 0.45rem; }
.repair-panel-selects { display: flex; flex-direction: column; gap: 0.5rem; }
.repair-panel-selects:empty { display: none; }
.repair-panel-selects .repair-filter-field { justify-content: space-between; }
.repair-panel-selects .repair-filter-label { min-width: 5rem; }
.repair-panel-selects select { flex: 1; min-width: 0; }
.repair-date-range { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }

/* Search box with a 🔍 inside and a ✕ that appears once something's typed. */
.repair-search-wrap { position: relative; flex: 1 1 180px; max-width: 360px; }
.repair-search-wrap::before {
    content: '🔍'; position: absolute; left: 0.6rem; top: 50%; transform: translateY(-50%);
    font-size: 0.85rem; opacity: 0.6; pointer-events: none;
}
.repair-search-wrap #repair-search { width: 100%; max-width: none; padding-left: 2rem; padding-right: 2rem; }
.repair-search-clear {
    position: absolute; right: 0.35rem; top: 50%; transform: translateY(-50%);
    border: none; background: none; color: var(--text-muted); cursor: pointer;
    font-size: 0.9rem; padding: 0.2rem 0.4rem; border-radius: 50%;
}
.repair-search-clear:hover { color: var(--text); background: var(--bg-hover, rgba(0,0,0,0.06)); }

/* A dropdown or date that is narrowing the list is outlined in the accent. */
#page-repairs .is-set { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }

.repair-filter-summary {
    display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap;
    flex: 1 1 auto; min-width: 0;
    font-size: 0.85rem; color: var(--text-muted);
}
.repair-filter-summary:empty { display: none; }
.repair-filter-count { white-space: nowrap; }
.repair-filter-count strong { color: var(--text); }
.repair-filter-sep { opacity: 0.6; }
.repair-filter-chip {
    display: inline-flex; align-items: center; gap: 0.35rem;
    padding: 0.2rem 0.6rem; border-radius: 20px;
    border: 1px solid var(--accent); background: none; color: var(--accent);
    font-size: 0.8rem; font-weight: 500; cursor: pointer; white-space: nowrap;
}
.repair-filter-chip span { font-size: 0.7rem; opacity: 0.8; }
.repair-filter-chip:hover { background: var(--accent); color: #fff; }
.repair-filter-clear-all {
    border: none; background: none; color: var(--text-muted); cursor: pointer;
    font-size: 0.8rem; text-decoration: underline; padding: 0.2rem 0.3rem; white-space: nowrap;
}
.repair-filter-clear-all:hover { color: var(--accent); }

.repair-preset-group {
    display: flex; gap: 0.3rem; flex-wrap: wrap;
}
.repair-preset {
    padding: 0.28rem 0.75rem;
    border: 1px solid var(--border); border-radius: 20px;
    background: none; color: var(--text-muted);
    font-size: 0.82rem; font-weight: 500; cursor: pointer;
    transition: all 0.15s;
    white-space: nowrap;
}
.repair-preset:hover { border-color: var(--accent); color: var(--accent); }
.repair-preset.active { background: var(--accent); border-color: var(--accent); color: #fff; }
.repair-date-label {
    font-size: 0.8rem; color: var(--text-muted); white-space: nowrap; user-select: none;
}
.repair-date-input { flex: 1 1 130px; max-width: 160px !important; }

/* Short of room for every label, the guide button keeps its 📄 alone. */
@container repair-toolbar (max-width: 1300px) {
    .repair-toolbar-actions a.btn .repair-action-text { display: none; }
}

/* A desktop bar that can't hold the count and both money markers beside the
   controls (a 1440 screen leaves about 1100px inside it): the count and the markers
   stack in a column the height of the controls, and the dropdowns and search
   give up width, so everything stays on one line with the actions at the
   right. With the markers inline the actions dropped to a second line under
   about 1600 wide, and at 1920 in Ukrainian (#2111). Filter chips don't fit
   a column, so with a filter on the count line goes under the controls and
   the actions keep their place. */
@container repair-toolbar (min-width: 1065.01px) and (max-width: 1680px) {
    /* 7rem, and the button's padding trimmed: "+ New repair" is wider than
       the "+ New Repair" it replaces, and in Ukrainian in a wide face that
       put the actions on a second line at 1440 (#2228). */
    .repair-toolbar .repair-search-wrap { flex: 1 1 7rem; }
    .repair-toolbar-actions .repair-new-btn { padding-inline: 0.75rem; }
    .repair-toolbar-selects { flex: 2 1 18.5rem; max-width: 29rem; }
    .repair-toolbar-selects .repair-filter-field { flex: 1 1 0; }
    .repair-toolbar .repair-filter-summary:not(:has(.repair-filter-chip)) {
        flex: 0 0 auto; flex-direction: column; flex-wrap: nowrap;
        align-items: flex-start; gap: 2px; font-size: 0.75rem; line-height: 1.1;
        /* Three short lines are taller than a control: let them into the
           bar's own padding rather than make the bar taller. */
        margin-block: -0.3rem;
    }
    .repair-toolbar .repair-filter-summary:not(:has(.repair-filter-chip)) .repair-mismatch-count {
        margin-left: 0; padding: 1px 0.5rem; font-size: 0.68rem; line-height: 1.2;
    }
    .repair-toolbar .repair-filter-summary:has(.repair-filter-chip) { order: 7; flex: 1 1 100%; }
}

/* A narrow bar (iPad in portrait, beside the sidebar; a 1366 laptop): title,
   search and the actions on the first line; the three dropdowns and Filters
   share the second; the count goes under them only if it can't fit beside. */
@container repair-toolbar (max-width: 1065px) {
    /* The search box asks for the whole line less 34rem: room for the title
       and the actions beside it (about 27rem in English or Ukrainian), and
       none for the dropdowns, so they always start the second line. With no
       width asked for, a bar from about 850px (an iPad on its side) held
       everything on one line, and the actions sat in the middle of it,
       between the search box and the dropdowns (#2228). */
    .repair-toolbar .repair-search-wrap { flex: 1 1 calc(100% - 34rem); max-width: none; min-width: 9rem; }
    .repair-toolbar-actions { order: 3; }
    /* The first line has room here, so 📄 says "Till guide": a tablet has no
       tooltip to say what it is (#2228). */
    .repair-toolbar-actions a.btn .repair-action-text { display: inline; }
    .repair-toolbar-selects { order: 4; flex: 1 1 22rem; }
    .repair-toolbar-selects .repair-filter-field { flex: 1 1 0; }
    .repair-filters-btn { order: 5; }
    .repair-toolbar .repair-filter-summary { order: 6; }
}
/* An iPad held upright has about 500px beside the sidebar: 📄 goes back to
   its icon and the search box gives up width, so "+ New repair" keeps its
   place on the first line, in Ukrainian too. */
@container repair-toolbar (max-width: 640px) {
    .repair-toolbar-actions a.btn .repair-action-text { display: none; }
    .repair-toolbar .repair-search-wrap { min-width: 5rem; }
    .repair-toolbar-actions .repair-new-btn { padding-inline: 0.75rem; }
    /* The dropdowns' words were cut ("All location", "Any paymer") and so was
       the search box's: a smaller size and less padding give them the room
       (#2358). The search box keeps its right-hand room for the ✕ only once
       something is typed and the ✕ is showing. */
    #repair-toolbar-selects select { font-size: 0.82rem; padding-inline: 0.4rem 0.2rem; }
    .repair-search-wrap #repair-search { font-size: 0.82rem; }
    .repair-search-wrap:has(.repair-search-clear.hidden) #repair-search { padding-right: 0.5rem; }
}

/* The "← Home" bar (#phone-bar, layout.css) is fixed across the top at 768
   and below, so the bar sticks under it there. The offset used to stop at
   639, and on a tablet held upright the bar slid behind it (#2111). */
@media (max-width: 768px) {
    .repair-toolbar { top: 3.25rem; }
}

/* Phone: the search box and "+ New repair" share the first line; Filters,
   "?" and the counts the second. The dropdowns are in the panel
   (RepairsPage._placeSelects) and the counts scroll sideways rather than
   stack. The line is broken by the bar's ::after, a full-width item with no
   height, so the two lines hold whatever the face or the language: with all
   four on one line a 320 phone had no search box left (#2228). The second
   line's gap is its items' margin, as the break would otherwise add one. */
@media (max-width: 639px) {
    .repair-toolbar {
        padding: 0.5rem; gap: 0 0.4rem;
    }
    .repair-toolbar::after { content: ''; order: 3; flex: 0 0 100%; height: 0; }
    .repair-toolbar .repair-search-wrap { order: 1; flex: 1 1 0; max-width: none; min-width: 4rem; }
    .repair-toolbar-actions { order: 2; margin-left: 0; flex-wrap: nowrap; }
    .repair-filters-btn { order: 4; margin-top: 0.4rem; }
    .repair-toolbar-title { order: 5; margin-top: 0.4rem; font-size: 1rem; }
    .repair-toolbar-title .help-q { margin-left: 0; }
    /* "?" opens the same guide, so the guide button goes. */
    .repair-toolbar-actions a.btn { display: none; }
    .repair-toolbar-actions .repair-new-btn { margin-block: 0; padding-inline: 0.75rem; }
    .repair-toolbar-selects:not(:has(.repair-filter-field)) { display: none; }
    .repair-toolbar .repair-filter-summary {
        order: 6; margin-top: 0.4rem; flex: 1 1 0; flex-wrap: nowrap; overflow-x: auto;
        -webkit-overflow-scrolling: touch; scrollbar-width: thin; padding-bottom: 2px;
    }
    .repair-filters-panel { width: 100%; max-height: 70vh; overflow-y: auto; }
    .repair-date-range { flex-wrap: nowrap; }
    .repair-date-input { flex: 1 1 0; min-width: 0; max-width: none !important; }
}

/* ── At a glance: a row of counts over a list ──────────────────────────────
   Repairs has Overdue, Due today, Awaiting parts and Ready to collect
   (repairs-glance.js); Purchase Orders has Not synced, Synced with errors,
   Discarded and Shop transfers (po_processing.js). Each is a button that
   filters its list, or opens one; the one showing is filled in the accent.
   They take New Repair's Quick jobs row as their model: big, condensed, one
   tap. They scroll away with the list.
   The row answers to its own width, not the screen's (#2239): beside the
   sidebar on an upright iPad it has 240px less than the screen, and tiles
   laid out for the screen broke their labels mid-word ("Overd / ue"). Drawn
   narrow unless the row is wide: the count over its label, four across. A
   label breaks between words only. */
.glance {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: min(0.6rem, 1.2%);
    margin-bottom: 0.75rem;
    container: glance / inline-size;
}
.glance[hidden], .glance:empty { display: none; }
/* Bike Specs has two tiles, Bike and E-Bike: half the row each, and slim
   on a wide row (below). */
#bike-specs-glance { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.glance-tile {
    display: flex; flex-direction: column-reverse; align-items: center; justify-content: center; gap: 2px;
    min-height: 52px; min-width: 0; padding: 0.3rem 0.25rem;
    background: var(--bg-card); border: 1.5px solid var(--border); border-radius: var(--radius);
    color: var(--text-strong, var(--text)); cursor: pointer; text-align: center;
    font-family: 'Barlow Condensed', 'Barlow', sans-serif;
    transition: background 0.12s, border-color 0.12s;
    -webkit-tap-highlight-color: transparent;
}
.glance-tile:hover { border-color: var(--accent); }
.glance-label { font-size: 0.8rem; font-weight: 600; line-height: 1.1; min-width: 0; max-width: 100%; }
.glance-count { font-size: 1.4rem; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
/* The narrowest row, Purchase Orders on a small phone: a little smaller, so
   "Discarded" stays inside its tile rather than breaking in two. */
@container glance (max-width: 480px) {
    .glance-tile { padding-inline: 0.15rem; }
    .glance-label { font-size: 0.72rem; }
}
/* A wide row: the label beside its count, both bigger. 900px is where the
   longest word in either language ("Прострочено") and a three-figure count
   fit side by side in a quarter of the row, in the fallback face too. */
@container glance (min-width: 1000px) {
    .glance-tile {
        flex-direction: row; justify-content: space-between; gap: 0.5rem;
        min-height: 56px; padding: 0.4rem 0.9rem; text-align: left;
    }
    .glance-label { font-size: 1.15rem; font-weight: 700; }
    .glance-count { font-size: 1.8rem; }
}
/* Purchase Orders only: a slimmer wide row. Its tiles head a table rather
   than a board, and at Repairs' size they pushed Recent PO uploads down. */
@container glance (min-width: 1000px) {
    #po-glance .glance-tile { min-height: 40px; padding: 0.2rem 0.9rem; }
    #po-glance .glance-label { font-size: 0.95rem; font-weight: 600; }
    #po-glance .glance-count { font-size: 1.3rem; }
}
/* Purchase Orders' tiles are one quiet style (#2856): no coloured stripe
   per status, the count bold, a count of nothing faded, and only the tile
   that is tapped (.is-on, above) in the accent. Synced with errors and
   Needs a price keep the shared amber while there are any. They sit over
   Recent PO uploads, the list they keep to (back there since 10 Oct; #2856
   had them in the start block over Suppliers); Shop transfers is a box in
   the start row (.po-transfers-btn), not one of them. However many tiles
   there are they share the row, so three do not leave a fourth's gap. */
#po-glance { grid-template-columns: repeat(auto-fit, minmax(6.5rem, 1fr)); }
/* The shared .glance margin spaces them from the list or the heading under them. */
#po-glance { margin-bottom: var(--sp-3); }
/* A phone keeps the wider floor: two and two, not three and one. */
@media (max-width: 600px) {
    #po-glance { grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr)); }
}
#po-glance .glance-tile.is-zero .glance-count { color: var(--text-muted); font-weight: 600; }
#po-glance .glance-tile.is-zero:not(.is-on) .glance-label { color: var(--text-muted); }
/* Bike Specs takes the same slim row, at every width: its two tiles head a
   table too, and at half the row each the label fits beside its count on
   a tablet and a phone as well. */
#bike-specs-glance .glance-tile {
    flex-direction: row; justify-content: space-between; gap: 0.5rem;
    min-height: 40px; padding: 0.2rem 0.9rem; text-align: left;
}
#bike-specs-glance .glance-label { font-size: 0.95rem; font-weight: 600; }
#bike-specs-glance .glance-count { font-size: 1.3rem; }
/* Repairs' tiles, like Purchase Orders': a coloured edge, and a count of
   nothing fades. Overdue is red and Ready to collect green (below, while
   there is something to count); Due today and Awaiting parts are amber and
   blue. */
#repair-glance .glance-tile { border-left: 4px solid var(--stripe, var(--border)); }
#repair-glance [data-glance="overdue"] { --stripe: var(--danger); }
#repair-glance [data-glance="today"]   { --stripe: var(--warning); }
#repair-glance [data-glance="parts"]   { --stripe: var(--accent); }
#repair-glance [data-glance="ready"]   { --stripe: var(--success); }
#repair-glance .glance-tile.is-zero .glance-count,
#repair-glance .glance-tile.is-zero:not(.is-on) .glance-label { color: var(--text-muted); }
#repair-glance .glance-tile.is-on { border-left-color: var(--stripe); }
/* Red and green only while there is something to count. */
.glance-tile--danger { border-color: var(--danger); }
.glance-tile--danger .glance-count { color: var(--danger); }
.glance-tile--ready .glance-count { color: var(--success); }
.glance-tile--warn { border-color: var(--warning); }
.glance-tile--warn .glance-count { color: var(--warning); }
.glance-tile.is-on { background: var(--accent-soft); border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
/* Not on a phone: the compact card there is cut to show four whole jobs on a
   375 x 667 screen (#2105), and the tiles would cost one of them. Overdue and
   Today are in Filters, Awaiting parts and Ready under Status. */
@media (max-width: 639px) {
    #repair-glance { display: none; }
}

/* ── Repairs unified list (single renderer, grid→flex responsive) ────────── */
/* No box round the list: rows on the page, ruled off from each other, as New
   Repair has no box round its form (#2036). */
.repairs-list-wrap {
    /* auto, not hidden: if the columns still don't fit, scroll rather than clip */
    overflow-x: auto;
    container-type: inline-size;
}

.repairs-list-header,
.repair-item {
    display: grid;
    grid-template-columns: 48px 56px minmax(140px, 1.4fr) minmax(180px, 2fr) 130px 90px 130px 120px 90px 104px 130px;
    align-items: center;
    gap: 0.5rem 0.7rem;
    padding: 0.55rem 0.75rem;
}

.repairs-list-header {
    font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--text-muted); font-weight: 600;
    border-bottom: 2px solid var(--border);
    background: var(--bg-card); border-radius: var(--radius) var(--radius) 0 0;
}

.repair-item {
    border-bottom: 1px solid var(--border);
    cursor: pointer; transition: background 0.12s;
    -webkit-tap-highlight-color: transparent;
}
.repair-item:hover { background: var(--accent-soft); }
.repair-item.overdue {
    border-left: 3px solid var(--danger);
    background: rgba(198, 40, 40, 0.05);
    padding-left: calc(0.75rem - 3px);
}
.repair-item.overdue:hover { background: rgba(198, 40, 40, 0.09); }
.repair-item.in-group:not(.overdue) {
    border-left: 3px solid var(--accent-dim, rgba(168, 36, 36, 0.25));
    padding-left: calc(0.75rem - 3px);
}

/* Batch (multi-bike) group header — same markup, restyled per breakpoint */
.repair-group-header {
    font-size: 0.78rem; font-weight: 600; color: var(--text-muted);
    background: var(--bg-subtle, #f0ede8);
    padding: 4px 12px;
    border-top: 2px solid var(--border);
    border-bottom: 1px solid var(--border);
}
/* A group is contiguous but can still straddle a page edge, so the header says
   how much of the drop-off is actually on this page rather than implying all
   of it is (#292). */
.repair-group-header .rgh-split { font-weight: 500; font-style: italic; opacity: 0.85; }

/* Cell visuals (desktop, source-order columns) */
.ri-media img {
    width: 40px; height: 40px; object-fit: cover; border-radius: 5px;
    cursor: zoom-in; display: block;
}
.ri-ticket { color: var(--text-muted); font-size: 0.8rem; white-space: nowrap; }
/* In the desktop list the ticket number and the price are in the condensed
   face, a size up: the two figures staff read a row by. The cards at 768 and
   under keep their own sizes. */
@media (min-width: 769px) {
    .ri-ticket { font: 700 1.1rem/1.2 'Barlow Condensed', 'Barlow', sans-serif; }
    .ri-cost { font: 600 1.15rem/1.2 'Barlow Condensed', 'Barlow', sans-serif; }
    .ri-identity > .ri-customer { font-weight: 600; }
}
.ri-identity { display: flex; flex-direction: column; min-width: 0; overflow: hidden; }
.ri-identity > .ri-customer { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ri-identity > .ri-bike { color: var(--text-muted); font-size: 0.82rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ri-desc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ri-status { display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; min-width: 0; }
/* Dates wrap rather than spill: "In Last Wednesday" is wider than the column
   and ran into the price beside it. */
.ri-in { color: var(--text-muted); font-size: 0.82rem; min-width: 0; overflow-wrap: anywhere; }
.ri-due { min-width: 0; overflow-wrap: anywhere; }
.ri-cost { white-space: nowrap; }
/* The Shopify column is 78 to 104px and its markers are up to 370px
   ("Order cancelled #1419 · not refunded", longer in Ukrainian), so on one
   line they ran under the PAY buttons' column and were cut off, and pushed
   the list sideways (#2139). They wrap inside the column instead; a row
   grows a line or two taller rather than hiding what a job owes.
   !important: the "€… unpaid" marker carries nowrap in its own style. */
.ri-shopify { white-space: normal; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.ri-shopify > a { max-width: 100%; }
.ri-shopify .badge {
    max-width: 100%; white-space: normal !important;
    overflow-wrap: anywhere; text-align: left;
}
.ri-shopify a { text-decoration: none; }
.ri-shopify-none { color: var(--text-muted); }
.ri-actions { display: flex; gap: 4px; align-items: center; white-space: nowrap; }

/* The label is drawn in the Due cell; scoped to .ri-status it never matched,
   so overdue dates were never red (#1157 UAT). */
.ri-due .overdue-label { color: var(--danger); font-weight: 600; }
/* Racing marks (#1502): the lanterne rouge on the oldest open job's booked-in
   date, the flamme rouge on a job due today. Text-sized, so they sit in the
   cell at every tier without changing its width. */
.race-flag { display: inline-flex; width: 1.1em; height: 1.1em; vertical-align: -0.2em; margin-right: 0.25em; flex: none; }
.race-flag svg { width: 100%; height: 100%; }
.race-flag .rf-red   { fill: var(--danger); }
.race-flag .rf-frame { fill: var(--text-muted); }
.race-flag .rf-glow  { fill: #ffd27a; }
/* Neutral service (#1516): the yellow of the race's neutral service car, on
   quick while-you-wait jobs. Dark text on yellow reads in both themes. */
.neutral-service {
    display: inline-block; background: #ffd400; color: #111;
    font: 800 0.66rem/1.4 'Barlow Condensed', 'Barlow', sans-serif;
    letter-spacing: 0.06em; text-transform: uppercase;
    padding: 1px 6px; border-radius: 4px; white-space: nowrap;
}
/* In the list's Status column the chip wraps rather than run into the next
   column: "Нейтральна допомога" is 45px wider than the column (#2139). */
.ri-status .neutral-service { white-space: normal; max-width: 100%; overflow-wrap: anywhere; }

/* Mobile-only cells — hidden on desktop */
.ri-status-select, .ri-edit, .ri-parts, .ri-label, .ri-desc-job { display: none; }

/* ── Touch-friendly filter bar + full-screen repair panel (≤1366px) ─────── */
@media (max-width: 1366px) {
    .repair-preset { padding: 0.5rem 1rem; font-size: 0.88rem; min-height: 40px; }
    #repair-search { font-size: 1rem; min-height: 44px; }
    #repair-location-filter, #repair-status-filter, #repair-payment-filter { font-size: 1rem; min-height: 44px; }
    .repair-filters-btn, .repair-toolbar-actions .btn, .repair-filters-panel .btn { min-height: 44px; }
    .repair-filter-chip { padding: 0.4rem 0.8rem; font-size: 0.88rem; min-height: 36px; }
    .repair-date-input { font-size: 1rem; min-height: 44px; }
    .repair-date-label { font-size: 0.92rem; }
    .modal-content { max-height: 88vh; }

    /* Repair ticket panel opens full-screen on iPad / phone so it's properly readable */
    .repair-panel,
    .repair-panel.expanded { width: 100vw !important; max-width: 100vw; }
}

/* ── Tight-grid rows whenever the list itself is under ~1408px wide ────────
   Keyed on the list's width, not the viewport's: the sidebar and page padding
   eat ~150px, so a 1400px window only gives the list ~1250px — too narrow for
   the full grid. Phones (≤768px) use the card layout below.
   1408px is what the full grid needs with its PAY / MARK COLLECTED column
   (pay-collect.css): columns 1272 + ten gaps 112 + padding 24. It was 1380,
   and a list between the two scrolled sideways (#2056). pay-collect.css
   switches at the same figure; tests/test_repairs_list_wide_desktop_2056.py
   holds the two together and checks the sum. */
@media (min-width: 769px) {
  @container (max-width: 1408px) {
    .repairs-list-header,
    .repair-item {
        grid-template-columns: 34px 44px minmax(90px, 1.1fr) minmax(120px, 1.6fr) 92px 68px 94px 82px 70px 78px 96px;
        gap: 0.3rem 0.4rem;
        padding: 0.45rem 0.55rem;
    }
    .repair-item { font-size: 0.88rem; }
    .repair-item.overdue,
    .repair-item.in-group:not(.overdue) { padding-left: calc(0.55rem - 3px); }
    .ri-media img { width: 32px; height: 32px; }
    .ri-ticket { font-size: 0.95rem; }
    .ri-identity > .ri-customer { font-size: 0.88rem; }
    .ri-identity > .ri-bike { font-size: 0.76rem; }
    .ri-desc { font-size: 0.85rem; }
    .ri-status { font-size: 0.8rem; gap: 0.2rem; }
    .ri-status .badge,
    .ri-priority .badge { font-size: 0.68rem; padding: 2px 5px; }
    .ri-due { font-size: 0.8rem; }
    .ri-in { font-size: 0.76rem; }
    .ri-cost { font-size: 1rem; }
    .ri-shopify .badge { font-size: 0.68rem; padding: 2px 5px; }
    .ri-actions { gap: 3px; }
    .ri-actions .btn,
    .ri-actions .btn-sm { padding: 2px 5px; font-size: 0.72rem; min-height: 0; }
    .ri-actions .btn-pin { padding: 2px 4px; }
  }
}

/* ── Phone / small tablet (≤768px): card layout ──────────────────────────── */
@media (max-width: 768px) {
    .repairs-list-wrap {
        background: transparent; border: none; overflow: visible;
    }
    .repairs-list-header { display: none; }

    /* A wrapping flex row, not a grid: in the old grid the status select sat
       in the photo's column and widened it, which pushed the name and bike
       right and cut them short. Here each line only sizes itself. */
    .repair-item {
        display: flex; flex-wrap: wrap; align-items: center;
        gap: var(--sp-2) var(--sp-3);
        padding: var(--sp-4);
        border: 1px solid var(--border); border-radius: var(--radius);
        margin-bottom: var(--sp-3);
        background: var(--bg-card);
    }
    .repair-item.overdue,
    .repair-item.in-group:not(.overdue) { padding-left: calc(var(--sp-4) - 3px); }
    .repair-item:last-child { border-bottom: 1px solid var(--border); }

    /* Top line: photo (if any), #id, customer and bike, price */
    .ri-media    { order: 1; flex: none; }
    .ri-media:empty { display: none; }
    .ri-media img { width: 44px; height: 44px; cursor: default; }
    .ri-ticket   { order: 2; flex: none; align-self: flex-start; font-size: 0.85rem; line-height: 1.6; }
    .ri-identity { order: 3; flex: 1 1 0; align-self: flex-start; }
    .ri-identity > .ri-customer { font-size: 1.1rem; font-weight: 700; color: var(--text-strong); }
    .ri-identity > .ri-bike {
        font-size: 0.92rem; color: var(--text-muted);
        white-space: normal; text-overflow: clip;
        display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    }
    .ri-cost     {
        order: 4; flex: none; align-self: flex-start; text-align: right;
        font-weight: 700; font-size: 1.1rem; line-height: 1.6;
    }

    /* The job, without the [ebike] tag the intake wizard files it under */
    .ri-desc     {
        order: 5; flex: 1 1 100%;
        font-size: 1rem; font-weight: 500; line-height: 1.35;
        white-space: normal; text-overflow: clip;
        display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
    }
    .ri-desc-full { display: none; }
    .ri-desc-job  { display: inline; }

    /* One status control: the select, in the status's own colours */
    .ri-status   { order: 6; flex: 1 1 0; flex-wrap: nowrap; }
    .ri-status .ri-status-badge { display: none; }
    .ri-status .badge { white-space: nowrap; }
    .ri-status-select {
        display: block; flex: 1 1 auto; min-width: 0;
        min-height: 44px; padding: var(--sp-2) var(--sp-3);
        font-size: 0.95rem; font-weight: 600;
        border: 1px solid var(--border); border-radius: var(--radius);
        cursor: pointer;
    }
    .ri-status-select:not([class*="badge-"]) { background: var(--bg-input); color: var(--text); }
    .ri-priority { order: 7; flex: none; }
    .ri-priority--normal { display: none; }

    /* Dates and the Shopify order, labelled; an empty one isn't shown */
    .ri-due, .ri-in, .ri-shopify {
        flex: none; font-size: 0.9rem; text-align: left; align-items: flex-start;
    }
    /* flex: none would let a long marker push the card wider than a phone
       (#2139: 64px over at 375px in Ukrainian); it wraps inside the card. */
    .ri-shopify { max-width: 100%; }
    .repair-item::before {
        content: ''; order: 8; flex: 1 1 100%; height: 0;
        margin-top: calc(-1 * var(--sp-2));
    }
    .ri-due      { order: 9; }
    .ri-in       { order: 10; color: var(--text-muted); }
    .ri-shopify  { order: 11; }
    .ri-due .ri-label { display: inline; color: var(--text-muted); margin-right: var(--sp-1); }
    .ri-empty    { display: none; }
    .ri-identity > .ri-bike.ri-empty { display: none; }

    /* Parts and Edit share the width; the pin sits at the right */
    .ri-actions  {
        order: 12; flex: 1 1 100%;
        border-top: 1px solid var(--border);
        padding-top: var(--sp-3);
        gap: var(--sp-2);
    }
    .ri-edit, .ri-parts { display: inline-flex; flex: 1 1 0; justify-content: center; }
    .ri-parts    { order: 1; }
    .ri-edit     { order: 2; }
    .ri-actions .btn { min-height: 44px; padding: var(--sp-2) var(--sp-3); font-size: 0.95rem; }
    .ri-actions .btn-pin {
        order: 4; flex: none; opacity: 1;
        min-width: 44px; min-height: 44px; font-size: 1.1rem;
        border-radius: var(--radius);
    }
    .ri-actions .btn-pin.pinned { background: var(--accent-soft); }
    .ri-actions .btn-pin:not(.pinned) { filter: grayscale(1); }
    .ri-actions > .btn:not(.ri-edit):not(.ri-parts) { order: 3; flex: none; }

    .repair-group-header {
        border: 1px solid var(--border);
        border-radius: var(--radius-sm, 6px);
        margin-top: 0.6rem; margin-bottom: 0.25rem;
    }
}


/* ── Repair detail panel ──────────────────────────────────────────────────── */
.repair-panel-overlay {
    position: fixed; inset: 0;
    background: rgba(0,0,0,0.45);
    z-index: 500;
    opacity: 0; pointer-events: none;
    transition: opacity 0.25s;
}
.repair-panel-overlay.open { opacity: 1; pointer-events: auto; }

.repair-panel {
    position: fixed; top: 0; right: 0; bottom: 0;
    width: 560px; max-width: 100vw;
    background: var(--bg-card); border-left: 1px solid var(--border);
    z-index: 501;
    display: flex; flex-direction: column;
    transform: translateX(110%);
    transition: transform 0.28s cubic-bezier(0.4,0,0.2,1), width 0.2s ease;
}
.repair-panel.open { transform: translateX(0); }

/* Wider on large screens */
@media (min-width: 1400px) {
    .repair-panel { width: 720px; }
}

/* Expanded state — fills available content width */
.repair-panel.expanded {
    width: min(960px, calc(100vw - var(--sidebar-w) - 2rem));
}

/* Expand toggle button */
.repair-panel-expand {
    background: none; border: none;
    color: var(--text-muted); cursor: pointer;
    padding: 0.3rem; border-radius: 4px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    transition: color 0.15s, background 0.15s;
}
.repair-panel-expand:hover { color: var(--text); background: var(--bg-hover); }

/* Expand button hidden on mobile — bottom sheet is already full width */
@media (max-width: 767px) {
    .repair-panel-expand { display: none; }
    .repair-panel.expanded { width: 100%; }
}

/* Title wrapper takes remaining space so buttons sit on the right */
.repair-panel-header > div:first-child { flex: 1; min-width: 0; }

.repair-panel-header {
    display: flex; align-items: center; gap: 0.5rem;
    padding: 1.25rem 1.25rem 0.75rem;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}
.repair-panel-title    { font-size: 1.1rem; font-weight: 600; }
.repair-panel-subtitle { font-size: 0.88rem; color: var(--text-muted); margin-top: 0.15rem; }
/* The customer's and the bike's names in the heading are buttons (#2197):
   the customer's opens Customer history, the bike's opens Edit bike. They
   read as the text they replace, with a dotted line under to say "tap me".
   A long name wraps rather than push the ✕ off the panel. */
.repair-panel-title, .repair-panel-subtitle { overflow-wrap: anywhere; }
.rp-head-link {
    background: none; border: 0; padding: 0; margin: 0;
    font: inherit; color: inherit; text-align: left; cursor: pointer;
    overflow-wrap: anywhere;
    text-decoration: underline dotted var(--text-muted); text-underline-offset: 0.2em;
}
.rp-head-link:hover, .rp-head-link:focus-visible { color: var(--accent); text-decoration-color: currentColor; }
.rp-head-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.repair-panel-close {
    background: none; border: none;
    color: var(--text-muted); font-size: 1.6rem; line-height: 1;
    cursor: pointer; padding: 0 0.25rem; border-radius: 4px; flex-shrink: 0;
    transition: color 0.15s, background 0.15s;
}
.repair-panel-close:hover { color: var(--text); background: var(--bg-hover); }

/* Share-history inline button in panel header */
#rp-share-history-btn {
    font-size: 0.8rem; padding: 0.3rem 0.65rem;
    flex-shrink: 0; white-space: nowrap;
}

.repair-panel-tabs {
    display: flex; border-bottom: 1px solid var(--border); flex-shrink: 0;
}
.repair-panel-tab {
    flex: 1; padding: 0.7rem 1rem;
    background: none; border: none; border-bottom: 2px solid transparent;
    color: var(--text-muted); font-size: 0.95rem; font-weight: 500;
    cursor: pointer; transition: color 0.15s, border-color 0.15s;
}
.repair-panel-tab:hover  { color: var(--text); }
.repair-panel-tab.active { color: var(--accent); border-bottom-color: var(--accent); }

.repair-panel-body {
    flex: 1; overflow-y: auto;
    padding: 1.25rem;
}

/* Details tab: full-width fields, dates and total as tiles */
.rp-details { display: flex; flex-direction: column; gap: 1.1rem; }
.rp-details .form-group { margin-bottom: 0; min-width: 0; }
.rp-grid { display: grid; gap: 0.9rem; }
.rp-grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.rp-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 560px) {
    .rp-grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .rp-tile-total { grid-column: 1 / -1; }
}
.rp-textarea, .rp-select {
    width: 100%; box-sizing: border-box;
    padding: 0.6rem 0.75rem; background: var(--bg-input); color: var(--text);
    border: 1px solid var(--border); border-radius: var(--radius);
    font: inherit; font-size: 0.95rem; line-height: 1.45;
}
.rp-textarea { resize: vertical; min-height: 2.8rem; }
.rp-textarea:focus, .rp-select:focus { outline: none; border-color: var(--accent); }

.rp-tile {
    position: relative; display: flex; flex-direction: column; gap: 0.2rem;
    padding: 0.7rem 0.85rem; min-width: 0;
    background: var(--bg-input); border: 1px solid var(--border); border-radius: var(--radius-lg);
}
.rp-tile-label {
    font-size: 0.75rem; font-weight: 600; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: 0.04em;
}
.rp-tile-sub { font-size: 0.8rem; color: var(--text-muted); }
.rp-tile-sub.rp-warn, .rp-warn { color: var(--warning); font-weight: 600; }
.rp-tile-sub.rp-bad { color: var(--danger); font-weight: 600; }

.rp-date { cursor: pointer; transition: border-color 0.15s; }
.rp-date:hover, .rp-date:focus-within { border-color: var(--accent); }
.rp-date-main { font-size: 1.05rem; font-weight: 600; color: var(--text); }
.rp-date-empty { color: var(--text-muted); font-weight: 500; }
/* The real input covers the tile, invisible, so any tap reaches it */
.rp-date input[type="date"] {
    position: absolute; inset: 0; width: 100%; height: 100%;
    opacity: 0; cursor: pointer; border: 0; padding: 0; margin: 0;
}
.rp-date input[type="date"]::-webkit-calendar-picker-indicator {
    position: absolute; inset: 0; width: 100%; height: 100%; cursor: pointer;
}

.rp-total-row { display: flex; align-items: baseline; gap: 0.15rem; }
.rp-total-cur { font-size: 1.05rem; font-weight: 600; color: var(--text); }
.rp-details input.rp-total {
    width: 100%; min-width: 0; padding: 0; border: 0; background: transparent;
    font: inherit; font-size: 1.05rem; font-weight: 600; color: var(--text);
    height: auto; line-height: 1.3;
    cursor: default; -moz-appearance: textfield; appearance: textfield;
}
.rp-total::-webkit-outer-spin-button, .rp-total::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.rp-details input.rp-total:focus { outline: none; }

.rp-hold {
    display: flex; align-items: center; gap: 0.75rem; cursor: pointer;
    padding: 0.7rem 0.85rem; border: 1px solid var(--border); border-radius: var(--radius-lg);
}
.rp-hold:has(input:checked) { border-color: var(--warning); background: var(--warning-soft); }
.rp-hold input { width: 1.1rem; height: 1.1rem; accent-color: var(--warning); flex-shrink: 0; }
.rp-hold-text { display: flex; flex-direction: column; font-size: 0.95rem; }
.rp-hold-text span { font-size: 0.8rem; color: var(--text-muted); }

.repair-panel-footer {
    padding: 0.9rem 1.25rem;
    border-top: 1px solid var(--border);
    display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap;
    flex-shrink: 0;
}

/* Paid cash / Paid card / Split: three equal tiles on their own row, icon
   over label, so the method reads at arm's length at the counter. */
.rp-pay-tiles {
    flex: 1 1 100%; order: -1;
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.6rem;
}
/* Cash and card are quiet outlines, one colour each (tokens.css --pay-*,
   #2007): a transparent tile, a 2px line and the label in capitals, as on
   New Repair's "Paying now?". Split stays slate: a neutral wash whose icon
   is the note in green and the card in blue. */
.rp-pay-tile {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 0.3rem; min-height: 5.5rem; padding: 0.4rem 0.3rem;
    --pay-line: color-mix(in srgb, var(--text-muted) 50%, transparent);
    --pay-text: var(--text-strong);
    --pay-icon: var(--text-muted);
    --pay-bg: color-mix(in srgb, var(--text-muted) 11%, var(--surface-raised));
    --pay-bg-hover: color-mix(in srgb, var(--text-muted) 20%, var(--surface-raised));
    border: 2px solid var(--pay-line);
    border-radius: var(--radius-lg);
    background: var(--pay-bg); color: var(--pay-text);
    font-size: 0.95rem; font-weight: 700; line-height: 1.15; text-align: center;
    text-transform: uppercase; letter-spacing: 0.02em; white-space: nowrap;
    transition: background-color 0.12s, border-color 0.12s;
}
/* A touch bigger than it was (2.4rem). The padding gave up what the icon
   took, so the tile is the height it was. */
.rp-pay-icon {
    width: 2.8rem; height: 2.8rem; flex-shrink: 0;
    color: var(--pay-icon);
}
.rp-pay-cash, .rp-pay-card {
    --pay-bg: transparent; --pay-icon: var(--pay-text);
    --pay-bg-hover: color-mix(in srgb, var(--pay-line) 16%, transparent);
}
.rp-pay-cash { --pay-line: var(--pay-cash-line); --pay-text: var(--pay-cash-text); }
.rp-pay-card { --pay-line: var(--pay-card-line); --pay-text: var(--pay-card-text); }
/* The card overlaps the note in Split's icon, so its fill is the tile's own surface. */
.rp-pay-split .rp-pay-icon > :nth-child(-n+2) { stroke: var(--pay-cash-text); }
.rp-pay-split .rp-pay-icon > :nth-child(n+3)  { stroke: var(--pay-card-text); }
.rp-pay-split .rp-pay-icon > rect:nth-child(3) { fill: var(--pay-bg); }
.rp-pay-tile:hover:not(:disabled) { --pay-bg: var(--pay-bg-hover); }
.rp-pay-split:hover:not(:disabled) { border-color: var(--text-muted); }
/* The pressed tile while Shopify answers: a spinner where the icon was. It
   keeps full strength so it stands out from the two dimmed beside it. */
.rp-pay-spinner {
    box-sizing: border-box; border: 3px solid currentColor; border-top-color: transparent;
    border-radius: 50%; animation: rp-pay-spin 0.7s linear infinite;
}
.rp-pay-tile.is-busy:disabled { opacity: 1; cursor: progress; }
@keyframes rp-pay-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .rp-pay-spinner { animation-duration: 2.4s; } }
/* A card's PAY pressed and waiting on the server (#2130; the button itself is
   pay-collect.css's): a spinner over the button,
   which keeps its size (the words are hidden, not removed) and its full
   strength, so it stands out from the other PAY buttons dimmed beside it. */
.ri-paid.is-busy, .ri-paid.is-busy:disabled { position: relative; color: transparent; opacity: 1; cursor: progress; }
.ri-paid.is-busy::after {
    content: ''; position: absolute; left: 50%; top: 50%;
    width: 1em; height: 1em; margin: -0.5em 0 0 -0.5em; box-sizing: border-box;
    border: 2px solid var(--pay-line); border-top-color: transparent; border-radius: 50%;
    animation: rp-pay-spin 0.7s linear infinite;
}
@media (prefers-reduced-motion: reduce) { .ri-paid.is-busy::after { animation-duration: 2.4s; } }

/* Mobile: bottom sheet */
@media (max-width: 767px) {
    .repair-panel {
        top: auto; left: 0; right: 0; bottom: 0;
        width: 100%; height: 92vh;
        border-left: none; border-top: 1px solid var(--border);
        border-radius: 14px 14px 0 0;
        transform: translateY(110%);
    }
    .repair-panel.open { transform: translateY(0); }

    .repair-panel-footer { padding-bottom: max(0.9rem, env(safe-area-inset-bottom)); }

    /* Parts & Labour panel — compact on small screens */
    .custom-part-row { flex-wrap: wrap; }
    .custom-part-row .input-search:first-child { flex: 1 1 100%; }
    .custom-part-row .input-search:nth-child(2) { flex: 1; width: auto; }

    .labour-card { min-width: 44px; padding: 0.4rem 0.5rem; }
    .labour-card-label { font-size: 0.82rem; }
    .labour-card-sub   { font-size: 0.68rem; }

    /* General panel body padding tightened for mobile */
    .repair-panel-body { padding: 0.85rem 0.9rem; }

    /* Header — tighter on mobile so more vertical room for tabs/body */
    .repair-panel-header {
        padding: 0.75rem 0.9rem 0.5rem;
        gap: 0.35rem;
    }
    .repair-panel-title { font-size: 1rem; }
    /* A thumb's height for each name in the heading (#2197), and a row of
       their own: beside Pin, Share and New link the names had 37px at 320
       wide and broke up letter by letter. The names and the ✕ take the first
       row; the buttons go under them. The names' own height stands in for
       the padding that was there. */
    .rp-head-link { display: inline-flex; align-items: center; min-height: 44px; }
    .repair-panel-subtitle { margin-top: 0; }
    .repair-panel-header { flex-wrap: wrap; padding-top: 0.3rem; padding-bottom: 0.4rem; }
    .repair-panel-header > div:first-child { flex: 1 1 calc(100% - 3.5rem); }
    .repair-panel-header > .repair-panel-close { order: 1; align-self: flex-start; min-height: 44px; }
    .repair-panel-header > button:not(.repair-panel-close) { order: 2; }

    /* Tabs — horizontal scroll, never wrap */
    .repair-panel-tabs {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        flex-shrink: 0;
    }
    .repair-panel-tabs::-webkit-scrollbar { display: none; }
    .repair-panel-tab {
        flex: none;
        white-space: nowrap;
        font-size: 0.85rem;
        padding: 0.6rem 0.75rem;
    }

    /* Footer — primary action spans full width; secondaries inline below */
    .repair-panel-footer { flex-wrap: wrap; }
    .repair-panel-footer .btn-primary { flex: 1 1 100%; order: -1; }
    .repair-panel-footer .btn-secondary { flex: 1; }

    /* The repair list's phone cards are all in the ≤768px block above */
}

/* ── Parts list in panel ─────────────────────────────────────────────────── */
/* Each part of the tab sits in its own card: the job, adding a part, labour */
.parts-card {
    padding: 0.9rem 1rem; margin-bottom: 1rem;
    background: var(--bg-input); border: 1px solid var(--border); border-radius: var(--radius-lg);
}
.parts-card:last-child { margin-bottom: 0; }
.parts-list-item {
    display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
    padding: 0.6rem 0; border-bottom: 1px solid var(--border);
}
.parts-list-item:first-child { padding-top: 0.1rem; }
.parts-list-item-info  { flex: 1 1 12rem; min-width: 0; }
.parts-list-item-name  { font-size: 0.95rem; font-weight: 500; color: var(--text);
                          overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.parts-list-item-meta  { font-size: 0.78rem; color: var(--text-muted); margin-top: 0.1rem; }
/* A part priced on the job above Shopify's price: what the draft charges (#2019).
   Wraps, where the name above it is cut to one line. */
.parts-shopify-price   { font-size: 0.78rem; font-weight: 600; color: var(--warning); margin-top: 0.15rem;
                          overflow-wrap: anywhere; }
.rp-shopify-price      { font-size: 0.85rem; color: var(--text); margin: 0.1rem 0 0.75rem;
                          padding: 0.5rem 0.65rem; border-left: 3px solid var(--warning);
                          background: var(--bg-muted, rgba(127,127,127,0.08)); border-radius: 4px;
                          overflow-wrap: anywhere; }
.rp-shopify-price strong { display: block; }
.parts-list-item-price { white-space: nowrap; font-size: 0.95rem; font-weight: 600; color: var(--text);
                          min-width: 4.5rem; text-align: right; font-variant-numeric: tabular-nums; }
.parts-list-item-actions { display: flex; align-items: center; gap: 0.35rem; margin-left: auto; }
/* Phone: name and price on one line, status and buttons under them */
@media (max-width: 560px) {
    /* The ::after is a line break after the price. Status and buttons share
       the next line when they fit, and the buttons drop to a line of their
       own when they don't (a long status, a narrow phone): as a grid they
       ran under the status once − and + were thumb-sized (#2196). */
    .parts-list-item { column-gap: 0.6rem; row-gap: 0.2rem; }
    .parts-list-item::after  { content: ''; flex: 0 0 100%; order: 2; }
    .parts-list-item--summary::after { display: none; }
    .parts-list-item-info    { order: 1; flex: 1 1 0; }
    .parts-list-item-price   { order: 1; min-width: 0; }
    .parts-list-item > .badge { order: 3; }
    .parts-list-item-actions { order: 3; gap: 0.6rem; }
    .parts-list-item-actions .parts-qty-step { width: 44px; height: 44px; font-size: 1.15rem; }
    .parts-list .parts-list-item--summary .parts-list-item-price { margin-right: 0; }
    .parts-add-section .custom-part-row { flex-wrap: wrap; }
    .parts-add-section .custom-part-row .input-search:first-child { flex: 1 1 100%; }
    .parts-add-section .custom-part-row .input-search:nth-child(2) { flex: 1; width: auto; }
}
.parts-qty {
    display: inline-block; padding: 0 0.35rem; margin-right: 0.15rem;
    border-radius: var(--radius-sm); background: var(--accent); color: #fff;
    font-size: 0.78rem; font-weight: 700; vertical-align: 1px;
}
.parts-list-item--labour .parts-list-item-name::before { content: '🔧 '; }
.parts-list-item--summary .parts-list-item-name,
.parts-list-item--summary .parts-list-item-price { color: var(--text-muted); font-weight: 500; }
.parts-list-item--summary .parts-list-item-price { margin-right: 1.9rem; }
.parts-no-price        { color: var(--warning); font-weight: 600; }
.parts-empty           { color: var(--text-muted); font-size: 0.9rem;
                          text-align: center; padding: 1rem 0; }
.parts-list-total      { display: flex; justify-content: space-between; align-items: baseline;
                          padding: 0.75rem 0 0;
                          font-size: 1.05rem; font-weight: 700; color: var(--text); }
.parts-list-total-amount { display: flex; flex-direction: column; align-items: flex-end;
                            font-size: 1.25rem; font-variant-numeric: tabular-nums; }
.parts-list-total-amount .parts-no-price { font-size: 0.78rem; }

/* ── Repair timeline tab ─────────────────────────────────────────────────── */
/* Stage profile at the top of the Timeline tab (#1503): the job's journey
   as a race stage, the road in the shop's second team colour. */
.sp-wrap { border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-card); padding: 0.5rem 0.6rem 0.2rem; margin-bottom: 0.9rem; overflow-x: auto; }
.sp-head { font-size: 0.72rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-muted); position: sticky; left: 0; }
/* Scaled to a phone the labels would shrink past reading; below this width
   the drawing keeps its size and scrolls sideways in its own box instead. */
.sp-svg { display: block; width: 100%; min-width: 520px; height: auto; }
.sp-area { fill: var(--team-2, #1fa35c); opacity: 0.16; }
.sp-line { fill: none; stroke: var(--team-2, #1fa35c); stroke-width: 3; stroke-linejoin: round; stroke-linecap: round; }
.sp-tick { stroke: var(--border); stroke-dasharray: 2 3; }
.sp-stop { font: 600 12px 'Barlow', sans-serif; fill: var(--text); }
.sp-when { font: 400 10.5px 'Barlow', sans-serif; fill: var(--text-muted); }
.sp-col  { font: italic 800 13px 'Barlow Condensed', 'Barlow', sans-serif; letter-spacing: 0.04em; fill: var(--text); }
.sp-now  { fill: var(--team-2, #1fa35c); stroke: var(--bg-card); stroke-width: 3; }
.sp-now-label { font: italic 800 12px 'Barlow Condensed', 'Barlow', sans-serif; fill: var(--team-2, #1fa35c); }
.sp-finish > rect { fill: var(--text); }
.sp-finish .sp-check { fill: var(--bg-card); stroke: var(--text); stroke-width: 0.6; }
.sp-finish .sp-check--dark { fill: var(--text); }

.tl-list { display: flex; flex-direction: column; padding: 0.25rem 0; }
.tl-day {
    font-size: 0.75rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--text-muted); padding: 0.9rem 0 0.35rem;
}
.tl-day:first-child { padding-top: 0; }
.tl-row  { position: relative; display: flex; gap: 0.75rem; padding: 0.5rem 0; }
/* The rail joining one event to the next within a day */
.tl-row::before {
    content: ''; position: absolute; left: 0.95rem; top: 2.3rem; bottom: -0.5rem;
    width: 2px; background: var(--border);
}
.tl-row:last-child::before, .tl-row:has(+ .tl-day)::before { display: none; }
.tl-icon {
    width: 1.9rem; height: 1.9rem; flex-shrink: 0; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: var(--bg-input); border: 1px solid var(--border); font-size: 0.9rem;
}
.tl-content { flex: 1; min-width: 0; padding-top: 0.2rem; }
.tl-header  { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap;
              font-size: 0.9rem; }
.tl-label   { font-weight: 600; color: var(--text); }
.tl-actor   { background: var(--bg-input); color: var(--text-muted);
              font-size: 0.75rem; padding: 0.1rem 0.45rem; border-radius: var(--radius-pill); }
.tl-time    { margin-left: auto; color: var(--text-muted); font-size: 0.8rem;
              white-space: nowrap; font-variant-numeric: tabular-nums; }
.tl-part    { display: block; font-size: 0.85rem; color: var(--text);
              margin-top: 0.2rem; }
.tl-change  { font-size: 0.85rem; margin-top: 0.2rem; }
.tl-old     { color: var(--text-muted); text-decoration: line-through; }
.tl-arrow   { color: var(--text-muted); }
.tl-new     { color: var(--accent); font-weight: 500; }
.tl-note-row { font-size: 0.8rem; color: var(--text-muted); margin-top: 0.15rem; }

/* ── Repair parts buckets ─────────────────────────────────────────────────── */
.bucket-tabs {
    display: flex; flex-wrap: wrap; gap: 0.3rem;
    margin-bottom: 0.6rem;
}
.bucket-tab {
    padding: 0.25rem 0.7rem;
    background: var(--bg-hover); border: 1px solid var(--border);
    border-radius: 999px; cursor: pointer;
    font-size: 0.8rem; color: var(--text-muted);
    transition: background 0.15s, border-color 0.15s, color 0.15s;
    white-space: nowrap;
}
.bucket-tab:hover       { background: var(--bg-card); color: var(--text); }
.bucket-tab--active     { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }
.bucket-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
    gap: 0.4rem;
    margin-bottom: 0.9rem;
}
.bucket-card {
    display: flex; flex-direction: column; align-items: flex-start;
    padding: 0.45rem 0.55rem;
    background: var(--bg-hover); border: 1px solid var(--border);
    border-radius: var(--radius); cursor: pointer;
    text-align: left; transition: background 0.15s, border-color 0.15s;
}
.bucket-card:hover           { background: var(--accent); border-color: var(--accent); color: #fff; }
.bucket-card:hover .bucket-card-price { color: rgba(255,255,255,.85); }
.bucket-card--out            { opacity: 0.5; }
.bucket-card-name            { font-size: 0.78rem; color: var(--text); line-height: 1.3; margin-bottom: 0.25rem; }
.bucket-card:hover .bucket-card-name { color: #fff; }
.bucket-card-footer          { display: flex; align-items: center; gap: 0.3rem; width: 100%; }
.bucket-card-price           { font-size: 0.75rem; color: var(--text-muted); font-weight: 600; }
.bucket-empty                { font-size: 0.8rem; color: var(--text-muted); padding: 0.4rem 0; }
/* The part's picture across the top of its card. A card with none keeps the
   space, so the names in a row of cards start level. 40px high: at 56px a
   three-line card was 159px against 98px with no picture; at 40px it is 143px.
   Below that the picture is too small to tell parts apart (at 32px a quick
   link is a speck). */
.bucket-card-pic {
    display: block; width: 100%; height: 40px; margin-bottom: 0.3rem;
    background: var(--bg-card); border-radius: 4px; overflow: hidden;
}
.bucket-card-pic img { display: block; width: 100%; height: 100%; object-fit: contain; }

/* ── Product search typeahead ─────────────────────────────────────────────── */
.product-search-wrap { position: relative; margin-bottom: 0.75rem; }
.product-search-dropdown {
    position: absolute; top: 100%; left: 0; right: 0;
    background: var(--bg-card); border: 1px solid var(--border);
    border-radius: var(--radius); z-index: 600;
    max-height: 220px; overflow-y: auto;
    box-shadow: 0 4px 20px rgba(0,0,0,.12);
    margin-top: 3px;
}
.product-search-item {
    padding: 0.6rem 0.85rem; cursor: pointer;
    border-bottom: 1px solid var(--border); font-size: 0.9rem;
    transition: background 0.1s;
}
.product-search-item:last-child { border-bottom: none; }
.product-search-item:hover      { background: var(--bg-hover); }
.product-search-item-sub        { font-size: 0.78rem; color: var(--text-muted); margin-top: 0.1rem; }

/* ── Supplier part photos in the repairs parts search ────────────────────── */
.product-search-item--pic { display: flex; align-items: center; gap: 0.6rem; }
.part-thumb {
    flex: 0 0 40px; width: 40px; height: 40px; padding: 0;
    border: 1px solid var(--border); border-radius: 4px;
    background: var(--bg-card); overflow: hidden;
}
.part-thumb img { display: block; width: 100%; height: 100%; object-fit: contain; }
.part-thumb-body { flex: 1; min-width: 0; }
.part-thumb--picked { flex-basis: 64px; width: 64px; height: 64px; cursor: zoom-in; }
.part-thumb--picked:hover { border-color: var(--accent); }
/* The Cache page's Photo column: the square only, no wider than it needs. */
.cache-photo-cell { width: 1%; }
/* 28px, not the 40px of the part lists: measured at 1600px wide, a row was
   46px before the column and 58px with a 40px photo. 28px is the largest
   that leaves it at 46px (30px makes it 48). */
.cache-photo-cell .part-thumb { display: block; width: 28px; height: 28px; }

/* ── Selected product chip ───────────────────────────────────────────────── */
.rp-selected-product {
    display: flex; align-items: center; gap: 0.5rem;
    padding: 0.55rem 0.85rem; margin-bottom: 0.75rem;
    background: var(--bg-input); border-radius: var(--radius);
    font-size: 0.9rem;
}
.rp-selected-product-name { font-weight: 500; flex: 1; min-width: 0;
                             overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rp-selected-product-sub  { font-size: 0.8rem; color: var(--text-muted); white-space: nowrap; }
.rp-selected-product-clear {
    background: none; border: none; cursor: pointer;
    color: var(--text-muted); font-size: 1.25rem; line-height: 1;
    padding: 0; margin-left: auto; flex-shrink: 0;
    transition: color 0.15s;
}
.rp-selected-product-clear:hover { color: var(--text); }

.parts-list-item-delete {
    flex-shrink: 0;
    background: none; border: none; cursor: pointer;
    color: var(--ink-red); font-size: 0.9rem; line-height: 1;
    padding: 0.2rem 0.4rem; border-radius: 4px;
    transition: color 0.15s, background 0.15s;
}
.parts-list-item-delete:hover { color: var(--ink-red-deep); background: rgba(198,40,40,0.12); }
/* − and + on a part's line (#2174) */
.parts-qty-step {
    flex-shrink: 0; width: 1.9rem; height: 1.9rem;
    background: none; border: 1px solid var(--border); border-radius: var(--radius-sm);
    color: var(--text); font-size: 1rem; line-height: 1; cursor: pointer;
}
.parts-qty-step:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.parts-qty-step:disabled { opacity: 0.35; cursor: default; }
/* ── Parts add section ────────────────────────────────────────────────────── */
.parts-add-section .input-search { max-width: none; background: var(--bg-card); }
.btn-add-part-toggle {
    display: inline-flex; align-items: center; gap: 0.35rem;
    padding: 0.3rem 0.85rem;
    background: none; border: 1px solid var(--accent);
    border-radius: var(--radius); cursor: pointer;
    font-size: 0.85rem; color: var(--accent);
    margin-bottom: 0.75rem;
    transition: background 0.15s, color 0.15s;
}
.btn-add-part-toggle:hover { background: var(--accent); color: #fff; }
/* ── Stock level badges (repairs parts search) ───────────────────────────── */
.product-search-item-row {
    display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
}
.stock-badge {
    flex-shrink: 0; font-size: 0.72rem; font-weight: 600;
    padding: 0.15rem 0.5rem; border-radius: 999px; white-space: nowrap;
}
.stock-badge--ok  { background: rgba(22, 163, 74, 0.12);  color: var(--ink-green); }
.stock-badge--low { background: rgba(194, 65, 12, 0.12);  color: var(--ink-orange); }
.stock-badge--out { background: rgba(198, 40, 40, 0.12);  color: var(--ink-red); }

/* ── Source badges (unified product+supplier search) ─────────────────────── */
.source-badge {
    flex-shrink: 0; font-size: 0.68rem; font-weight: 600;
    padding: 0.1rem 0.45rem; border-radius: 999px; white-space: nowrap;
    margin-left: auto;
}
.source-badge--shopify  { background: rgba(149, 191, 71, 0.15); color: var(--ink-olive); }
.source-badge--supplier { background: rgba(59, 130, 246, 0.12); color: var(--ink-blue-2); }
.product-search-divider {
    padding: 0.3rem 0.85rem; font-size: 0.72rem; font-weight: 600;
    color: var(--text-muted); background: var(--bg-hover);
    border-bottom: 1px solid var(--border); text-transform: uppercase;
    letter-spacing: 0.04em;
}


/* ── Parts & Labour tab redesign ─────────────────────────────────────── */
.parts-section-label {
    font-size: 0.72rem; font-weight: 700; letter-spacing: 0.08em;
    text-transform: uppercase; color: var(--text-muted);
    margin-bottom: 0.75rem;
}

.custom-part-row {
    display: flex; gap: 0.5rem; align-items: center;
}
.custom-part-row .input-search:first-child { flex: 1; }
.custom-part-row .input-search:nth-child(2) { width: 110px; }

.common-parts-pills {
    display: flex; flex-wrap: wrap; gap: 0.35rem; margin-bottom: 0.75rem;
}
.common-part-pill {
    display: inline-flex; align-items: center; gap: 0.3rem;
    padding: 0.3rem 0.65rem;
    border: 1px solid var(--border); border-radius: 999px;
    background: var(--bg-hover); color: var(--text);
    font-size: 0.8rem; cursor: pointer; white-space: nowrap;
    transition: background 0.15s, border-color 0.15s;
}
.common-part-pill:hover { background: var(--accent); border-color: var(--accent); color: #fff; }
/* A quick search, not a priced part: outlined so it doesn't read as one. */
.common-part-pill--search { background: transparent; }
.common-part-pill:hover .common-part-pill-price { color: rgba(255,255,255,.85); }
.common-part-pill:active { transform: scale(0.96); }
.common-part-pill-price { font-size: 0.74rem; color: var(--text-muted); }
.parts-section-sublabel {
    font-size: 0.7rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--text-muted); margin: 0.25rem 0 0.4rem;
}

.labour-cards {
    display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 0.5rem;
}
.labour-card {
    display: flex; flex-direction: column; align-items: center;
    padding: 0.6rem 0.5rem; min-width: 0;
    background: var(--bg-hover); border: 1px solid var(--border);
    border-radius: var(--radius); cursor: pointer;
    transition: background 0.15s, border-color 0.15s, transform 0.1s;
    user-select: none;
}
.labour-card:hover   { background: var(--bg-input); border-color: var(--accent); }
.labour-card:active  { transform: scale(0.95); }
.labour-card--rate   { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, var(--bg-hover)); }
.labour-card-label   { font-size: 0.88rem; font-weight: 600; color: var(--text); line-height: 1.2; }
.labour-card-sub     { font-size: 0.72rem; color: var(--text-muted); margin-top: 1px; }

#rp-add-part-form { padding-top: 0.25rem; }

/* ── Job Checklist (ticket panel) ─────────────────────────────────────────── */
.job-list {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.job-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 10px;
    border-radius: 6px;
    background: var(--bg-hover);
    transition: background 0.1s;
}
.job-item:hover { background: color-mix(in srgb, var(--accent) 8%, var(--bg-hover)); }
.job-item.job-done { opacity: 0.65; }
.job-check-label {
    display: flex;
    align-items: center;
    gap: 10px;
    cursor: pointer;
    flex: 1;
    font-size: 0.92rem;
}
.job-check-label input[type="checkbox"] { width: 16px; height: 16px; flex-shrink: 0; cursor: pointer; }
.job-add-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 4px 0;
}
.btn-icon-sm {
    background: none;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    padding: 2px 6px;
    border-radius: 4px;
    font-size: 0.8rem;
    line-height: 1;
    flex-shrink: 0;
}
.btn-icon-sm:hover { color: var(--danger); background: rgba(198, 40, 40, 0.12); }

/* ── Job Card Templates (settings) ────────────────────────────────────────── */
.loc-toggle-wrap { display: inline-flex; align-items: center; gap: 0.6rem; cursor: pointer; user-select: none; }
.loc-toggle-label { font-size: 0.875rem; font-weight: 500; color: var(--text-muted); transition: color 0.15s; }
.loc-toggle-label.loc-toggle-active { color: var(--text); font-weight: 700; }
.loc-toggle-track { position: relative; width: 2.5rem; height: 1.35rem; background: #374151; border-radius: 999px; flex-shrink: 0; transition: background 0.2s; }
.loc-toggle-input { position: absolute; opacity: 0; width: 0; height: 0; }
.loc-toggle-thumb { position: absolute; top: 0.175rem; left: 0.175rem; width: 1rem; height: 1rem; background: #fff; border-radius: 50%; transition: transform 0.2s; pointer-events: none; }
.loc-toggle-input:checked ~ .loc-toggle-thumb { transform: translateX(1.15rem); }
.jt-container { display: flex; flex-direction: column; gap: 16px; margin-top: 12px; }
.jt-type {
    border: 1px solid var(--border);
    border-radius: 8px;
    overflow: hidden;
}
.jt-type.jt-inactive { opacity: 0.5; }
.jt-type-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    background: var(--bg-hover);
    border-bottom: 1px solid var(--border);
}
.jt-type-name { font-weight: 600; font-size: 0.95rem; flex: 1; }
.jt-type-badge {
    font-size: 0.75rem;
    color: var(--text-muted);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 1px 7px;
}
.jt-type-actions { flex-shrink: 0; }
.jt-item-list { list-style: none; padding: 6px 14px; display: flex; flex-direction: column; gap: 2px; }
.jt-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 5px 4px;
    font-size: 0.88rem;
    border-radius: 4px;
}
.jt-item:hover { background: var(--bg-hover); }
.jt-item-label { flex: 1; color: var(--text); }
.jt-add-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px 12px;
    border-top: 1px solid var(--border);
    margin-top: 4px;
}



/* ── Barcode scanner button & modal ─────────────────────────────────────── */
.product-search-row {
    display: flex; align-items: stretch; gap: 0.4rem;
}
.product-search-row .input-search {
    flex: 1 1 auto; min-width: 0;
}
/* The part search's box, with a ✕ inside once something's typed. */
.rp-part-search-box { position: relative; flex: 1 1 auto; min-width: 0; display: flex; }
.rp-part-search-box #rp-part-search { width: 100%; padding-right: 2.2rem; }
.rp-part-search-clear {
    position: absolute; right: 0.35rem; top: 50%; transform: translateY(-50%);
    border: none; background: none; color: var(--text-muted); cursor: pointer;
    font-size: 0.95rem; padding: 0.25rem 0.45rem; border-radius: 50%;
}
.rp-part-search-clear:hover { color: var(--text); background: var(--bg-hover, rgba(0,0,0,0.06)); }
.btn-barcode-scan {
    flex: 0 0 auto;
    display: flex; align-items: center; justify-content: center;
    padding: 0 0.65rem;
    background: var(--bg-card); border: 1px solid var(--border);
    border-radius: var(--radius); color: var(--text-muted);
    cursor: pointer; transition: background 0.15s, color 0.15s;
    line-height: 1;
}
.btn-barcode-scan:hover {
    background: var(--accent); color: #fff; border-color: var(--accent);
}

.btn-supplier-toggle {
    flex: 0 0 auto;
    padding: 0 0.65rem;
    background: var(--bg-card); border: 1px solid var(--border);
    border-radius: var(--radius); color: var(--text-muted);
    font: inherit; font-size: 0.85rem;
    cursor: pointer; transition: background 0.15s, color 0.15s;
}
.btn-supplier-toggle[aria-pressed="true"] {
    background: var(--accent); color: #fff; border-color: var(--accent);
}

/* ── Pin button ─────────────────────────────────────────────────────────── */
.btn-pin {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 0.9rem;
    padding: 0 3px;
    opacity: 0.2;
    transition: opacity 0.15s;
    vertical-align: middle;
}
.btn-pin:hover { opacity: 0.6; }
.btn-pin.pinned { opacity: 1; }

/* ── Repair photo gallery ───────────────────────────────────────────────── */
.photos-actions {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.6rem;
    margin-bottom: 1rem;
}
.photos-actions .btn { justify-content: center; cursor: pointer; }
.photos-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 0.6rem;
}
.photo-thumb-wrap { position: relative; aspect-ratio: 1; }
.photo-thumb {
    width: 100%; height: 100%; object-fit: cover; display: block;
    border-radius: var(--radius-lg); border: 1px solid var(--border);
    cursor: zoom-in; transition: transform 0.15s;
}
.photo-thumb:hover { transform: scale(1.02); }
.photo-delete-btn {
    position: absolute; top: 6px; right: 6px;
    width: 26px; height: 26px; padding: 0;
    display: flex; align-items: center; justify-content: center;
    background: rgba(0, 0, 0, 0.62); color: #fff;
    border: none; border-radius: 50%;
    font-size: 11px; line-height: 1; cursor: pointer;
    opacity: 0; transition: opacity 0.15s;
}
.photo-thumb-wrap:hover .photo-delete-btn { opacity: 1; }
/* No hover on a tablet: the delete button stays visible there */
@media (hover: none) { .photo-delete-btn { opacity: 1; } }
.photos-camera-section { margin-bottom: 1rem; }
.photos-camera-section video { width: 100%; border-radius: var(--radius-lg); background: #000; display: block; }
.photos-camera-buttons { display: flex; gap: 0.5rem; margin-top: 0.5rem; }

/* Empty state shared by the panel tabs */
.rp-empty {
    display: flex; flex-direction: column; align-items: center; gap: 0.35rem;
    padding: 2rem 1rem; text-align: center;
    border: 1px dashed var(--border); border-radius: var(--radius-lg);
}
.rp-empty-icon  { font-size: 1.75rem; opacity: 0.8; }
.rp-empty-title { font-weight: 600; color: var(--text); }
.rp-empty-sub   { font-size: 0.85rem; color: var(--text-muted); max-width: 28rem; }

/* ── Repair panel: Message tab ───────────────────────────────────────────── */
.msg-contact {
    display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem;
    font-size: 0.88rem;
}
.msg-contact-label {
    font-size: 0.75rem; font-weight: 600; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: 0.04em; margin-right: 0.2rem;
}
.msg-chip {
    padding: 0.3rem 0.7rem; border-radius: var(--radius-pill);
    background: var(--bg-input); border: 1px solid var(--border); color: var(--text);
}
.msg-contact--none {
    padding: 0.7rem 0.85rem; border-radius: var(--radius-lg);
    background: var(--danger-soft); color: var(--danger); font-weight: 600;
}
.msg-body-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.msg-body-head .btn { padding-top: 0.15rem; padding-bottom: 0.15rem; }
.rp-textarea.msg-body { min-height: 10rem; line-height: 1.55; }
.msg-send {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr)); gap: 0.5rem;
}
.msg-send .btn { justify-content: center; }
.msg-send .msg-send-wa { background: #25d366; border-color: #25d366; color: #fff; }
.msg-send .msg-send-wa:hover { background: #1ebe5a; border-color: #1ebe5a; }
.msg-hint { font-size: 0.78rem; color: var(--text-muted); }

/* ── Repair panel: Warranty tab ──────────────────────────────────────────── */
.wc-list { display: flex; flex-direction: column; gap: 0.9rem; }
.warranty-claim-row {
    display: flex; flex-direction: column; gap: 0.6rem;
    padding: 0.9rem 1rem;
    background: var(--bg-input); border: 1px solid var(--border); border-radius: var(--radius-lg);
}
.wc-head   { display: flex; justify-content: space-between; align-items: flex-start; gap: 0.5rem; flex-wrap: wrap; }
.wc-title  { font-size: 1rem; font-weight: 600; color: var(--text); }
.wc-badges { display: flex; gap: 0.35rem; flex-wrap: wrap; }
.wc-meta   { font-size: 0.82rem; color: var(--text-muted); margin-top: -0.35rem; }
.wc-fields .form-group { margin-bottom: 0; min-width: 0; }
.warranty-claim-row .rp-select, .warranty-claim-row .rp-textarea { background: var(--bg-card); }
.wc-notes  { font-size: 0.88rem; color: var(--text-muted); white-space: pre-wrap; }
.wc-foot   { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; }
.wc-notify {
    padding: 0.35rem 0.8rem; border-radius: var(--radius-pill); cursor: pointer;
    font: inherit; font-size: 0.82rem; font-weight: 600;
    background: var(--warning-soft); color: var(--warning); border: 1px solid transparent;
}
.wc-notify--done { background: var(--success-soft); color: var(--success); }

/* ── Products — Inventory summary strip ──────────────────────────────────── */
/* The six cards in the tiles' look (.glance, above), in the condensed face.
   A tap filters, the card showing is filled in the accent, and a second tap
   shows everything again (products.js _cardIsOn).

   As with .glance, the cards go by the width of their own row, not the
   screen's (#2262): a screen-width rule cannot see the sidebar, which takes
   240px, and at 769 wide the names broke mid-word ("Produc / ts"). Narrow
   unless the row is wide: the count over its name. A name
   breaks between words only, and both rows of cards are the one height. */
.inv-summary-strip {
    display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); grid-auto-rows: 1fr; gap: 0.6rem;
    margin-bottom: 0.75rem;
    container: inv-strip / inline-size;
}
.inv-summary-strip:empty { display: none; }
.inv-stat {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
    min-height: 52px; min-width: 0; padding: 0.3rem 0.25rem;
    background: var(--bg-card); border: 1.5px solid var(--border); border-radius: var(--radius);
    font-family: 'Barlow Condensed', 'Barlow', sans-serif; text-align: center;
    transition: background 0.12s, border-color 0.12s;
    user-select: none; -webkit-tap-highlight-color: transparent;
}
.inv-stat:hover { border-color: var(--accent); }
.inv-stat-n { font-size: 1.4rem; font-weight: 700; color: var(--text-strong, var(--text)); line-height: 1; font-variant-numeric: tabular-nums; }
.inv-stat-label { font-size: 0.8rem; font-weight: 600; color: var(--text-strong, var(--text)); line-height: 1.1; min-width: 0; max-width: 100%; }
/* A wide row: the name on the left, the count on the right, both bigger. The
   count comes first in the markup, so row-reverse puts it on the right. 600px
   is where "Products" and a four-figure count fit side by side in a third of
   the row, in the fallback face too. */
@container inv-strip (min-width: 600px) {
    .inv-stat {
        flex-direction: row-reverse; justify-content: space-between; gap: 0.5rem;
        min-height: 56px; padding: 0.4rem 0.9rem; text-align: left;
    }
    .inv-stat-label { font-size: 1.15rem; font-weight: 700; }
    .inv-stat-n { font-size: 1.8rem; }
}
.inv-stat-warn.has-items { border-color: var(--stat-warn-edge); }
.inv-stat-warn.has-items .inv-stat-n { color: var(--ink-amber-2); }
.inv-stat-danger.has-items { border-color: var(--stat-danger-edge); }
.inv-stat-danger.has-items .inv-stat-n { color: var(--ink-red-2); }
.inv-stat.is-on { background: var(--accent-soft); border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
/* A laptop beside the sidebar, and anything smaller: three across, two rows. */
@media (max-width: 1366px) {
    .inv-summary-strip { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* Six in a row that is not yet wide: a little smaller, so "Products" still
   fits beside a four-figure count. */
@media (min-width: 1367px) {
    @container inv-strip (max-width: 1260px) {
        .inv-stat { padding-inline: 0.6rem; }
        .inv-stat-label { font-size: 1rem; }
        .inv-stat-n { font-size: 1.5rem; }
    }
}
@media (max-width: 768px) {
    .inv-summary-strip { gap: 0.4rem; }
}

/* The table has to fit the room beside the sidebar: with 300px each for a
   long title and a long vendor, Margin sat off the right edge at 1440 and
   the shop had to scroll sideways to reach it (#2290). Long names are cut
   with an ellipsis, as the Repairs description is. */
.products-table td.product-title-cell { max-width: 200px; }
.products-table td.text-muted-cell { max-width: 110px; }
.products-table th, .products-table td { padding-left: 0.5rem; padding-right: 0.5rem; }

/* ── Products — Stock badges ─────────────────────────────────────────────── */
.stock-badge {
    display: inline-block; padding: 0.2rem 0.55rem;
    border-radius: 4px; font-size: 0.78rem; font-weight: 500; white-space: nowrap;
}
.stock-out      { background: rgba(239,68,68,.15);  color: var(--ink-red-2); }
.stock-low      { background: rgba(245,158,11,.15); color: var(--ink-amber-2); }
.stock-ok       { background: rgba(34,197,94,.12);  color: var(--ink-green); }
.stock-untracked { color: var(--text-muted); }

/* Days Left, Age and Last sold (#1342). products.js has emitted these
   classes since Phase R, but nothing styled them, so 3 days of stock left
   read the same as 90. Same shape and tints as the stock badges. */
.days-badge {
    display: inline-block; padding: 0.2rem 0.55rem;
    border-radius: 4px; font-size: 0.78rem; font-weight: 500; white-space: nowrap;
}
.days-critical  { background: rgba(239,68,68,.15);  color: var(--ink-red-2); }
.days-warn      { background: rgba(245,158,11,.15); color: var(--ink-amber-2); }
.days-ok        { background: rgba(34,197,94,.12);  color: var(--ink-green); }
.days-nodata    { color: var(--text-muted); }
.days-age       { color: inherit; }

/* ── Products — Inline price edit ───────────────────────────────────────── */
.price-cell.inline-editable { cursor: pointer; }
.price-cell.inline-editable:hover { text-decoration: underline dotted var(--text-muted); }
.inline-price-input {
    width: 80px; padding: 0.2rem 0.4rem;
    border: 1px solid var(--accent); border-radius: 4px;
    font-size: 0.9rem; background: var(--bg); color: var(--text);
}

/* ── Customer History Modal ──────────────────────────────────────────────── */
/* A long item name with no spaces wrapped nowhere and ran out of the modal (#2146). */
#customer-history-modal { overflow-wrap: anywhere; }
.ch-bike-card, .ch-ticket-row { min-width: 0; }
.ch-bike-card {
    padding: .55rem .8rem;
    background: var(--bg-page, #f8f9fa);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    cursor: pointer;
    min-width: 120px;
    transition: border-color .15s, box-shadow .15s;
}
.ch-bike-card:hover { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(0,102,204,.1); }
.ch-bike-card--selected { border-color: var(--accent); background: rgba(0,102,204,.07); }

.ch-ticket-row {
    padding: .65rem .8rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    cursor: pointer;
    transition: background .12s;
}
.ch-ticket-row:hover { background: var(--hover, #f5f5f5); }

.ch-ticket-meta {
    display: flex; align-items: center; gap: .5rem; margin-bottom: 2px;
}

.ch-status-dot {
    width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;
}

/* ── B2W search dropdowns & utilities ────────────────────────────────────── */
/* The box a search input and its dropdown share, so the dropdown has
   something to hang off that is exactly the size of the input (#846).
   `.form-group` is a flex column, and an absolutely positioned child of a
   flex container starts at the container's content box rather than after the
   sibling it follows — which put the list over the label and the input.
   `top: 100%` on the group would have dropped it below the whole thing,
   moving further down whenever the create-customer box opened. */
.search-field { position: relative; display: flex; flex-direction: column; }
.search-results {
    position: absolute; top: 100%; left: 0; margin-top: 2px;
    z-index: 100; background: var(--bg-card, #fff);
    border: 1px solid var(--border, #ddd); border-radius: 6px;
    max-height: 200px; overflow-y: auto; width: 100%;
    box-shadow: 0 4px 12px rgba(0,0,0,0.12);
}
.search-result-item {
    padding: 0.5rem 0.75rem; cursor: pointer; font-size: 0.9rem;
}
.search-result-item:hover { background: var(--bg-hover, #f5f5f5); }
/* Always the last row of the customer dropdown, so a name nobody matches
   has somewhere to go instead of an empty list (#842). */
.search-result-action { color: var(--accent, #0066cc); font-weight: 500; }
/* "Nothing matched" and "the lookup failed" are different answers and must
   not both show as an empty dropdown. */
.search-result-msg {
    padding: 0.5rem 0.75rem; font-size: 0.85rem; color: var(--text-muted, #888);
}
.search-result-error { color: var(--ink-red-2); }
.form-group { position: relative; }

.clickable-row { cursor: pointer; }
.clickable-row:hover td { background: var(--bg-hover, #f5f5f5); }
/* The PO opened from Recent PO uploads (#2833): its row is marked while it is open; tap it again to close. */
#po-history-table tr.is-open td { background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--accent); }
#po-history-table tr.is-open:hover td { background: var(--accent-soft); }

.badge-info {
    background: var(--blue-50, #e0f0ff); color: var(--blue-700, #1a5fb4);
    padding: 2px 8px; border-radius: 4px; font-size: 0.8rem;
}

/* ── Margin badges (products page) ───────────────────────────────────────── */
.margin-badge {
    display: inline-block;
    padding: 2px 7px;
    border-radius: 4px;
    font-size: 0.78rem;
    font-weight: 600;
    white-space: nowrap;
}
.margin-good    { background: rgba(22, 163, 74,  0.12); color: var(--ink-green); }
.margin-warn    { background: rgba(245, 158, 11, 0.15); color: var(--ink-amber-2); }
.margin-bad     { background: rgba(239,  68, 68, 0.15); color: var(--ink-red-2); }
.margin-unknown { background: transparent; color: var(--text-muted); }


/* ── Missing-SKU badge ───────────────────────────────────────────────────── */
.badge-missing-sku {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 6px;
    border-radius: 4px;
    font-size: 0.70rem;
    font-weight: 600;
    background: rgba(245, 158, 11, 0.18);
    color: #b45309;
    vertical-align: middle;
}

/* ── Cara Trade availability badge ───────────────────────────────────────── */
.cara-badge {
    display: inline-block;
    padding: 2px 7px;
    border-radius: 4px;
    font-size: 0.78rem;
    font-weight: 600;
    white-space: nowrap;
}
.cara-ok   { background: rgba(22, 163, 74,  0.12); color: var(--ink-green); }
.cara-out  { background: rgba(239,  68, 68, 0.15); color: var(--ink-red-2); }
.cara-none { background: transparent; color: var(--text-muted); }

/* ── Archived badge ──────────────────────────────────────────────────────── */
.badge-archived { background: rgba(107, 100, 96, 0.10); color: #9ca3af; }

/* ── Reorder view ────────────────────────────────────────────────────────── */
.reorder-view-header {
    display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
    margin-bottom: 1rem; padding: 0.75rem 1rem;
    background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px;
}
.reorder-view-count { font-weight: 600; color: var(--text); flex: 1; min-width: 180px; }
.reorder-threshold-control { display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: var(--text-muted); }
.reorder-threshold-control input { padding: 0.3rem 0.5rem; font-size: 0.85rem; }
.reorder-filters {
    display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
    margin-bottom: 1rem;
}
.reorder-filters .input-search { font-size: 0.85rem; padding: 0.35rem 0.6rem; }
.reorder-group { margin-bottom: 1.5rem; }
.reorder-group-header {
    font-weight: 700; font-size: 0.95rem; padding: 0.4rem 0;
    border-bottom: 2px solid var(--border); margin-bottom: 0.4rem; color: var(--text);
}
.reorder-group .data-table { margin-bottom: 0; }
.btn-link {
    background: none; border: none; color: var(--accent); cursor: pointer;
    font-size: 0.80rem; padding: 0 4px; text-decoration: underline;
}

/* ── Tab buttons (used by Cara Stock and other tabbed pages) ─────────────── */
.tab-btn {
    background: none; border: none; border-bottom: 2px solid transparent;
    padding: 0.45rem 0.9rem; font-size: 0.9rem; font-weight: 500;
    color: var(--text-muted); cursor: pointer; transition: color 0.15s, border-color 0.15s;
    border-radius: 0;
}
.tab-btn:hover { color: var(--text); }
.tab-btn.tab-active { color: var(--accent); border-bottom-color: var(--accent); }

/* ── Bike Finder wizard ──────────────────────────────────────────────────── */
.bf-shell { max-width: 680px; margin: 0 auto; padding: 1.5rem 1rem 3rem; }
.bf-header { margin-bottom: 1.5rem; }
.bf-title { font-size: 1.5rem; font-weight: 700; margin: 0 0 0.75rem; color: var(--text); }
.bf-progress-bar-wrap { display: flex; align-items: center; gap: 0.75rem; }
.bf-progress-bar-bg { flex: 1; height: 6px; background: var(--border); border-radius: 3px; overflow: hidden; }
.bf-progress-bar-fill { height: 100%; background: var(--accent); border-radius: 3px; transition: width 0.3s ease; }
.bf-progress-text { font-size: 0.8rem; color: var(--text-muted); white-space: nowrap; }
.bf-question-container { min-height: 200px; }
.bf-question-prompt { font-size: 1.2rem; font-weight: 600; margin: 0 0 1.25rem; color: var(--text); line-height: 1.4; }
.bf-options-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; }
@media (max-width: 480px) { .bf-options-grid { grid-template-columns: 1fr; } }
.bf-option-card { display: flex; align-items: center; gap: 0.6rem; padding: 0.9rem 1rem; border: 2px solid var(--border); border-radius: 10px; background: var(--bg-card); cursor: pointer; transition: border-color 0.15s, background 0.15s; font-size: 0.95rem; color: var(--text); }
.bf-option-card:hover { border-color: var(--accent); background: var(--bg); }
.bf-option-card.selected { border-color: var(--accent); background: var(--bg); }
.bf-radio-hidden { display: none; }
.bf-option-label { flex: 1; }
.bf-number-wrap { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem; }
.bf-unit-prefix, .bf-unit-suffix { font-size: 1.1rem; font-weight: 600; color: var(--text-muted); }
.bf-number-input { flex: 1; max-width: 200px; padding: 0.75rem 1rem; font-size: 1.2rem; border: 2px solid var(--border); border-radius: 8px; background: var(--bg-card); color: var(--text); text-align: center; }
.bf-number-input:focus { outline: none; border-color: var(--accent); }
.bf-skip-note { font-size: 0.8rem; color: var(--text-muted); margin-top: 0.3rem; }
/* Sliders: one drag, big thumbs for a finger at the kiosk. */
.bf-slider { max-width: 560px; }
.bf-slider-value { display: block; font-size: 1.6rem; font-weight: 700; color: var(--text); margin-bottom: 0.9rem; }
.bf-slider-unset .bf-slider-value { color: var(--text-muted); font-size: 1.1rem; font-weight: 500; }
.bf-slider-unset .bf-slider-input { opacity: 0.5; }
.bf-slider-ends { display: flex; justify-content: space-between; font-size: 0.8rem; color: var(--text-muted); margin-top: 0.4rem; }
.bf-slider-input { width: 100%; height: 36px; margin: 0; background: none; cursor: pointer; -webkit-appearance: none; appearance: none; }
.bf-slider-input::-webkit-slider-runnable-track { height: 6px; border-radius: 3px; background: var(--border); }
.bf-slider-input::-moz-range-track { height: 6px; border-radius: 3px; background: var(--border); }
.bf-slider-input::-webkit-slider-thumb { -webkit-appearance: none; width: 30px; height: 30px; margin-top: -12px; border-radius: 50%; background: var(--accent); border: 3px solid #fff; box-shadow: 0 1px 4px rgba(0,0,0,0.4); cursor: grab; }
.bf-slider-input::-moz-range-thumb { width: 26px; height: 26px; border-radius: 50%; background: var(--accent); border: 3px solid #fff; box-shadow: 0 1px 4px rgba(0,0,0,0.4); cursor: grab; }
.bf-slider-clear { margin-top: 0.75rem; background: none; border: none; color: var(--text-muted); text-decoration: underline; font-size: 0.9rem; cursor: pointer; padding: 0.3rem 0; }
.bf-range-track { position: relative; height: 36px; }
.bf-range-track::before { content: ''; position: absolute; left: 0; right: 0; top: 50%; height: 6px; margin-top: -3px; border-radius: 3px; background: var(--border); }
.bf-range-fill { position: absolute; top: 50%; height: 6px; margin-top: -3px; border-radius: 3px; background: var(--accent); }
/* Two inputs on one track: only the thumbs take the pointer. */
.bf-range-input { position: absolute; left: 0; top: 0; width: 100%; height: 36px; margin: 0; background: none; pointer-events: none; -webkit-appearance: none; appearance: none; }
.bf-range-input::-webkit-slider-runnable-track { background: none; }
.bf-range-input::-moz-range-track { background: none; }
.bf-range-input::-webkit-slider-thumb { -webkit-appearance: none; pointer-events: auto; width: 30px; height: 30px; border-radius: 50%; background: var(--accent); border: 3px solid #fff; box-shadow: 0 1px 4px rgba(0,0,0,0.4); cursor: grab; }
.bf-range-input::-moz-range-thumb { pointer-events: auto; width: 26px; height: 26px; border-radius: 50%; background: var(--accent); border: 3px solid #fff; box-shadow: 0 1px 4px rgba(0,0,0,0.4); cursor: grab; }
/* Bike Finder's UI is hands-off: it keeps the button look it had before the
   app-wide .btn-primary / .btn-secondary change in style.css. */
#bike-finder-page .btn-primary { background: var(--accent); color: #fff; border: none; font-weight: normal; }
#bike-finder-page .btn.btn-primary { font-weight: 500; }
#bike-finder-page .bf-lead-prompt .btn-primary { font-weight: 600; }
#bike-finder-page .btn-primary:hover:not(:disabled) { background: var(--accent-hover); }
[data-theme="dark"] #bike-finder-page .btn-secondary { border-color: var(--border); }
.bf-nav-btns { display: flex; justify-content: space-between; align-items: center; margin-top: 2rem; gap: 1rem; }
.bf-nav-btn { min-width: 130px; min-height: 48px; font-size: 1rem; border-radius: 8px; }
.bf-required-hint { color: #e53e3e; font-size: 0.88rem; margin-top: 0.5rem; }
@keyframes bf-shake { 0%, 100% { transform: translateX(0); } 20%, 60% { transform: translateX(-6px); } 40%, 80% { transform: translateX(6px); } }
.bf-shake { animation: bf-shake 0.35s ease; }
.bf-results-header { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 0.75rem; margin-bottom: 1.5rem; }
.bf-results-header h2 { margin: 0; font-size: 1.3rem; color: var(--text); }
.bf-restart-btn { font-size: 0.85rem; padding: 0.4rem 0.9rem; }
.bf-bucket { margin-bottom: 2rem; }
.bf-bucket-title { font-size: 1rem; font-weight: 700; margin: 0 0 0.75rem; color: var(--text); }
.bf-cards-scroll { display: flex; gap: 1rem; overflow-x: auto; padding-bottom: 0.5rem; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; }
/* Thin, in the theme's colours (style.css): -webkit-scrollbar rules no longer apply. */
.bf-cards-scroll { scrollbar-width: thin; }
.bf-card { flex: 0 0 240px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; scroll-snap-align: start; display: flex; flex-direction: column; }
.bf-card-img { width: 100%; height: 130px; object-fit: cover; }
.bf-card-img-placeholder { display: flex; align-items: center; justify-content: center; font-size: 2.5rem; background: var(--bg); }
.bf-card-body { padding: 0.75rem; display: flex; flex-direction: column; flex: 1; gap: 0.4rem; }
.bf-card-title { font-size: 0.88rem; font-weight: 600; color: var(--text); line-height: 1.3; }
.bf-card-price { font-size: 1rem; font-weight: 700; color: var(--accent); }
.bf-card-reasons { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: auto; padding-top: 0.4rem; }
.bf-match-reason { font-size: 0.72rem; padding: 0.15rem 0.45rem; border-radius: 4px; background: var(--bg); color: var(--accent); font-weight: 500; }
.bf-card-btn { margin-top: 0.5rem; width: 100%; padding: 0.45rem; font-size: 0.82rem; }
/* More info: a panel on the kiosk itself, never a staff screen (#437). */
.bf-detail { display: flex; flex-direction: column; gap: 0.6rem; max-width: 520px; }
.bf-detail-img { width: 100%; max-height: 320px; object-fit: contain; border-radius: 8px; background: var(--bg); }
.bf-detail-title { margin: 0; font-size: 1.25rem; color: var(--text); line-height: 1.3; }
.bf-detail .bf-card-reasons { margin-top: 0; padding-top: 0; }
.bf-detail-next { margin: 0.25rem 0 0; font-size: 1rem; color: var(--text); }
.bf-lead-prompt { text-align: center; margin-top: 1.5rem; padding-top: 1.5rem; border-top: 1px solid var(--border); }
/* The button has no .btn class, so it got the fill with no padding and read
   as highlighted text. Style it here: it's the main call to action. */
.bf-lead-prompt .btn-primary {
    display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
    padding: 0.9rem 1.75rem; border: none; border-radius: var(--radius);
    font-family: inherit;
    font-size: 1.15rem; font-weight: 600; cursor: pointer;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25); max-width: 100%;
}
.bf-lead-subtitle { color: var(--text-muted); font-size: 0.9rem; margin-bottom: 1rem; }
.bf-form-row { margin-bottom: 1rem; }
.bf-form-label { display: block; font-size: 0.85rem; font-weight: 600; color: var(--text-muted); margin-bottom: 0.3rem; }
.bf-form-input { width: 100%; padding: 0.6rem 0.75rem; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-card); color: var(--text); font-size: 0.95rem; box-sizing: border-box; }
.bf-lead-btns { display: flex; gap: 1rem; margin-top: 1.5rem; }
.bf-thanks-msg { font-size: 1.1rem; color: var(--accent); font-weight: 600; margin-bottom: 1rem; }
.bf-loading { color: var(--text-muted); padding: 2rem 0; text-align: center; font-size: 1rem; }
.bf-error-inline { color: #e53e3e; }
.bf-no-results { background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 1.5rem; text-align: center; color: var(--text-muted); }

/* Shown when a source could not be consulted: an incomplete answer has to
   look different from a complete one (#400). */
.bf-source-warning { background: var(--warning-bg, #fff4e5); border: 1px solid var(--warning, #f0ad4e); border-radius: 8px; padding: 0.85rem 1rem; margin-bottom: 1rem; font-size: 0.9rem; color: var(--text); line-height: 1.4; }
.bf-source-warning strong { display: block; margin-bottom: 0.2rem; }

/* Savings banner at top of results */
.bf-savings-banner { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; margin-bottom: 1.5rem; }
@media (max-width: 640px) { .bf-savings-banner { grid-template-columns: 1fr; } }
.bf-banner-item { display: flex; gap: 0.75rem; background: var(--bg-card); border: 1px solid var(--border); border-radius: 10px; padding: 0.85rem 1rem; }
.bf-banner-icon { font-size: 1.8rem; flex-shrink: 0; }
.bf-banner-item strong { display: block; font-size: 0.95rem; color: var(--text); margin-bottom: 0.2rem; }
.bf-banner-item p { margin: 0; font-size: 0.8rem; color: var(--text-muted); line-height: 1.35; }

/* Savings block on each card */
.bf-savings-block { display: flex; flex-direction: column; gap: 0.3rem; margin: 0.25rem 0; }
.bf-savings-row { display: flex; align-items: center; gap: 0.4rem; padding: 0.35rem 0.5rem; border-radius: 5px; background: var(--bg); border-left: 3px solid transparent; }
.bf-savings-row.bf-b2w { border-left-color: var(--ink-green); }
.bf-savings-row.bf-plan { border-left-color: var(--ink-blue-2); }
.bf-savings-icon { font-size: 1.05rem; }
.bf-savings-text { display: flex; flex-direction: column; line-height: 1.15; flex: 1; min-width: 0; }
.bf-savings-text strong { font-size: 0.83rem; color: var(--text); font-weight: 700; }
.bf-savings-sub { font-size: 0.68rem; color: var(--text-muted); }

/* Featured (preferred brand) card styling */
.bf-card-featured { border-color: var(--stat-warn-edge); box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.25); position: relative; }
.bf-featured-ribbon {
    position: absolute; top: 8px; right: 0;
    background: linear-gradient(90deg, #f59e0b, #d97706);
    color: #fff; font-size: 0.68rem; font-weight: 800; letter-spacing: 0.04em;
    padding: 0.25rem 0.55rem 0.25rem 0.7rem;
    border-radius: 4px 0 0 4px;
    z-index: 2;
    box-shadow: 0 2px 4px rgba(0,0,0,0.15);
}

/* "Why Bottecchia?" promotional callout */
.bf-bottecchia-pitch {
    display: flex; gap: 1rem; align-items: center;
    background: linear-gradient(135deg, #fef3c7, #fde68a);
    border: 1px solid #f59e0b;
    border-radius: 10px;
    padding: 1rem 1.15rem;
    margin-bottom: 1.5rem;
    color: #1f2937;
}
.bf-bottecchia-icon { font-size: 2rem; flex-shrink: 0; }
.bf-bottecchia-text strong { display: block; font-size: 1rem; margin-bottom: 0.25rem; color: #92400e; }
.bf-bottecchia-text p { margin: 0; font-size: 0.85rem; line-height: 1.45; }
.bf-bottecchia-text p strong { display: inline; font-size: inherit; color: #92400e; margin: 0; }

/* ── Session timeout warning banner ─────────────────────────────────────── */
#session-warning-banner {
    position: fixed;
    bottom: 1rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 9999;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem 1.25rem;
    background: #7c3aed;
    color: #fff;
    border-radius: 0.5rem;
    box-shadow: 0 4px 16px rgba(0,0,0,0.25);
    font-size: 0.9rem;
    white-space: nowrap;
    animation: toast-in 0.2s ease;
}
#session-warning-banner strong { font-weight: 700; }
#session-warning-banner .btn-primary {
    background: #fff;
    color: var(--ink-violet);
    border: none;
}
#session-warning-banner .btn-primary:hover {
    background: #ede9fe;
}
#session-warning-banner .btn-secondary {
    background: transparent;
    color: #ede9fe;
    border: 1px solid #ede9fe;
}

/* ── Failing background job banner (#151) ───────────────────────────────── */
#job-health-banner {
    margin-bottom: 1rem;
    padding: 0.6rem 0.9rem;
    border: 1px solid var(--danger);
    border-radius: 0.5rem;
    background: rgba(198, 40, 40, 0.08);
    color: var(--text);
    font-size: 0.875rem;
    display: grid;
    gap: 0.35rem;
    overflow-wrap: anywhere;
    position: relative;
    padding-right: 2.4rem;
}
#job-health-banner .job-health-close {
    position: absolute;
    top: 0.3rem;
    right: 0.4rem;
    width: 1.75rem;
    height: 1.75rem;
    border: 0;
    border-radius: 0.35rem;
    background: transparent;
    color: var(--text-muted);
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
}
#job-health-banner .job-health-close:hover { color: var(--text); background: rgba(198, 40, 40, 0.15); }
#job-health-banner strong { color: var(--danger); }
#job-health-banner .job-health-error { color: var(--text-muted); font-size: 0.8rem; }

/* ── Steps strip while a slow fetch runs (components/bike-race.js) ───────── */
/* Fixed to the top of the window, in view wherever the page is scrolled
   (#2919). It takes no clicks: only the × of a failed one does. At 40px it
   is taller than the 2rem of padding above every page (#2923: 26px with 12px
   text read as too small), so the page moves down 16px while it shows and
   the strip still covers nothing. */
.bike-race {
    position: fixed; top: 4px; left: var(--sidebar-w); right: 0; z-index: 150;
    box-sizing: border-box; width: fit-content; max-width: min(820px, calc(100% - var(--sidebar-w) - 1rem));
    height: 40px; margin: 0 auto; padding: 0 1rem;
    display: flex; align-items: center; gap: 0.75rem;
    background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-pill);
    box-shadow: var(--shadow-md); font-size: 0.95rem; line-height: 1;
    pointer-events: none;
}
.bike-race.is-failed { border-color: var(--danger); }
.bike-race > .bike-wheel { flex: none; color: var(--accent); }
.step-line { display: flex; flex: none; align-items: center; margin: 0; padding: 0; list-style: none; }
.step-line-step { display: flex; align-items: center; gap: 0.4rem; }
/* The line into each dot from the one before it. */
.step-line-step + .step-line-step::before {
    content: ''; width: 20px; height: 2px; margin: 0 0.4rem;
    background: repeating-linear-gradient(90deg, var(--border) 0 4px, transparent 4px 7px);
}
.step-line-step.is-done::before, .step-line-step.is-now::before { background: var(--accent); }
.step-line-dot {
    box-sizing: border-box; flex: none; width: 14px; height: 14px; border-radius: 50%;
    border: 2px solid var(--border); background: var(--bg-card);
    transition: background 0.3s, border-color 0.3s;
}
.step-line-step.is-done .step-line-dot { background: var(--accent); border-color: var(--accent); }
.step-line-step.is-now .step-line-dot {
    border-color: var(--accent); animation: step-line-pulse 1.2s ease-out infinite;
}
.step-line-step.is-failed .step-line-dot {
    background: var(--danger); border-color: var(--danger); animation: none;
}
.step-line-label { color: var(--text-muted); white-space: nowrap; }
.step-line-step.is-done .step-line-label,
.step-line-step.is-now .step-line-label { color: var(--text); font-weight: 600; }
.step-line-step.is-failed .step-line-label { color: var(--danger); }
.bike-race-detail {
    flex: 0 1 auto; min-width: 0; color: var(--text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.5;
}
.bike-race.is-failed .bike-race-detail { color: var(--danger); font-weight: 600; }
.bike-race-close {
    flex: none; pointer-events: auto; cursor: pointer;
    width: 26px; height: 26px; margin-right: -0.5rem; padding: 0;
    border: none; border-radius: 50%; background: none;
    color: var(--text-muted); font: inherit; font-size: 1.25rem; line-height: 1;
}
.bike-race-close:hover { color: var(--text); background: var(--accent-soft); }

body.has-steps-strip #content { padding-top: calc(2rem + 16px); }

/* A phone has a bar across the top and 1rem under it: the strip sits under
   the bar on two lines, and the page moves down to clear it while it shows. */
@media (max-width: 768px) {
    .bike-race {
        top: calc(3.25rem + 4px); left: 0.5rem; right: 0.5rem; width: auto; max-width: none;
        height: 62px; font-size: 0.875rem; flex-wrap: wrap; align-content: center; justify-content: center;
        gap: 0.35rem 0.5rem; border-radius: var(--radius);
    }
    .bike-race-detail { flex-basis: 100%; text-align: center; }
    .bike-race.is-failed .bike-race-detail { flex-basis: calc(100% - 34px); }
    body.has-steps-strip #content { padding-top: calc(4.25rem + 60px); }
}

/* The inline wheel: BikeRace.wheel() / busy(), for short quiet waits. */
.bike-wheel { display: inline-block; width: 1em; height: 1em; vertical-align: -0.15em; }
.bike-wheel svg {
    display: block; width: 100%; height: 100%;
    fill: none; stroke: currentColor; stroke-width: 3; stroke-linecap: round;
}
.bike-wheel .bike-race-wheel { animation: bike-race-spin 0.7s linear infinite; }
.po-img-busy { display: inline-flex; margin-left: 0.3rem; color: var(--accent); }
.po-img-thumb.is-busy, .btn-img-add.is-busy { opacity: 0.4; }

@keyframes bike-race-spin { to { transform: rotate(360deg); } }
@keyframes step-line-pulse {
    0%   { box-shadow: 0 0 0 0 var(--accent-soft); }
    100% { box-shadow: 0 0 0 8px transparent; }
}

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

/* ── "Working…" pill: any slow request someone started (busy-wheel.js) ───── */
#busy-wheel {
    position: fixed; left: 50%; bottom: 1.5rem; z-index: 9998;
    transform: translate(-50%, 1rem); opacity: 0; pointer-events: none;
    display: inline-flex; align-items: center; gap: 0.45rem;
    padding: 0.4rem 0.9rem; border-radius: var(--radius-pill);
    background: var(--surface-raised); color: var(--text);
    border: 1px solid var(--border); box-shadow: 0 4px 14px rgba(0, 0, 0, 0.15);
    font-size: 0.85rem; font-weight: 600;
    transition: opacity 0.15s, transform 0.15s;
}
#busy-wheel.is-on { opacity: 1; transform: translate(-50%, 0); }
#busy-wheel .bike-wheel { color: var(--accent); }

/* ── Deliveries: "or" divider and the JHI / Bottecchia / Manual PO row ───────
   Their flex layout lives here, not inline: discardPreview() shows them again
   with style.display = '', which used to strip an inline display:flex and
   drop both to block from the second invoice on (#1087). */
#po-or-divider {
    display: flex; align-items: center; gap: 0.75rem; margin: 0.75rem 0;
    color: var(--text-muted); font-size: 0.85rem;
}
#po-or-divider hr { flex: 1; border: none; border-top: 1px solid var(--border); margin: 0; }
#po-jhi-row {
    display: flex; flex-direction: column; gap: var(--sp-3);
}

/* ── Purchase Orders: start ──────────────────────────────────────────────
   Kept short so Recent PO uploads shows without scrolling on an iPad or a
   phone. Top: a slim file-drop bar with + New Manual PO beside it. Under it
   one row of suppliers, each a name, Show invoices and its links in a line;
   the JHI Invoice ID box opens on a line of its own under the row. The
   Bottecchia and JHI invoice lists sit full width under the row. On a phone
   the drop bar and the button stack as two slim buttons. */
/* The sidebar names the page: the title is for screen readers, and its "?"
   (help-link.js) leads the start bar. It had a row of its own. */
.po-title { display: flex; align-items: center; margin: 0; font-size: 1.15rem; }
.po-title-text {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.po-title .help-q { margin-left: 0; }
/* + New Manual PO is the page's one filled button, in the app's primary
   blue; Defaults beside it stays a plain outline with a small cog, and
   the dashed upload tile is the third way in. */
.po-source-btn#po-manual-btn {
    background: var(--ink-blue); color: var(--on-solid); border-color: var(--accent);
}
.po-source-btn#po-manual-btn:hover { filter: brightness(0.9); border-color: var(--accent); }
.po-source-btn.po-source-btn--quiet {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
    background: transparent; color: var(--text-muted); font-weight: 500;
}
.po-source-btn.po-source-btn--quiet::before {
    content: ""; width: 1.05em; height: 1.05em; background: currentColor;
    -webkit-mask: var(--po-cog) center / contain no-repeat; mask: var(--po-cog) center / contain no-repeat;
}
.po-source-btn.po-source-btn--quiet:hover { color: var(--ink-blue); }
.po-start { --po-cog: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3Cpath d='M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z'/%3E%3C/svg%3E"); }
/* No margin under it: #po-history brings its own gap and line. */
.po-start { display: flex; flex-direction: column; gap: var(--sp-3); }
.po-start-top { display: flex; flex-direction: row; align-items: center; gap: var(--sp-3); }
.po-source {
    background: var(--bg-card); border: 1px solid var(--border);
    border-radius: var(--radius-lg); padding: var(--sp-2) var(--sp-4);
}
.po-source--upload {
    /* A tile the size of the buttons beside it: the icon and the title. It
       was a bar that filled the row, so the three could not stack or wrap as
       equals. The dashed edge still says "drop it here". */
    flex: 0 0 auto; min-width: 0;
    display: flex; flex-direction: row; gap: var(--sp-3);
    align-items: center; justify-content: center; text-align: left;
    margin: 0; padding: var(--sp-2) var(--sp-4); min-height: 44px;
    border: 2px dashed var(--border); background: transparent;
}
.po-source--upload:hover, .po-source--upload.drag-over {
    border-color: var(--accent); background: var(--accent-soft);
}
.po-source--upload:hover .po-source-icon, .po-source--upload.drag-over .po-source-icon { color: var(--accent); }
.po-source-icon { flex: 0 0 auto; color: var(--text-muted); transition: color 0.2s; }
.po-source-title { flex: 0 0 auto; font-size: 0.95rem; font-weight: 600; color: var(--text); margin: 0; white-space: nowrap; }
/* The hint is a sentence, and a tile this size has no room for one. */
.po-source--upload .upload-hint { display: none; }
.po-source-label {
    display: block; margin: 0;
    font-size: 0.75rem; font-weight: 600; letter-spacing: 0.04em;
    text-transform: uppercase; color: var(--text-muted);
}
.po-source-inline { display: flex; gap: var(--sp-2); }
.po-source-inline .form-input { flex: 1 1 8rem; min-width: 0; }
.po-source-inline .btn { flex: 0 0 auto; }
#po-jhi-status { display: block; margin-top: var(--sp-2); font-size: 0.85rem; color: var(--text-muted); }
.po-source-btn {
    flex: 0 0 auto; min-height: 44px; cursor: pointer; font: inherit; font-weight: 600;
    color: var(--text); text-align: center; white-space: nowrap;
    transition: border-color 0.15s, background 0.15s;
}
.po-source-btn:hover { border-color: var(--accent); background: var(--accent-soft); }
.po-source-btn:focus-visible { outline: 2px solid var(--ink-blue); outline-offset: 2px; }

/* Shop transfers (#2856): a box in the start row like the tile and
   Defaults (.po-source-btn gives it the shape and the shared floor), the
   units still to move on it. It is stock to move between the shops, not a
   state a PO can be in, so it is apart from the PO tiles. Filled, like an
   open supplier pill, while its list is open; quiet at 0; gone ([hidden])
   until there is something to move or a Moved log. The attribute must win
   over the display, or hidden would show it. */
.po-source-btn.po-transfers-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
    transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.po-source-btn.po-transfers-btn[hidden] { display: none; }
.po-transfers-count {
    font-family: 'Barlow Condensed', 'Barlow', sans-serif; font-size: 1.2rem; font-weight: 700;
    line-height: 1; font-variant-numeric: tabular-nums; color: var(--success);
}
.po-transfers-btn.is-zero .po-transfers-count { color: var(--text-muted); font-weight: 600; }
.po-source-btn.po-transfers-btn.is-on { background: var(--ink-blue); border-color: var(--ink-blue); color: #fff; }
.po-transfers-btn.is-on .po-transfers-count { color: #fff; }
[data-theme="dark"] .po-source-btn.po-transfers-btn.is-on { background: var(--accent); border-color: var(--accent); }
/* + New Manual PO, the one filled button, at the right-hand end of the row
   (Neil, 9 Oct). On a phone the row is a column and it is last, full width. */
@media (min-width: 601px) {
    #po-manual-btn { margin-left: auto; }
}
@media (max-width: 600px) {
    #po-manual-btn { margin-left: 0; }
    /* The row is a column there: the box takes the width, its count at the end. */
    .po-source-btn.po-transfers-btn { justify-content: space-between; }
}

/* A supplier's invoice list is capped to a box that scrolls inside, so Still
   to come and Recent PO uploads stay in reach under a long list (Neil, 9
   Oct). "Show the whole list" (.po-supplier-more, shown by _foldPanels()
   only when the list runs past the cap) lifts it; "Show less" puts it back. */
#po-jhi-panel:not(.is-open), #po-bottecchia-panel:not(.is-open),
#po-cara-panel:not(.is-open), #po-madison-panel:not(.is-open) {
    max-height: 22rem; overflow-y: auto; scrollbar-width: thin;
}
.po-supplier-more {
    flex: 1 0 100%; padding: var(--sp-1) 0; border: 0; background: none; text-align: left;
    font: inherit; font-size: 0.85rem; color: var(--text-muted); text-decoration: underline; cursor: pointer;
}
.po-supplier-more[hidden] { display: none; }
.po-supplier-more:hover { color: var(--ink-blue); }
.po-supplier-more:focus-visible { outline: 2px solid var(--ink-blue); outline-offset: 2px; }

/* The start row beside the sidebar (#2297). An upright iPad with the sidebar
   showing leaves it 465px. The row wraps: one row where the "?", the tile
   and both buttons fit, and otherwise the "?" and the tile, then both
   buttons under them. Nothing stretches, so each is as wide as its words
   in whatever face is drawing them. A phone is the column above. */
.po-start-buttons { display: flex; gap: var(--sp-3); }
@media (max-width: 600px) {
    .po-start-buttons { flex-direction: column; gap: var(--sp-2); }
}
@media (min-width: 601px) {
    .po-start-top { flex-wrap: wrap; }
    .po-title { flex: 0 0 auto; }
    /* The tile and the two buttons are one width, so they sit and stack as
       equals: a floor of 15rem, which is the tile's icon and title with room
       to spare in the app's own face. (A wider face can push the tile past
       it; the buttons stay on the floor.) Where both buttons do not fit one
       line they stack too, still that width. */
    .po-source--upload,
    .po-start-buttons .po-source-btn,
    #po-manual-btn { min-width: 15rem; box-sizing: border-box; }
    .po-start-buttons { flex: 0 1 auto; flex-wrap: wrap; min-width: 0; }
}

/* Suppliers: one compact row, the label first, then a pill for each
   supplier and any link after them. A plain row, not a card: the
   box around it made it as heavy as the upload bar over it, and the line
   over Recent PO uploads already closes it off. */
.po-suppliers {
    display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--sp-2) var(--sp-4);
    padding: 0 var(--sp-1);
}
/* Level with the pills, whether or not JHI's box is open under them. */
.po-suppliers > .po-source-label { line-height: 36px; }
.po-suppliers-grid {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3);
    flex: 1 1 auto; min-width: 0;
}
/* Each supplier is one pill, named for the supplier: pressing it opens that
   supplier's invoices under the row, so no pill needs "Show invoices" on it.
   .po-supplier only groups a pill with its extras (JHI's number box), and
   display: contents lets them all sit in the one row; the JHI link goes to
   the end of it and the box to a line of its own. */
.po-supplier { display: contents; }
.po-supplier-btn {
    display: inline-flex; align-items: center; gap: var(--sp-2);
    min-height: 36px; padding: 0.3rem 0.9rem; cursor: pointer;
    font: inherit; font-size: 0.9rem; font-weight: 600; color: var(--text);
    background: var(--accent-soft); border: 1px solid var(--border); border-radius: 999px;
    transition: border-color 0.15s, color 0.15s;
}
.po-supplier-btn:hover { border-color: var(--accent); }
.po-supplier-btn:focus-visible { outline: 2px solid var(--ink-blue); outline-offset: 2px; }
/* Tabs, not drop-downs: the open supplier is filled, and its list sits under the
   row with the supplier's name on it. Only one list is open at a time.
   --tab-fill is the dark ink in the light theme so the white words keep their contrast. */
#po-suppliers { --tab-fill: var(--ink-blue); }
[data-theme="dark"] #po-suppliers { --tab-fill: var(--accent); }
.po-supplier-btn[aria-expanded="true"] { background: var(--tab-fill); border-color: var(--tab-fill); color: #fff; }
.po-supplier-btn:disabled { opacity: 0.6; cursor: progress; }
/* Beside the pills, "Enter a JHI invoice number" (#2014) opens the Invoice ID
   box for JHI on a line of its own under them. Shut with [hidden], never
   inline display (#1087). */
.po-supplier-link {
    order: 1; margin-left: auto;
    padding: var(--sp-1) 0; border: 0; background: none;
    font: inherit; font-size: 0.85rem; color: var(--text-muted);
    text-decoration: underline; cursor: pointer;
}
.po-supplier-link:hover, .po-supplier-link[aria-expanded="true"] { color: var(--ink-blue); }
.po-supplier-link:focus-visible { outline: 2px solid var(--ink-blue); outline-offset: 2px; }
#po-jhi-number-box { order: 2; flex: 1 0 100%; }
#po-jhi-number-box[hidden] { display: none; }
/* The invoice lists run full width under the row. */
#po-jhi-panel, #po-bottecchia-panel, #po-cara-panel, #po-madison-panel { flex: 1 0 100%; min-width: 0; }
/* Touch size up to an iPad held upright (820 wide), not only a phone (#2114):
   the global 44px rule stops at 768. */
@media (max-width: 1024px) {
    .po-supplier-btn, .po-supplier-link { min-height: 44px; box-sizing: border-box; }
    #po-jhi-number-box .form-input, #po-jhi-number-box .btn,
    #po-jhi-panel .btn-sm, #po-bottecchia-panel .btn-sm, #po-cara-panel .btn-sm, #po-madison-panel .btn-sm { min-height: 44px; }
    #po-jhi-number-box .form-input, #po-jhi-number-box .btn,
    #po-jhi-panel .btn-sm, #po-bottecchia-panel .btn-sm, #po-cara-panel .btn-sm, #po-madison-panel .btn-sm { box-sizing: border-box; }
}
@media (max-width: 600px) {
    .po-start-top { flex-direction: column; align-items: stretch; gap: var(--sp-2); }
    /* One supplier to a line. */
    .po-suppliers > .po-source-label { display: none; }
    .po-suppliers-grid { flex-direction: column; align-items: stretch; gap: var(--sp-2); }
    .po-supplier-btn { justify-content: space-between; min-height: 44px; }
    .po-supplier-link { margin-left: 0; }
}
/* The open list is a card headed with the supplier's name (data-title). */
#po-jhi-panel, #po-bottecchia-panel, #po-cara-panel, #po-madison-panel {
    margin-top: var(--sp-2); padding: 0 var(--sp-3) var(--sp-3);
    background: var(--bg-card); border: 1px solid var(--tab-fill); border-radius: var(--radius-lg);
}
#po-jhi-panel::before, #po-bottecchia-panel::before, #po-cara-panel::before, #po-madison-panel::before {
    content: attr(data-title); display: block; margin: 0 calc(-1 * var(--sp-3)) var(--sp-2);
    padding: var(--sp-2) var(--sp-3); font-weight: 700; color: #fff; background: var(--tab-fill);
    border-radius: calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px) 0 0;
}
#po-bottecchia-panel .data-table { width: 100%; }
/* Under 960px the five columns (900px at least) outrun the panel and the Fetch
   buttons sat off the right edge (#2313). Each invoice is a block instead: number
   over date, status and amount, the button to the right. */
@media (max-width: 960px) {
    #po-bottecchia-panel .data-table { min-width: 0; }
    #po-bottecchia-panel .data-table, #po-bottecchia-panel .data-table tbody { display: block; }
    #po-bottecchia-panel .data-table thead { display: none; }
    #po-bottecchia-panel .data-table tr {
        display: grid; grid-template-columns: minmax(0, 1fr) auto auto;
        grid-template-areas: "num amt act" "date status act";
        align-items: center; column-gap: var(--sp-2);
        padding: var(--sp-2) 0; border-bottom: 1px solid var(--border);
    }
    #po-bottecchia-panel .data-table td { padding: 0; border: 0; max-width: none; overflow: visible; }
    #po-bottecchia-panel .po-bott-num { grid-area: num; font-weight: 600; white-space: normal; }
    #po-bottecchia-panel .po-bott-date { grid-area: date; color: var(--text-muted); font-size: 0.85rem; }
    #po-bottecchia-panel .po-bott-amt { grid-area: amt; }
    #po-bottecchia-panel .po-bott-status { grid-area: status; justify-self: end; }
    #po-bottecchia-panel .po-bott-act { grid-area: act; }
}
/* JHI's invoice list (#1980), and Cara's drawn the same way (#2256). The
   status beside the number, then the last fetch's line counts.
   .data-table is 900px at least, which is right for the big tables and far
   too wide here. */
#po-jhi-panel, #po-cara-panel, #po-madison-panel { margin-top: var(--sp-2); }
#po-jhi-panel .data-table, #po-cara-panel .data-table, #po-madison-panel .data-table { width: 100%; min-width: 0; }
#po-jhi-panel .po-jhi-num, #po-cara-panel .po-jhi-num, #po-madison-panel .po-jhi-num { font-weight: 600; }
#po-jhi-panel .po-jhi-num > span:first-child, #po-cara-panel .po-jhi-num > span:first-child, #po-madison-panel .po-jhi-num > span:first-child { margin-right: var(--sp-2); }
#po-jhi-panel .po-jhi-num .badge, #po-cara-panel .po-jhi-num .badge, #po-madison-panel .po-jhi-num .badge { font-weight: 400; }
#po-jhi-panel .po-jhi-amt, #po-jhi-panel .po-jhi-act, #po-cara-panel .po-jhi-amt, #po-madison-panel .po-jhi-amt, #po-cara-panel .po-jhi-act, #po-madison-panel .po-jhi-act { text-align: right; }
/* Items, Synced, Units, Unmatched, Fetched: a column each here, one line
   under the number on a phone (.po-jhi-lines). */
#po-jhi-panel .po-jhi-count, #po-cara-panel .po-jhi-count, #po-madison-panel .po-jhi-count { text-align: right; white-space: nowrap; }
#po-jhi-panel .po-jhi-lines, #po-cara-panel .po-jhi-lines, #po-madison-panel .po-jhi-lines { display: none; }
/* A phone: each invoice is a block, number over date, with the amount and
   its button to the right, so nothing runs off the side. */
@media (max-width: 600px) {
    #po-jhi-panel .data-table, #po-jhi-panel .data-table tbody, #po-cara-panel .data-table, #po-madison-panel .data-table, #po-cara-panel .data-table tbody, #po-madison-panel .data-table tbody { display: block; }
    #po-jhi-panel .data-table thead, #po-cara-panel .data-table thead, #po-madison-panel .data-table thead { display: none; }
    #po-jhi-panel .data-table tr, #po-cara-panel .data-table tr, #po-madison-panel .data-table tr {
        display: grid; grid-template-columns: minmax(0, 1fr) auto auto;
        grid-template-areas: "num amt act" "date amt act" "lines lines lines";
        align-items: center; column-gap: var(--sp-2);
        padding: var(--sp-2) 0; border-bottom: 1px solid var(--border);
    }
    #po-jhi-panel .data-table td, #po-cara-panel .data-table td, #po-madison-panel .data-table td { padding: 0; border: 0; max-width: none; }
    #po-jhi-panel .po-jhi-num, #po-cara-panel .po-jhi-num, #po-madison-panel .po-jhi-num { grid-area: num; white-space: normal; }
    #po-jhi-panel .po-jhi-date, #po-cara-panel .po-jhi-date, #po-madison-panel .po-jhi-date { grid-area: date; color: var(--text-muted); font-size: 0.85rem; }
    #po-jhi-panel .po-jhi-amt, #po-cara-panel .po-jhi-amt, #po-madison-panel .po-jhi-amt { grid-area: amt; }
    #po-jhi-panel .po-jhi-act, #po-cara-panel .po-jhi-act, #po-madison-panel .po-jhi-act { grid-area: act; overflow: visible; }
    #po-jhi-panel .po-jhi-count, #po-cara-panel .po-jhi-count, #po-madison-panel .po-jhi-count { display: none; }
    #po-jhi-panel .po-jhi-lines, #po-cara-panel .po-jhi-lines, #po-madison-panel .po-jhi-lines {
        display: block; grid-area: lines; white-space: normal;
        margin-top: var(--sp-1); font-size: 0.85rem; color: var(--text-muted);
    }
}
.po-jhi-more {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    gap: var(--sp-2); margin-top: var(--sp-3); font-size: 0.85rem; color: var(--text-muted);
}
/* "Read from JHI 2 h ago. [Refresh]" leads the list: text one side, button the other. */
.po-jhi-age {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    gap: var(--sp-2); margin-bottom: var(--sp-3); font-size: 0.85rem; color: var(--text-muted);
}
.po-jhi-note { text-align: center; color: var(--text-muted); font-size: 0.85rem; }
.po-jhi-note--bad { color: var(--danger); }

/* Defaults sit at the foot of the page: set once, rarely touched. The
   summary reads as a quiet note beside the heading. */
#po-defaults-section { margin-top: var(--sp-6); }
#po-defaults-summary {
    font-weight: 400; text-transform: none; letter-spacing: 0;
    color: var(--text-muted); margin-left: 0.5rem; font-size: 0.85rem;
}
/* The four defaults two to a row on a desktop, one per row on a phone. */
#po-defaults-form .form-row { display: grid; gap: var(--sp-4); grid-template-columns: minmax(0, 1fr); }
@media (min-width: 700px) {
    #po-defaults-form .form-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Preview: the Total is the number people check against the paper invoice. */
#po-invoice-header { border-radius: var(--radius-lg); padding: var(--sp-3) var(--sp-4); }
#po-invoice-header .invoice-header-grid div:has(> #po-total) .value {
    font-size: 1.05rem; font-weight: 700;
}
@media (max-width: 600px) {
    #po-invoice-header { padding: var(--sp-2) var(--sp-3); }
    .invoice-header-grid { gap: 0.15rem 1rem; }
}
/* PO Line Items: a small-caps label like the others on this page, not a
   page heading, so the figures above and the table under it lead. */
#po-preview > h2 {
    margin: var(--sp-5) 0 var(--sp-2);
    font-size: 0.75rem; font-weight: 600; letter-spacing: 0.04em;
    text-transform: uppercase; color: var(--text-muted);
}
/* The header card: a blue edge like the tiles', the supplier first and
   bold, the Total in the accent. */
#po-invoice-header { border-left: 4px solid var(--accent); }
#po-invoice-header #po-supplier { font-size: 1.05rem; font-weight: 700; }
#po-invoice-header #po-total { color: var(--ink-blue); }
/* Discard is the way out, not the way on: an outline until it is hovered,
   so Confirm & Sync is the one solid button in the bar. */
#po-preview #po-discard-btn { background: transparent; color: var(--danger); border: 1px solid var(--danger); }
#po-preview #po-discard-btn:hover:not(:disabled) { background: var(--danger); color: var(--on-solid); }
/* PO review: the buttons under the table. Markup, its box and Fill Blank
   Prices are one group (.po-fill-group): on a narrow row the label used to
   stay up at the end of one line while its box and button dropped to the
   start of the next. The group keeps to the right, as the Confirm bar's
   buttons do. On a phone the buttons are a little narrower, so the three on
   the left share one line and the group has the next. */
#po-preview .table-row-actions { flex-wrap: wrap; gap: var(--sp-2); }
.po-fill-group {
    display: flex; align-items: center; gap: var(--sp-2);
    margin-left: auto; font-size: 0.9rem; white-space: nowrap;
}
.po-fill-group input { width: 5.5rem; }
@media (max-width: 600px) {
    #po-preview .table-row-actions .btn { padding-inline: 0.55rem; }
    .po-fill-group { flex: 1 1 100%; }
    .po-fill-group input { flex: 1 1 5.5rem; min-width: 0; }
}
/* Review: a column with nothing in it is hidden. po_processing.js sets the
   classes: Group and Size until a line has one or Group sizes is pressed,
   Location while there are no stock locations to pick from. */
#po-items-table.po-no-sizes :is(th,td)[data-key="variant_group"],
#po-items-table.po-no-sizes :is(th,td)[data-key="option_value"],
#po-items-table.po-no-location [data-key="_location"] { display: none; }
/* .btn sets display, which would beat the hidden attribute. */
#po-group-sizes-btn[hidden] { display: none; }
/* Keep Confirm in reach at the foot of a long invoice. */
#po-preview .action-bar {
    position: sticky; bottom: 0; z-index: 5;
    background: var(--bg); padding: var(--sp-3) 0;
    box-shadow: 0 -8px 12px -8px rgba(0, 0, 0, 0.25);
}
/* The Confirm bar, tidied. It is pinned to the foot of the screen, so what
   it costs in height comes off the table. Its parts are one size and one
   height; rows sit closer when it wraps; and the buttons keep to the right
   on a row of their own, without the divider that then started the row. */
#po-preview .action-bar { gap: var(--sp-2) var(--sp-4); }
#po-preview .action-bar-flags { gap: var(--sp-2) var(--sp-4); font-size: 0.9rem; }
#po-preview .action-bar-buttons { margin-left: auto; }
#po-preview .action-bar-buttons::before { display: none; }
#po-preview .action-bar-buttons .btn { min-height: 2.2rem; white-space: nowrap; }
@media (min-width: 601px) {
    #po-preview .action-bar select { height: 2.2rem; padding-block: 0; }
}
/* A phone: the two pickers share a row and the three buttons share one, so
   the pinned bar is three rows, not five. */
@media (max-width: 600px) {
    #po-preview .action-bar-flags { width: 100%; }
    #po-preview .action-bar-flags select { min-width: 0; }
    #po-preview #po-inventory-mode { flex: 3 1 9rem; }
    #po-preview #po-location-wrap { flex: 2 1 8rem; display: flex; align-items: center; gap: var(--sp-2); white-space: nowrap; }
    #po-preview #po-location-slot { flex: 1 1 auto; min-width: 0; display: flex; }
    #po-preview #po-location-slot select { flex: 1 1 auto; width: 100%; }
    #po-preview .action-bar-buttons { width: 100%; margin-left: 0; }
    #po-preview .action-bar-buttons .btn { flex: 1 1 auto; justify-content: center; min-height: 2.5rem; padding-inline: 0.6rem; }
}

/* Recent Jobs: status as a pill, numbers lined up. */
#po-history h2 { margin-bottom: var(--sp-3); }
#po-history-table td[data-col="item_count"],
#po-history-table td[data-col="created_count"],
#po-history-table td[data-col="updated_count"] {
    font-weight: 600; font-variant-numeric: tabular-nums;
}
/* A tighter table. The counts were big condensed figures in columns a third
   of the page apart; now they are the row's own size, and every column but
   File is only as wide as it needs, so File takes the slack and the counts,
   status and date sit together. Supplier, File, three counts, Status, Date:
   the th have no data-col, so they go by position. */
#po-history-table .data-table th { padding-block: 0.4rem; }
#po-history-table .data-table td { padding-block: 0.3rem; }
#po-history-table .data-table th:not(:nth-child(2)),
#po-history-table .data-table td:not([data-col="file_name"]) { width: 1%; }
/* File: whatever is left, a long name trimmed, never squeezed under 12rem. */
#po-history-table .data-table td[data-col="file_name"] { max-width: 0; }
#po-history-table .data-table th:nth-child(2) { min-width: 12rem; }
/* Beside the sidebar on an iPad the row is 686 to 797px, and the table's
   900px floor put Status and Date off the right edge (#2326). Here the table
   takes the box it is in: File gives way, and the pager's buttons are
   touch size up to an iPad held across (the global 44px rule stops at 768). */
#po-history-table .data-table { min-width: 0; }
/* Seven columns need about 860px, and the box is 460 to 570 beside the
   sidebar. From a big phone to an iPad held across, Created and Updated step
   aside (Products still totals the job), File shrinks to what is left, and
   the Date may take two lines, so Status and Date stay in view. (.data-table
   in these selectors outranks the later nowrap rules for the same cells.) */
@media (min-width: 601px) and (max-width: 1100px) {
    #po-history-table .data-table th:nth-child(4), #po-history-table .data-table th:nth-child(5),
    #po-history-table .data-table td[data-col="created_count"],
    #po-history-table .data-table td[data-col="updated_count"] { display: none; }
    #po-history-table .data-table th:nth-child(2) { min-width: 3rem; }
    #po-history-table .data-table th { padding-inline: 0.3rem; }
    #po-history-table .data-table td { padding-inline: 0.3rem; }
    #po-history-table .data-table td[data-col="created_at"] { white-space: normal; min-width: 0; }
    #po-history-table .po-status { white-space: normal; }
    #po-history-table .data-table td[data-col="supplier"] { white-space: normal; }
    #po-history-table .data-table th, #po-history-table .data-table td { font-size: 0.8rem; }
    #po-history-table .po-sup-chip { display: none; }
}
@media (max-width: 1024px) {
    #po-history-table .list-pager .btn,
    #po-history-table .list-pager select { min-height: 44px; box-sizing: border-box; }
}
/* In the Defaults pop-up the two sections are open, one over the other. */
/* ...and plain. They were built as folding strips for the foot of the page:
   a bordered section, a header that folds, a bordered card inside, and a
   summary line that repeats the fields under it. In a pop-up that is boxes
   in boxes: here each is a heading over its fields, with a line between.
   PO Defaults' own heading goes, since the pop-up's title says it. */
/* The heading the way New Manual PO has it: a line saying what the pop-up
   is for, and a divider before the fields. */
#po-settings-modal .pm-head {
    display: flex; justify-content: space-between; align-items: flex-start; gap: var(--sp-3);
    margin-bottom: var(--sp-4); padding-bottom: var(--sp-3); border-bottom: 1px solid var(--border);
}
#po-settings-modal .pm-head h2 { margin: 0; }
#po-settings-modal .pm-sub { margin: var(--sp-1) 0 0; font-size: 0.85rem; color: var(--text-muted); }
#po-settings-modal .pm-close { font-size: 1.4rem; line-height: 1; color: var(--text-muted); }
#po-defaults-form .form-group label {
    font-size: 0.75rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted);
}
#po-defaults-form > .btn { min-width: 10rem; }
#po-settings-modal .settings-section { margin: 0; border: 0; border-radius: 0; overflow: visible; }
#po-settings-modal .settings-section + .settings-section {
    margin-top: var(--sp-4); padding-top: var(--sp-4); border-top: 1px solid var(--border);
}
#po-settings-modal .settings-section-header { padding: 0; background: none; cursor: default; pointer-events: none; }
#po-settings-modal .settings-chevron,
#po-settings-modal #po-defaults-toggle { display: none; }
#po-settings-modal .settings-body,
#po-settings-modal .settings-section.collapsed .settings-body { display: block; padding: var(--sp-3) 0 0; }
#po-settings-modal #po-defaults-section .settings-body { padding-top: 0; }
#po-settings-modal .settings-body .card { margin: 0; padding: 0; border: 0; background: none; box-shadow: none; }
/* The four fields in two level rows: a label never wraps, so a long one
   can't push its box below its neighbour's. */
#po-defaults-form .form-row { align-items: start; }
#po-defaults-form .form-group { margin-bottom: 0; }
#po-defaults-form .form-group label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#po-defaults-form > .btn { margin-top: var(--sp-4); }

/* New Manual PO: the four top fields one height, and a line table whose
   Name box takes the room. Each cell had the big tables' padding, which left
   Name 100px and cut a nine-letter SKU short. */
/* New Manual PO: a heading with a line under it saying what the pop-up is
   for, the four fields in two columns, a small caps label over the lines,
   and Cancel / Review PO set apart by a line at the foot. */
#po-manual-modal .pm-head {
    display: flex; justify-content: space-between; align-items: flex-start; gap: var(--sp-3);
    margin-bottom: var(--sp-4); padding-bottom: var(--sp-3); border-bottom: 1px solid var(--border);
}
#po-manual-modal .pm-head h2 { margin: 0; }
#po-manual-modal .pm-sub { margin: var(--sp-1) 0 0; font-size: 0.85rem; color: var(--text-muted); }
#po-manual-modal .pm-close { font-size: 1.4rem; line-height: 1; color: var(--text-muted); }
#po-manual-modal .pm-fields { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-4); }
#po-manual-modal .pm-fields .pm-wide { grid-column: 1 / -1; }
#po-manual-modal .pm-section {
    margin: 0 0 var(--sp-2); font-size: 0.75rem; font-weight: 600;
    letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted);
}
#po-manual-modal .pm-row-tools { display: flex; align-items: center; gap: var(--sp-3); margin: var(--sp-2) 0 var(--sp-4); }
#po-manual-modal .pm-status { font-size: 0.82rem; color: var(--text-muted); }
#po-manual-modal .modal-actions { margin-top: var(--sp-4); padding-top: var(--sp-3); border-top: 1px solid var(--border); }
@media (max-width: 600px) {
    #po-manual-modal .pm-fields { grid-template-columns: 1fr; }
}
#po-manual-modal .form-group { margin-bottom: 0; }
#po-manual-modal .form-group .form-input { width: 100%; height: 2.3rem; padding-block: 0; }
#po-manual-modal .data-table th,
#po-manual-modal .data-table td { padding: 0.3rem 0.25rem; }
#po-manual-modal .data-table td { max-width: none; overflow: visible; }
#po-manual-modal .data-table td .form-input { padding: 0.35rem 0.5rem; }
#po-manual-modal .data-table th:first-child,
#po-manual-modal .data-table td:first-child { padding-left: 0.5rem; }
#po-history-table td[data-col="created_at"] { white-space: nowrap; color: var(--text-muted); }
/* To carry to the other shop (#1957): the running list above Recent Jobs. */
/* Shop transfers: opens under its tile, over Recent PO uploads. */
#po-carry {
    margin-bottom: var(--sp-4); padding: var(--sp-3) var(--sp-4);
    background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius);
}
.po-carry-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.po-carry-help { color: var(--text-muted); margin: var(--sp-2) 0 0; }
.po-carry-shop h3 { margin: var(--sp-4) 0 var(--sp-2); font-size: 1rem; }
.po-carry-units { color: var(--text-muted); font-weight: 400; margin-left: var(--sp-2); }
.po-owed-listed { color: var(--text-muted); font-weight: 400; }
.po-carry-table td { vertical-align: top; }
.po-carry-tick { width: 4.5rem; text-align: center; }
.po-carry-done { width: 1.25rem; height: 1.25rem; cursor: pointer; }
.po-carry-qty { text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.po-carry-sku { white-space: nowrap; font-variant-numeric: tabular-nums; }
.po-carry-from { color: var(--text-muted); }
/* Checking a delivery in by scanning (#2779): the Stock Take card, buttons and
   box (stock-take.css) over the review's table. Four short columns need no
   .data-table 900px, and Name wraps rather than ellipsising. */
.po-scan { margin-bottom: var(--sp-4); }
/* An opened PO that is still owed something, on a phone (#2776): the tick box
   and the SKU stay at the left while the table scrolls inside its box to
   Arrived and Not coming, so you can see which line you are typing in; and the
   tick box is a finger-sized target (it was 16px). scroll-padding keeps a box
   that takes focus from scrolling in under them. */
@media (max-width: 600px) {
    .po-job-lines .table-container { scroll-padding-left: 9.75rem; }
    .data-table.po-job-table input[type="checkbox"].po-job-tick { width: 2.75rem; height: 2.75rem; }
    .po-job-table th:first-child, .po-job-table td.po-carry-tick {
        position: sticky; left: 0; z-index: 1; width: 3.25rem; min-width: 3.25rem; padding: 0;
    }
    .po-job-table td.po-carry-tick { background: var(--bg-card); }
    .po-job-table th:nth-child(2), .po-job-table td.po-carry-sku {
        position: sticky; left: 3.25rem; z-index: 1; width: 6.5rem; min-width: 6.5rem; max-width: 6.5rem;
        white-space: normal; overflow-wrap: anywhere;
    }
    .po-job-table td.po-carry-sku { background: var(--bg-card); }
}
.po-scan .st-card { max-width: 60rem; }
.data-table.po-scan-table { min-width: 0; }
.data-table.po-scan-table th,
.data-table.po-scan-table td { padding: 0.5rem 0.4rem; white-space: normal; max-width: none; overflow: visible; overflow-wrap: anywhere; }
/* The line's picture: small enough that the printout stays one line a row. */
.po-carry-pic { width: 3.25rem; }
.po-carry-img {
    display: block; width: 40px; height: 40px; object-fit: contain;
    border: 1px solid var(--border); border-radius: 4px; background: #fff;
}
.po-carry-row--done td:not(.po-carry-tick) { text-decoration: line-through; opacity: 0.55; }
/* Five short columns: no need for .data-table's 900px, which ran the page
   off the side of a phone or tablet (#1978). Narrower than this, it scrolls
   inside its own .table-container. */
.po-carry-table { min-width: 30rem; }
.data-table.po-carry-table td.po-carry-name,
.data-table.po-carry-table td.po-carry-from { white-space: normal; max-width: none; overflow: visible; }
/* In a wide list the shops' tables share one set of column widths, so To
   Boyle and To Sligo line up under each other; each used to size its columns
   to its own lines. Name takes what is left, and Delivery stays on one line,
   which is what made a row two lines tall. In a list under 52rem the fixed
   widths would leave Name nothing, so there the columns size themselves as
   before. The list's own width, not the screen's, which cannot see the
   sidebar; the printed sheet is outside #po-carry, so it is untouched. */
#po-carry { container: po-carry / inline-size; }
@container po-carry (min-width: 52rem) {
    .po-carry-shop .po-carry-table { table-layout: fixed; }
    .po-carry-shop .po-carry-table th:nth-child(3) { width: 4rem; }
    .po-carry-shop .po-carry-table th:nth-child(4) { width: 8rem; }
    .po-carry-shop .po-carry-table th:nth-child(6) { width: 17rem; }
    .po-carry-shop .data-table.po-carry-table td.po-carry-from {
        white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .po-carry-shop .po-carry-table td.po-carry-sku { overflow: hidden; text-overflow: ellipsis; }
}
/* In a narrow list the table scrolls inside its own box, as it always did;
   Name and Delivery keep enough width there to read. Squeezed to a few
   letters a line, one long name made a row 247px tall on a phone. */
.po-carry-shop .po-carry-table td.po-carry-name { min-width: 12rem; }
.po-carry-shop .po-carry-table td.po-carry-from { min-width: 9rem; }
/* The Moved log is a quiet line under the list, with room over it. */
.po-carry-log { margin-top: var(--sp-3); }
.po-carry-log > summary { cursor: pointer; font-size: 0.9rem; font-weight: 600; color: var(--text-muted); }
.po-carry-log[open] > summary { margin-bottom: var(--sp-2); }
/* The log's own table. It is history, so it is a size smaller. It has a
   column more than the shops' tables (To) and a wider first one (when, and
   who), so its fixed columns need a wider list than theirs: 61rem. There,
   Delivery stays on one line, which is what made every row two lines tall.
   The first column may wrap, so a long user name is never cut off. In a
   narrower list Name and Delivery keep enough width to read; squeezed, one
   long name made a row 339px tall on a phone. */
.po-carry-log .po-carry-table { font-size: 0.85rem; }
.po-carry-log .po-carry-table td.po-carry-name { min-width: 12rem; }
.po-carry-log .po-carry-table td.po-carry-from { min-width: 9rem; }
@container po-carry (min-width: 61rem) {
    .po-carry-log .po-carry-table { table-layout: fixed; }
    .po-carry-log .po-carry-table th:nth-child(1) { width: 12.5rem; }
    .po-carry-log .po-carry-table th:nth-child(3) { width: 3.5rem; }
    .po-carry-log .po-carry-table th:nth-child(4) { width: 7.5rem; }
    .po-carry-log .po-carry-table th:nth-child(6) { width: 4.5rem; }
    .po-carry-log .po-carry-table th:nth-child(7) { width: 16rem; }
    .po-carry-log .data-table.po-carry-table td.po-carry-from,
    .po-carry-log .po-carry-table td.po-carry-sku { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .po-carry-log .data-table.po-carry-table td:first-child { white-space: normal; }
}
/* Print (#1978): the sheet printCarry() adds to <body>, and nothing else.
   display, not visibility, so the hidden page takes up no paper. */
#po-carry-sheet { display: none; }
@media print {
    body.po-carry-printing > *:not(#po-carry-sheet) { display: none !important; }
    /* The paper too (#1990): <body> carries the theme's background, which
       printed as a black block under the list with "Background graphics" on. */
    html:has(body.po-carry-printing), body.po-carry-printing { background: #fff !important; }
    body.po-carry-printing #po-carry-sheet { display: block; color: #000; background: #fff; }
    body.po-carry-printing #po-carry-sheet * { color: #000 !important; background: #fff !important; }
    body.po-carry-printing #po-carry-sheet .table-container { overflow: visible; }
    body.po-carry-printing #po-carry-sheet .po-carry-table { min-width: 0; width: 100%; }
    body.po-carry-printing #po-carry-sheet td, body.po-carry-printing #po-carry-sheet th { border-bottom: 1px solid #999; }
    body.po-carry-printing #po-carry-sheet .po-carry-done { appearance: none; border: 1px solid #000; border-radius: 0; }
}
.po-status { display: inline-block; padding: 0.15rem 0.6rem; border-radius: 999px; font-size: 0.78rem; font-weight: 600; white-space: nowrap; }
/* Light theme: green and amber a shade darker than the shared inks, which
   fall just under 4.5:1 on their own tinted pill (4.25 and 3.95 on the page
   colour). The dark theme's inks are already light enough. */
.po-status--completed { background: var(--success-soft); color: #0b5f2a; }
.po-status--partial, .po-status--partly { background: var(--warning-soft); color: #7a4a05; }
[data-theme="dark"] .po-status--completed { color: var(--ink-green); }
[data-theme="dark"] .po-status--partial, [data-theme="dark"] .po-status--partly { color: var(--ink-amber); }
.po-status--preview   { background: var(--accent-soft);  color: var(--ink-blue); }
.po-status--discarded { background: var(--bg-input);    color: var(--text-muted); }
.po-status--other     { background: var(--bg-input);     color: var(--text-muted); }
/* A dot before the word, in the pill's own colour, so the column reads as
   a run of lights down the page. */
#po-history-table .po-status { display: inline-flex; align-items: center; gap: 0.4rem; }
#po-history-table .po-status::before {
    content: ""; width: 0.45rem; height: 0.45rem; border-radius: 50%; background: currentColor;
}
/* Recent PO uploads: the supplier leads, in a round chip; a count of nothing
   fades; an upload that went nowhere is quieter than one still to do. */
#po-history-table td[data-col="supplier"] { font-weight: 600; white-space: nowrap; }
.po-sup-chip {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.5rem; height: 1.5rem; margin-right: var(--sp-2); border-radius: 50%;
    background: var(--accent-soft); color: var(--ink-blue); font-size: 0.75rem; font-weight: 700;
}
#po-history-table td[data-col="file_name"] { color: var(--text-muted); }
#po-history-table tr.po-row--discarded td:not([data-col="status"]) { color: var(--text-muted); }
#po-history-table tr.po-row--partial td[data-col="file_name"],
#po-history-table tr.po-row--preview td[data-col="file_name"] { color: var(--text); }

/* ── Help: the guide as folded cards with search (#1176) ─────────────────
   help.js builds these from docs/USER_GUIDE.md: a heading per "##" area, a
   card per "###" screen, "In short" bullets as the preview, and "More
   detail" <details> blocks folded inside. */
#help-content { max-width: 70ch; line-height: 1.55; overflow-wrap: anywhere; }
#help-content li + li { margin-top: 0.35rem; }
#help-content ol > li { padding-left: 0.2rem; }
#help-content table { display: block; overflow-x: auto; max-width: 100%; border-collapse: collapse; }
#help-content th, #help-content td { border: 1px solid var(--border); padding: 0.35rem 0.6rem; text-align: left; vertical-align: top; }
#help-content pre { overflow-x: auto; }
.help-tools {
    display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center;
    margin-bottom: 1rem;
}
.help-search {
    flex: 1 1 14rem; min-width: 0; min-height: 44px; padding: 0.5rem 0.75rem;
    font-size: 1rem; color: var(--text); background: var(--bg-input);
    border: 1px solid var(--border); border-radius: var(--radius);
}
.help-search:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.help-tools-buttons { display: flex; gap: 0.5rem; }
.help-no-matches { color: var(--text-muted); font-style: italic; }
/* How to use Help, above the cards rather than folded inside one (#1218). */
.help-how { color: var(--text-muted); font-size: 0.9rem; margin: 0.25rem 0 0.75rem; }
.help-group-title {
    margin: 1.75rem 0 0.6rem; padding-bottom: 0.3rem;
    font-size: 1.15rem; border-bottom: 1px solid var(--border);
}
.help-card {
    margin: 0 0 0.6rem; border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--bg-card);
}
.help-card.is-open { border-color: var(--accent); }
.help-card-heading { margin: 0; font-size: 1.02rem; }
.help-card-toggle {
    display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
    width: 100%; min-height: 44px; padding: 0.6rem 0.9rem;
    font: inherit; font-weight: 600; text-align: left; color: var(--text-strong);
    background: none; border: 0; border-radius: var(--radius); cursor: pointer;
}
.help-card-toggle:hover { background: var(--bg-hover); }
.help-card-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.help-card-chevron::before { content: '\25B8'; display: inline-block; color: var(--text-muted); transition: transform 0.15s; }
.help-card.is-open .help-card-chevron::before { transform: rotate(90deg); }
.help-card-short {
    padding: 0 0.9rem 0.6rem; color: var(--text-muted); font-size: 0.93rem; cursor: pointer;
}
.help-card-short ul, .help-card-short ol { margin: 0; padding-left: 1.2rem; }
.help-card.is-open .help-card-short { cursor: default; }
.help-card-body { padding: 0.2rem 0.9rem 0.9rem; border-top: 1px solid var(--border); }
.help-card-body ol, .help-card-body ul { padding-left: 1.6rem; }
.help-card-body h4 { margin: 1.1rem 0 0.4rem; font-size: 1rem; }
.help-card-body hr { border: 0; border-top: 1px dashed var(--border); margin: 1rem 0; }
.help-more {
    margin: 0.6rem 0; padding: 0 0.75rem; border-left: 3px solid var(--accent);
    background: var(--accent-soft); border-radius: var(--radius-sm);
}
.help-more > summary {
    min-height: 40px; display: flex; align-items: center; gap: 0.35rem;
    cursor: pointer; font-weight: 600; color: var(--ink-blue); list-style: none;
}
.help-more > summary::-webkit-details-marker { display: none; }
.help-more > summary::before { content: '+'; }
.help-more[open] > summary::before { content: '\2212'; }
.help-more[open] { padding-bottom: 0.5rem; }
mark.help-hit { background: var(--warning-soft); color: inherit; outline: 1px solid var(--warning); border-radius: 2px; }
@media (max-width: 600px) {
    /* The card around the guide has inline padding in index.html. */
    .card:has(> #help-content) { padding: 1rem 0.75rem !important; }
    .help-tools-buttons { width: 100%; }
    .help-tools-buttons .btn { flex: 1; min-height: 40px; }
}

/* ── "?" beside each page title: opens Help at that page (#1206) ───────── */
.help-q {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.5rem; height: 1.5rem; margin-left: 0.5rem; padding: 0;
    vertical-align: middle; border-radius: 50%;
    border: 1px solid var(--border); background: var(--bg-card); color: var(--text-muted);
    font: 700 0.9rem/1 inherit; cursor: pointer; position: relative;
}
.help-q:hover, .help-q:focus-visible { color: var(--accent); border-color: var(--accent); }
/* A bigger tap target than it looks, for the iPad. */
.help-q::after { content: ''; position: absolute; inset: -0.6rem; }

/* ── Sticky selection bar ────────────────────────────────────────────────
   Shared by any list where ticking rows unlocks an action (Products'
   Bulk Action, #1285). Pinned to the foot of the screen, shown with the
   [hidden] attribute rather than inline display (#1087). */
.selection-bar {
    position: sticky; bottom: var(--sp-3); z-index: 20;
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2) var(--sp-3);
    width: fit-content; max-width: 100%; margin: var(--sp-4) auto 0;
    padding: var(--sp-2) var(--sp-2) var(--sp-2) var(--sp-4);
    background: var(--surface-raised); border: 1px solid var(--border);
    border-radius: 999px; box-shadow: var(--shadow-lg);
}
.selection-bar[hidden] { display: none; }
.selection-bar-count { font-weight: 600; margin-right: var(--sp-2); }
@media (max-width: 600px) {
    .selection-bar { width: 100%; border-radius: var(--radius-lg); justify-content: center; }
}

/* ── Header "More" menu: occasional jobs kept out of the everyday row ─── */
.page-more { position: relative; }
.page-more[hidden] { display: none; }
.page-more > summary { list-style: none; }
.page-more > summary::-webkit-details-marker { display: none; }
.page-more-menu {
    position: absolute; right: 0; top: calc(100% + 4px); z-index: 30;
    display: flex; flex-direction: column; align-items: stretch; min-width: 12rem;
    padding: var(--sp-1); background: var(--surface-raised);
    border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-md);
}
.page-more-menu .btn { justify-content: flex-start; text-align: left; width: 100%; }

/* ── Products header and filter row (#1285) ──────────────────────────────
   One bar: the "?", search, the three filter dropdowns and the buttons, with
   the summary cards under it (3 Oct 2026). The sidebar names the page, so the
   heading's word is for screen readers. Search is the everyday job, so it
   takes the spare width; the filters are equal dropdowns. */
#page-products .page-header { flex-wrap: wrap; align-items: flex-start; gap: var(--sp-3); margin-bottom: var(--sp-3); }
/* As tall as the bar's controls, so the "?" stays level with the search box
   when the buttons drop to a second line. */
#page-products .products-title { margin: 0; display: flex; align-items: center; min-height: 40px; }
#page-products .products-title .help-q { margin-left: 0; }
.products-title-text {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* One line on a laptop: each flex-basis is the least the piece can live with,
   since a row breaks on the bases before anything shrinks. The spare width
   goes to search first, then the dropdowns, and what is left sits before the
   buttons, which keep to the right. */
#page-products .page-actions { flex: 1 1 32rem; flex-wrap: wrap; justify-content: flex-start; }
.product-search-wrap { display: flex; gap: var(--sp-2); flex: 2 1 15rem; min-width: 0; max-width: 32rem; margin-bottom: 0; }
.product-search-wrap .input-search { flex: 1 1 auto; min-width: 0; width: auto; }
/* The general .input-search cap (260px) kept the box narrow inside a wrap
   that had room, leaving a gap before Reorder List (#1301). */
#page-products .product-search-wrap .input-search { max-width: none; }
/* The three dropdowns stay side by side and share their box equally; they
   used to wrap two over one beside the search box. */
.product-filters { display: flex; flex-wrap: nowrap; gap: var(--sp-2); margin: 0; flex: 1 1 22rem; min-width: 0; max-width: 37rem; }
/* 22rem is three dropdowns just wide enough to show "All Vendors" whole. */
.product-filters select { flex: 1 1 0; min-width: 0; width: auto; max-width: none; padding-left: var(--sp-2); padding-right: var(--sp-2); }
.product-filters .is-set { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
/* Every control in the bar is the one height, so the row has a level top
   and bottom. */
#page-products .page-actions .input-search,
#page-products .page-actions .btn { height: 40px; }
#page-products .page-actions .btn { white-space: nowrap; }
.products-buttons { display: flex; gap: var(--sp-2); flex: 0 0 auto; margin-left: auto; }
/* A tablet held upright and a phone: the "?" at the top left, then search,
   the three filters side by side, and the buttons, each a line of its own. */
@media (max-width: 900px) {
    #page-products .page-header { flex-wrap: nowrap; }
    /* min-width 0: beside the "?" the bar has to give up width, or it ran
       off a phone's screen. */
    #page-products .page-actions { min-width: 0; }
    #page-products .page-actions > * { order: 3; }
    #page-products .page-actions > .product-search-wrap { order: 1; flex-basis: 100%; max-width: none; }
    #page-products .page-actions > .product-filters { order: 2; flex-basis: 100%; flex-wrap: nowrap; }
    #page-products .page-actions > .product-filters { max-width: none; }
}
/* Upright iPad and phone: the "?" has a 44px tap (its ::after), so its row is
   44px tall. Above 768 the row is the bar's 40px, so the "?" is level with the
   search box instead of 2px under it (#2295). */
@media (max-width: 768px) {
    #page-products .products-title { min-height: 44px; }
}
/* A phone: the three buttons share the last line, edge to edge. */
@media (max-width: 600px) {
    /* The "?" takes 44px of the width, leaving three dropdowns about 95px each:
       "All Status", "All Stock" and "All Vendors" were cut off. Two share a
       line and Vendors takes the next (#2295). */
    #page-products .page-actions > .product-filters { flex-wrap: wrap; }
    .product-filters select { flex: 1 1 calc(50% - var(--sp-2)); }
    .product-filters select#product-vendor-filter { flex-basis: 100%; }
    #page-products .page-actions > .products-buttons { flex: 1 1 100%; min-width: 0; }
    .products-buttons > .btn, .products-buttons > .page-more { flex: 1 1 auto; min-width: 0; }
    .products-buttons .btn { justify-content: center; padding-left: var(--sp-2); padding-right: var(--sp-2); }
    .products-buttons > .page-more > summary { width: 100%; }
}

/* ── Parts Ordering: supplier cards, To call, selection bar (#1284) ──────
   ordering.js renders these; they had no rules at all before, so each
   supplier's heading, count, Select all and buttons stacked as plain lines. */
#ordering-content .supplier-section {
    background: var(--bg-card); border: 1px solid var(--border);
    border-radius: var(--radius-lg); margin-bottom: var(--sp-5); overflow: hidden;
}
#ordering-content .supplier-header {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2) var(--sp-3);
    padding: var(--sp-3) var(--sp-4); background: var(--bg-input);
    border-bottom: 1px solid var(--border);
}
#ordering-content .supplier-header h3 { margin: 0; font-size: 1.05rem; }
.supplier-subtotal { color: var(--text-muted); font-size: 0.85rem; font-variant-numeric: tabular-nums; }
#ordering-content .supplier-actions {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2); margin-left: auto;
}
.supplier-select-all { display: inline-flex; align-items: center; gap: 0.35rem; cursor: pointer; font-size: 0.85rem; }
#ordering-content .supplier-section .table-container { margin: 0; border: 0; border-radius: 0; }
#ordering-content .supplier-section .parts-list-total { padding: var(--sp-3) var(--sp-4); margin: 0; }
.supplier-section-note { margin: 0; padding: var(--sp-3) var(--sp-4) 0; color: var(--text-muted); font-size: 0.85rem; }
.parts-list-total--grand { margin-top: var(--sp-2); border-top-width: 3px; font-size: 1.05rem; }
/* To call: the customer is waiting, so it comes first and stands out. */
#ordering-content .to-call-section { border-color: var(--purple); box-shadow: 0 0 0 1px var(--purple) inset; }
#ordering-content .to-call-section .supplier-header { background: var(--purple-soft); }
/* A ticked row is easy to lose in a long list. */
#ordering-content tr:has(.order-check:checked) td { background: var(--accent-soft); }
#ordering-content .order-check { width: 18px; height: 18px; cursor: pointer; }
.closed-job-badge {
    display: block; width: fit-content; margin-top: 0.2rem;
    font-size: 0.65rem; padding: 0.05rem 0.35rem;
}
.customer-order-badge { border: 0; cursor: pointer; }
.ordering-recent { margin-top: var(--sp-5); }
.ordering-recent > summary { cursor: pointer; padding: var(--sp-3) var(--sp-4); font-weight: 600; color: var(--text-muted); }
.part-status { white-space: nowrap; }

@media (max-width: 600px) {
    #ordering-content .supplier-actions { margin-left: 0; width: 100%; }
}

/* ── Cache page (#1287) ───────────────────────────────────────────────────
   The records come first; importing a supplier file is a button in the
   header that opens its drop zone, and the count, nightly refresh and
   weekly photo check share one summary card. */
.cache-import-panel { margin-bottom: var(--sp-3); }
.cache-import-hint { margin: 0 0 var(--sp-2); color: var(--text-muted); font-size: 0.9em; }
.cache-import-zone { padding: 1.25rem; margin-bottom: var(--sp-2); }
.cache-summary { margin-bottom: var(--sp-3); display: flex; flex-direction: column; gap: var(--sp-2); }
.cache-summary-count { display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap; }
.cache-summary-count h3 { margin: 0; }
.cache-summary-count .card-stat { margin: 0; }
.cache-summary-line { padding: 0.5rem 0.75rem; border-radius: 6px; font-size: 0.85em; }
.cache-records-header { margin-bottom: var(--sp-2); }
.cache-records-header h2 { margin: 0; }
.cache-pagination { margin-top: var(--sp-3); display: flex; align-items: center; gap: var(--sp-3); }

/* ── Bike Specs (#1286) ──────────────────────────────────────────────────
   One search box with a bike / part switch, the list, and the chosen
   bike's card beside it. Under 900px the card slides over the list
   instead of sharing a narrow screen with it. Shown and hidden with
   [hidden], never inline display (#1087). */
.bike-specs-search-card { margin-bottom: var(--sp-4); padding: var(--sp-3) var(--sp-4); }
.bike-specs-mode {
    display: inline-flex; padding: 3px; margin-bottom: var(--sp-3);
    background: var(--bg-input); border-radius: 999px;
}
.bike-specs-mode-btn {
    border: 0; background: none; color: var(--text-muted); cursor: pointer;
    font: inherit; font-size: 0.85rem; font-weight: 600;
    padding: 0.35rem 0.9rem; border-radius: 999px; min-height: 32px;
}
.bike-specs-mode-btn[aria-pressed="true"] { background: var(--bg-card); color: var(--text); box-shadow: var(--shadow-sm); }
.bike-specs-mode-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.bike-specs-search-row { display: flex; gap: var(--sp-2); align-items: center; }
.bike-specs-search-row[hidden], #bike-specs-component-results[hidden],
#bike-specs-component-clear[hidden] { display: none; }
.bike-specs-search-row .input-search { flex: 1 1 auto; min-width: 0; width: auto; max-width: none; }
#bike-specs-component-results { margin-top: var(--sp-3); }
.bike-part-results-count { margin: 0 0 var(--sp-2); font-size: 0.85rem; color: var(--text-muted); }
.bike-part-results { max-height: min(15rem, 35vh); overflow-y: auto; display: grid; gap: var(--sp-1); }
.bike-part-result {
    display: block; width: 100%; text-align: left; cursor: pointer;
    padding: var(--sp-2) var(--sp-3); font: inherit; color: var(--text);
    background: none; border: 1px solid transparent; border-radius: var(--radius);
}
.bike-part-result:hover { border-color: var(--accent); background: var(--accent-soft); }
.bike-part-result-match { display: block; font-size: 0.85rem; color: var(--text-muted); }

.bike-specs-layout { display: flex; gap: var(--sp-4); align-items: flex-start; }
.bike-specs-list-panel { flex: 1 1 auto; min-width: 0; }
/* With the card open the list gets ~720px on a laptop, less than the generic
   900px table minimum, so it scrolled sideways and hid the last columns
   (#1346). Drop the minimum, cap the cells tighter, and hide the Components
   count, the sixth column: the card beside it lists the bike's parts. */
.bike-specs-layout.has-detail .bike-specs-table { min-width: 0; }
.bike-specs-layout.has-detail .bike-specs-table td { max-width: 180px; }
.bike-specs-layout.has-detail .bike-specs-table :is(th, td):nth-child(6) { display: none; }
.bike-specs-row { cursor: pointer; }
.bike-specs-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.bike-specs-detail {
    flex: 0 0 380px; position: sticky; top: var(--sp-4);
    max-height: calc(100vh - 2rem); overflow-y: auto;
}
.bike-specs-detail[hidden] { display: none; }
.bike-specs-detail-card { padding: var(--sp-4); border-radius: var(--radius-lg); }
.bike-specs-detail-head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--sp-2); }
.bike-specs-detail-head h3 { margin: 0; font-size: 1.15rem; }
.bike-specs-detail-meta { margin: 0.2rem 0 0; color: var(--text-muted); font-size: 0.85rem; }
.bike-specs-detail-close { font-size: 1.1rem; min-width: 36px; min-height: 36px; }
.bike-specs-detail-actions { display: flex; gap: var(--sp-2); margin: var(--sp-3) 0; }
.bike-specs-detail-actions:empty { display: none; }
.bike-specs-detail-foot { margin-top: var(--sp-3); }
.bike-specs-empty { color: var(--text-muted); font-size: 0.9rem; }
.bike-comp-group { border-top: 1px solid var(--border); padding-top: var(--sp-2); margin-top: var(--sp-2); }
.bike-comp-group-title {
    margin: 0 0 var(--sp-1); font-size: 0.72rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted);
}
.bike-comp-row { display: flex; align-items: center; gap: var(--sp-1); min-height: 36px; }
.bike-comp-text { flex: 1 1 auto; min-width: 0; }
.bike-comp-value { display: block; overflow-wrap: anywhere; }
.bike-comp-notes { display: block; font-size: 0.8rem; color: var(--text-muted); }
.bike-comp-row .btn { min-height: 32px; }

@media (max-width: 900px) {
    /* The card slides over the list rather than squeezing beside it. */
    .bike-specs-detail {
        position: fixed; inset: 0 0 0 auto; z-index: 60; top: 0;
        width: min(100%, 420px); max-height: none; height: 100%;
        box-shadow: var(--shadow-lg);
    }
    .bike-specs-detail-card { min-height: 100%; border-radius: 0; }
}

/* A clear but not shouting Discard: red outline, fills red on hover. */
.btn-danger-outline { background: none; color: var(--danger); border: 1px solid var(--danger); }
.btn-danger-outline:hover:not(:disabled) { background: var(--danger); color: var(--on-solid); }

/* New Repair inside the app (#repairs/new): the form, framed over the board. */
/* !important: the board's pager sets display:flex inline, which beat this and
   left "Showing 1-48 of 48" over the form (#1594). */
#page-repairs.repairs--intake > :not(.repairs-intake) { display: none !important; }
/* On a computer the form is the page too: #content is one screen tall and the
   form takes what the bar leaves, so only the form scrolls. A fixed-height
   form in a page that also scrolled gave two scrollbars side by side (#1594). */
/* No padding round it: the form is the page, edge to edge, not a panel set
   into it (#2036). */
@media (min-width: 769px) {
    #content:has(> #page-repairs.repairs--intake) {
        display: flex; flex-direction: column;
        height: 100vh; height: 100dvh; padding: 0;
    }
}
#page-repairs.repairs--intake { flex: 1; min-height: 0; position: relative; }
.repairs-intake { position: absolute; inset: 0; display: flex; flex-direction: column; }
/* Back to the board waits here while the form loads, then moves into the
   form's ribbon beside the steps (showIntake), leaving this empty. It is at
   the right in both, where a right thumb reaches it (#2236). */
.repairs-intake-bar { display: flex; justify-content: flex-end; margin: 0; padding: 0.5rem 1rem; }
#repairs-intake-close { min-height: 44px; white-space: nowrap; }
#repairs-intake-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.repairs-intake-back-icon {
    flex: none; width: 1.15em; height: 1.15em;
    fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round;
}
.repairs-intake-bar:empty { display: none; }
/* The form's own box is `.repairs-intake > #new-repair` in new-repair.css.
   On a phone the form fills the screen under the phone bar (3.25rem, layout.css),
   edge to edge: no border or page padding around it, and the form is the one
   thing that scrolls. As a bordered box inside the padded page it lost 34px of
   width, clipped "Details", wrapped Walk-in, and scrolled inside a scroll (#1590). */
@media (max-width: 768px) {
    .repairs-intake {
        position: fixed; top: 3.25rem; left: 0; right: 0; bottom: 0; z-index: 90;
        display: flex; flex-direction: column; background: var(--bg);
    }
    .repairs-intake-bar {
        margin: 0; padding: 0.4rem 0.75rem;
        border-bottom: 1px solid var(--border); background: var(--bg-card);
    }
}

/* Part search on a phone (#1612). Each result's name gets its own full line,
   and the SKU, price and the stock and source badges share one small line
   under it: the row's contents join the item's own wrap. A side column of
   badges squeezed the names onto 2-3 lines, so only two results fitted. */
@media (max-width: 480px) {
    .product-search-item { display: flex; flex-wrap: wrap; align-items: center; column-gap: 0.4rem; row-gap: 0.15rem; padding: 0.5rem 0.75rem; }
    .product-search-item-row { display: contents; }
    .product-search-item-row > span:first-child { flex: 0 0 100%; order: 0; }
    .product-search-item-sub { order: 1; margin-top: 0; }
    .product-search-item .stock-badge,
    .product-search-item .source-badge { order: 2; margin-left: 0; font-size: 0.66rem; padding: 0.05rem 0.4rem; }
    /* While searching, the list gets the room: the footer steps aside. */
    .repair-panel .product-search-dropdown { max-height: 50vh; }
    .repair-panel:has(#rp-part-search:focus) .repair-panel-footer,
    .repair-panel:has(#rp-part-dropdown[style*="block"]) .repair-panel-footer { display: none; }
}
/* A long press on a footer button acts on it, not selects its text (#1612). */
.repair-panel-footer .btn { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }

/* Dashboard: each user's Customise panel (#1861), opened from the profile menu. */
@media (max-width: 768px) { #customise-dashboard-btn { display: none; } }
.dash-customise {
    background: var(--bg-card); border: 1px solid var(--border);
    border-radius: var(--radius); padding: 1rem 1.25rem; margin-top: 1rem; max-width: 460px;
}
.dash-customise[hidden] { display: none; }
.dash-customise-title { font-weight: 600; color: var(--text-strong); margin-bottom: 0.5rem; }
.dash-customise-list { list-style: none; margin: 0; padding: 0; }
.dash-customise-row {
    display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
    padding: 0.35rem 0; border-bottom: 1px solid var(--border);
}
.dash-customise-row label { display: flex; align-items: center; gap: 0.5rem; cursor: pointer; }
.dash-customise-move { display: flex; gap: 0.25rem; }
.dash-customise-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.75rem; }
