/* OperHub — Theme layer
 *
 * Loads after Bootstrap and retargets it at the tokens in tokens.css.
 * Wherever Bootstrap 5.3 exposes component-level custom properties we set
 * those rather than redeclaring the rule, so component behaviour, dark-mode
 * plumbing and specificity all stay Bootstrap's. Direct property overrides
 * appear only where 5.3 has no variable for the thing (forms, mainly).
 *
 * No markup change is required to pick any of this up.
 */

/* ================================================================
   1. Global — remap Bootstrap's root variables onto the tokens
   ================================================================ */

:root,
[data-bs-theme="light"] {
  --bs-body-bg:            var(--oh-canvas);
  --bs-body-color:         var(--oh-ink);
  --bs-body-font-family:   var(--oh-font);
  --bs-body-font-size:     var(--oh-text-md);
  --bs-body-line-height:   var(--oh-leading-body);
  --bs-emphasis-color:     var(--oh-ink);
  --bs-secondary-color:    var(--oh-ink-2);
  --bs-tertiary-color:     var(--oh-ink-3);
  --bs-border-color:       var(--oh-border);
  --bs-tertiary-bg:        var(--oh-surface-2);

  --bs-border-radius:       var(--oh-radius);
  --bs-border-radius-sm:    var(--oh-radius-sm);
  --bs-border-radius-lg:    var(--oh-radius-lg);

  --bs-primary:            var(--oh-accent);
  --bs-primary-rgb:        59, 91, 219;
  --bs-link-color:         var(--oh-accent);
  --bs-link-hover-color:   var(--oh-accent-hover);
  --bs-link-color-rgb:     59, 91, 219;

  --bs-focus-ring-color:   var(--oh-accent-soft);
  --bs-focus-ring-width:   3px;

  /* Semantic colours, so `.text-danger` and friends land on the palette instead
     of stock Bootstrap. These carry real meaning in this app — a negative
     Available, an overdue delivery — and must not be a second red.
     `.text-*` resolves through the -rgb triple, not the colour, so both are
     needed; the triples must be kept in step with the tokens by hand, the same
     way --bs-primary-rgb above is. */
  --bs-success:                 var(--oh-success);
  --bs-success-rgb:             28, 122, 67;    /* --oh-success #1c7a43 */
  --bs-success-text-emphasis:   var(--oh-success);
  --bs-warning:                 var(--oh-warning);
  --bs-warning-rgb:             146, 96, 11;    /* --oh-warning #92600b */
  --bs-warning-text-emphasis:   var(--oh-warning);
  --bs-danger:                  var(--oh-danger);
  --bs-danger-rgb:              180, 34, 34;    /* --oh-danger  #b42222 */
  --bs-danger-text-emphasis:    var(--oh-danger);
  --bs-info:                    var(--oh-info);
  --bs-info-rgb:                26, 109, 132;   /* --oh-info    #1a6d84 */
  --bs-info-text-emphasis:      var(--oh-info);
}

body {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  letter-spacing: 0;
}

h1, h2, h3, h4, h5, h6 {
  letter-spacing: var(--oh-tracking-tight);
  font-weight: var(--oh-weight-semi);
  color: var(--oh-ink);
}

/* Screen titles. Templates use <h2> at the top of a page, so this is the
   one that matters most. */
h2 { font-size: var(--oh-text-2xl); }
h3 { font-size: var(--oh-text-xl); }
h4, h5 { font-size: var(--oh-text-lg); }

/* Dashboard figures */
.display-6 {
  font-size: var(--oh-text-3xl);
  font-weight: var(--oh-weight-bold);
  letter-spacing: var(--oh-tracking-tight);
  font-variant-numeric: tabular-nums;
}

/* Figures, IDs, dates and money line up column-to-column. */
.tnum,
.table td,
.table th,
.pagination,
time { font-variant-numeric: tabular-nums; }

/* ================================================================
   2. Sidebar
   ================================================================ */

#sidebar {
  width: var(--oh-sidebar-w);
  min-width: var(--oh-sidebar-w);
  background: var(--oh-nav-bg) !important;   /* over Bootstrap .bg-dark */
  color: var(--oh-nav-ink) !important;
  padding: var(--oh-space-3) var(--oh-space-2) !important;
  gap: 1px;
}

#sidebar .brand,
#sidebar > a:first-child {
  color: #fff;
  font-weight: var(--oh-weight-semi);
  font-size: var(--oh-text-md);
  letter-spacing: var(--oh-tracking-tight);
  padding: var(--oh-space-1) var(--oh-space-2) var(--oh-space-3);
  display: flex;
  align-items: center;
  gap: 7px;
  margin-bottom: 0 !important;
}
#sidebar .brand h5 { font-size: var(--oh-text-md); margin: 0; }

/* Square accent mark next to the wordmark */
#sidebar .brand-mark {
  width: 16px;
  height: 16px;
  border-radius: var(--oh-radius-sm);
  background: var(--oh-accent);
  display: grid;
  place-items: center;
  font-size: 9px;
  font-weight: 700;
  color: #fff;
  flex: none;
}

/* Section headings that break the flat link list into groups */
#sidebar .nav-group {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  font-weight: var(--oh-weight-semi);
  color: var(--oh-nav-group);
  padding: var(--oh-space-3) var(--oh-space-2) var(--oh-space-1);
}
#sidebar .nav-group:first-of-type { padding-top: var(--oh-space-1); }

#sidebar .nav-link {
  display: flex;
  align-items: center;
  gap: var(--oh-space-2);
  padding: 5px var(--oh-space-2);
  font-size: var(--oh-text-sm);
  color: var(--oh-nav-ink) !important;
  border-radius: 5px;
}
#sidebar .nav-link i { font-size: var(--oh-text-md); width: 15px; opacity: 0.85; }
/* Logout is a submit button (see base.html). Anchors are already block-level
   here, but a button shrinks to its text, which would clip the hover band. */
#sidebar button.nav-link { width: 100%; }

#sidebar .nav-link:hover {
  background-color: var(--oh-nav-hover);
  color: #dfe2e8 !important;
}
#sidebar .nav-link.active {
  background-color: var(--oh-nav-active);
  color: var(--oh-nav-ink-on) !important;
  font-weight: var(--oh-weight-medium);
}

/* Pending-work count pinned to the right of a nav item */
#sidebar .nav-count {
  margin-left: auto;
  font-size: var(--oh-text-2xs);
  color: #7d8390;
  font-variant-numeric: tabular-nums;
}

#sidebar hr { border-color: var(--oh-nav-rule) !important; opacity: 1; margin: var(--oh-space-2) 0; }

/* Language toggle badges */
#sidebar .badge.bg-primary { background: var(--oh-accent) !important; }
#sidebar .badge.bg-secondary {
  background: var(--oh-nav-active) !important;
  color: var(--oh-nav-ink) !important;
}
#sidebar .badge.bg-secondary:hover { background: #31363f !important; color: #fff !important; }

/* Sidebar clock — the shop's own time, on every page. */
.sidebar-clock {
  padding: var(--oh-space-2) 0 var(--oh-space-1);
  line-height: 1.25;
}
.sidebar-clock-time {
  /* Tabular figures so the digits don't jitter the row a second at a time. */
  font-variant-numeric: tabular-nums;
  font-size: var(--oh-clock-size);
  font-weight: 600;
  color: var(--oh-clock-ink);
  letter-spacing: 0.01em;
}
.sidebar-clock-date,
.sidebar-clock-tz {
  font-variant-numeric: tabular-nums;
  font-size: var(--oh-text-2xs);
  color: var(--oh-clock-meta);
}

/* ================================================================
   3. Buttons
   ================================================================ */

.btn {
  --bs-btn-padding-y: 0;
  --bs-btn-padding-x: 0.625rem;
  --bs-btn-font-size: var(--oh-text-sm);
  --bs-btn-font-weight: var(--oh-weight-medium);
  --bs-btn-border-radius: var(--oh-radius);
  --bs-btn-focus-box-shadow: var(--oh-focus-ring);
  height: var(--oh-control-h);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  white-space: nowrap;
}

.btn-sm {
  --bs-btn-padding-x: 0.4375rem;
  --bs-btn-font-size: var(--oh-text-xs);
  height: var(--oh-control-h-sm);
}

.btn-primary {
  --bs-btn-bg: var(--oh-accent);
  --bs-btn-border-color: var(--oh-accent);
  --bs-btn-hover-bg: var(--oh-accent-hover);
  --bs-btn-hover-border-color: var(--oh-accent-hover);
  --bs-btn-active-bg: var(--oh-accent-hover);
  --bs-btn-active-border-color: var(--oh-accent-hover);
  --bs-btn-disabled-bg: var(--oh-accent);
  --bs-btn-disabled-border-color: var(--oh-accent);
  --bs-btn-font-weight: var(--oh-weight-medium);
}

/* The neutral workhorse button — used across every list and detail screen. */
.btn-outline-secondary {
  --bs-btn-color: var(--oh-ink);
  --bs-btn-border-color: var(--oh-border-firm);
  --bs-btn-bg: var(--oh-surface);
  --bs-btn-hover-color: var(--oh-ink);
  --bs-btn-hover-bg: var(--oh-surface-3);
  --bs-btn-hover-border-color: var(--oh-border-firm);
  --bs-btn-active-color: var(--oh-ink);
  --bs-btn-active-bg: var(--oh-surface-3);
  --bs-btn-active-border-color: var(--oh-border-firm);
  --bs-btn-disabled-color: var(--oh-ink-3);
  --bs-btn-disabled-border-color: var(--oh-border);
}

.btn-secondary {
  --bs-btn-bg: var(--oh-surface-3);
  --bs-btn-border-color: var(--oh-border-firm);
  --bs-btn-color: var(--oh-ink);
  --bs-btn-hover-bg: #e6e8eb;
  --bs-btn-hover-border-color: var(--oh-border-firm);
  --bs-btn-hover-color: var(--oh-ink);
}

.btn-outline-danger {
  --bs-btn-color: var(--oh-danger);
  --bs-btn-border-color: #edc9c9;
  --bs-btn-bg: var(--oh-surface);
  --bs-btn-hover-bg: var(--oh-danger-soft);
  --bs-btn-hover-color: var(--oh-danger);
  --bs-btn-hover-border-color: #e0aaaa;
  --bs-btn-active-bg: var(--oh-danger-soft);
  --bs-btn-active-color: var(--oh-danger);
  --bs-btn-active-border-color: #e0aaaa;
}

.btn-outline-primary {
  --bs-btn-color: var(--oh-accent);
  --bs-btn-border-color: #c3ccf5;
  --bs-btn-bg: var(--oh-surface);
  --bs-btn-hover-bg: var(--oh-accent-soft);
  --bs-btn-hover-color: var(--oh-accent-ink);
  --bs-btn-hover-border-color: #a8b5ef;
  --bs-btn-active-bg: var(--oh-accent-soft);
  --bs-btn-active-color: var(--oh-accent-ink);
  --bs-btn-active-border-color: #a8b5ef;
}

.btn-success {
  --bs-btn-bg: var(--oh-success);
  --bs-btn-border-color: var(--oh-success);
  --bs-btn-hover-bg: #17683a;
  --bs-btn-hover-border-color: #17683a;
  --bs-btn-active-bg: #17683a;
  --bs-btn-active-border-color: #17683a;
}

/* Icon-only buttons stay square. This has to be opt-in: CSS can't tell an
   icon-only button from `<i class="bi bi-plus-lg"></i>Create`, because
   :only-child counts element children and ignores the adjacent text node —
   so a :has() selector would collapse every labelled icon button too. */
.btn.btn-icon { width: var(--oh-control-h); padding: 0; }
.btn.btn-icon.btn-sm { width: var(--oh-control-h-sm); }

/* ================================================================
   4. Forms
   Bootstrap 5.3 has no component variables for these, so they are
   direct property overrides.
   ================================================================ */

.form-label {
  font-size: var(--oh-text-xs);
  font-weight: var(--oh-weight-medium);
  color: var(--oh-ink-2);
  margin-bottom: var(--oh-space-1);
}
/* Templates often write .form-label.small; keep it from shrinking further. */
.form-label.small { font-size: var(--oh-text-xs); }

.form-control,
.form-select {
  height: var(--oh-control-h);
  min-height: var(--oh-control-h);
  padding: 0 var(--oh-space-2);
  font-size: var(--oh-text-sm);
  line-height: normal;
  color: var(--oh-ink);
  background-color: var(--oh-surface);
  border: 1px solid var(--oh-border-firm);
  border-radius: var(--oh-radius);
  box-shadow: none;
}
.form-select { padding-right: var(--oh-space-6); background-position: right 0.5rem center; }

.form-control-sm,
.form-select-sm {
  height: var(--oh-control-h);
  min-height: var(--oh-control-h);
  font-size: var(--oh-text-sm);
  padding: 0 var(--oh-space-2);
  border-radius: var(--oh-radius);
}
.form-select-sm { padding-right: var(--oh-space-6); }

/* textarea and multi-line inputs need their height back */
textarea.form-control,
textarea.form-control-sm {
  height: auto;
  min-height: 0;
  padding: var(--oh-space-2);
  line-height: var(--oh-leading-body);
}
select[multiple].form-select,
select[size].form-select { height: auto; padding: var(--oh-space-1) var(--oh-space-2); }

.form-control:focus,
.form-select:focus {
  border-color: var(--oh-accent);
  box-shadow: var(--oh-focus-ring);
  outline: none;
}
.form-control::placeholder { color: var(--oh-ink-3); }
.form-control:disabled,
.form-select:disabled { background-color: var(--oh-surface-3); color: var(--oh-ink-3); }

.form-text {
  font-size: var(--oh-text-xs);
  color: var(--oh-ink-3);
  margin-top: var(--oh-space-1);
}

.form-check-input {
  border-color: var(--oh-border-firm);
  width: 0.9375rem;
  height: 0.9375rem;
  margin-top: 0.1875rem;
}
.form-check-input:checked {
  background-color: var(--oh-accent);
  border-color: var(--oh-accent);
}
.form-check-input:focus {
  border-color: var(--oh-accent);
  box-shadow: var(--oh-focus-ring);
}
.form-check-label { font-size: var(--oh-text-sm); color: var(--oh-ink); }

.input-group > .form-control,
.input-group > .form-select { height: var(--oh-control-h); }
.input-group-text {
  height: var(--oh-control-h);
  padding: 0 var(--oh-space-2);
  font-size: var(--oh-text-sm);
  color: var(--oh-ink-2);
  background-color: var(--oh-surface-2);
  border-color: var(--oh-border-firm);
  border-radius: var(--oh-radius);
}

/* ================================================================
   5. Cards
   ================================================================ */

.card {
  --bs-card-bg: var(--oh-surface);
  --bs-card-border-color: var(--oh-border);
  --bs-card-border-radius: var(--oh-radius);
  --bs-card-inner-border-radius: var(--oh-radius);
  --bs-card-spacer-y: var(--oh-space-3);
  --bs-card-spacer-x: var(--oh-space-4);
  --bs-card-cap-bg: var(--oh-surface-2);
  --bs-card-cap-padding-y: var(--oh-space-2);
  --bs-card-cap-padding-x: var(--oh-space-4);
  box-shadow: var(--oh-shadow-sm);
}

.card-header {
  font-size: var(--oh-text-sm);
  font-weight: var(--oh-weight-semi);
  color: var(--oh-ink);
  border-bottom-color: var(--oh-border);
}

.card-title {
  font-size: var(--oh-text-lg);
  font-weight: var(--oh-weight-semi);
  letter-spacing: var(--oh-tracking-tight);
}

/* Cards that are links (the dashboard tiles) should react to the pointer. */
a.card {
  text-decoration: none;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
a.card:hover {
  border-color: var(--oh-border-firm);
  box-shadow: var(--oh-shadow);
}

/* ================================================================
   6. Tables
   ================================================================ */

.table {
  --bs-table-bg: var(--oh-surface);
  --bs-table-color: var(--oh-ink);
  --bs-table-border-color: #f0f1f3;
  --bs-table-hover-bg: #fafbfc;
  --bs-table-hover-color: var(--oh-ink);
  --bs-table-striped-bg: var(--oh-surface-2);
  margin-bottom: 0;
}

.table > thead > tr > th {
  font-size: var(--oh-text-2xs);
  font-weight: var(--oh-weight-semi);
  color: var(--oh-ink-3);
  text-transform: uppercase;
  letter-spacing: var(--oh-tracking-wide);
  white-space: nowrap;
  padding: 0 var(--oh-space-3);
  height: 1.875rem;
  vertical-align: middle;
  border-bottom: 1px solid var(--oh-border);
  background: var(--oh-surface);
}

/* .table-light on a thead is the app's convention; the uppercase treatment
   above already separates the header, so drop the grey fill. */
.table > thead.table-light > tr > th {
  --bs-table-bg: var(--oh-surface);
  background: var(--oh-surface);
  color: var(--oh-ink-3);
}

.table > tbody > tr > td {
  padding: 0 var(--oh-space-3);
  height: var(--oh-row-h);
  vertical-align: middle;
  border-bottom-color: #f0f1f3;
}

.table-sm > tbody > tr > td { height: 1.75rem; }

/* Right-align anything numeric. Add .num to a cell to opt in. */
.table .num { text-align: right; font-variant-numeric: tabular-nums; }

.table > tbody > tr:last-child > td { border-bottom: 0; }

.table-warning { --bs-table-bg: var(--oh-warning-soft); --bs-table-color: var(--oh-warning); }

/* A table sitting flush inside .card-body.p-0 shouldn't double its corners. */
.card > .card-body.p-0 > .table { border-radius: inherit; overflow: hidden; }

/* ================================================================
   7. Badges and status pills
   ================================================================ */

.badge {
  --bs-badge-padding-y: 0.0625rem;
  --bs-badge-padding-x: 0.4375rem;
  --bs-badge-font-size: 0.71875rem;   /* 11.5px */
  --bs-badge-font-weight: var(--oh-weight-medium);
  --bs-badge-border-radius: var(--oh-radius-sm);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  vertical-align: middle;
}

/* Status pills carry a dot so state reads without relying on colour alone —
   which also keeps them legible for colour-blind users and in print. */
.badge[class*="status-"]::before,
.badge[class*="badge-status-"]::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

/* Status — .status-{value}, the pattern templates use */
.badge.status-created     { background-color: var(--oh-st-created-bg);    color: var(--oh-st-created-fg); }
.badge.status-confirmed   { background-color: var(--oh-st-confirmed-bg);  color: var(--oh-st-confirmed-fg); }
.badge.status-finished    { background-color: var(--oh-st-finished-bg);   color: var(--oh-st-finished-fg); }
.badge.status-draft       { background-color: var(--oh-st-draft-bg);      color: var(--oh-st-draft-fg); }
.badge.status-scheduled   { background-color: var(--oh-st-scheduled-bg);  color: var(--oh-st-scheduled-fg); }
.badge.status-delivering  { background-color: var(--oh-st-delivering-bg); color: var(--oh-st-delivering-fg); }
.badge.status-delivered   { background-color: var(--oh-st-delivered-bg);  color: var(--oh-st-delivered-fg); }
.badge.status-cancelled   { background-color: var(--oh-st-cancelled-bg);  color: var(--oh-st-cancelled-fg); }
.badge.status-paid        { background-color: var(--oh-st-paid-bg);       color: var(--oh-st-paid-fg); }

/* Legacy class names (kept for compatibility) */
.badge-status-created     { background-color: var(--oh-st-created-bg);    color: var(--oh-st-created-fg); }
.badge-status-confirmed   { background-color: var(--oh-st-confirmed-bg);  color: var(--oh-st-confirmed-fg); }
.badge-status-finished    { background-color: var(--oh-st-finished-bg);   color: var(--oh-st-finished-fg); }
.badge-status-draft       { background-color: var(--oh-st-draft-bg);      color: var(--oh-st-draft-fg); }
.badge-status-scheduled   { background-color: var(--oh-st-scheduled-bg);  color: var(--oh-st-scheduled-fg); }
.badge-status-delivering  { background-color: var(--oh-st-delivering-bg); color: var(--oh-st-delivering-fg); }
.badge-status-delivered   { background-color: var(--oh-st-delivered-bg);  color: var(--oh-st-delivered-fg); }
.badge-status-cancelled   { background-color: var(--oh-st-cancelled-bg);  color: var(--oh-st-cancelled-fg); }
.badge-status-paid        { background-color: var(--oh-st-paid-bg);       color: var(--oh-st-paid-fg); }

/* Product attribute / tag chips from _macros.html. Outside the sidebar
   these are metadata, not status, so they stay quiet. */
.card .badge.bg-info,
.table .badge.bg-info,
main .badge.bg-info {
  background-color: var(--oh-accent-soft) !important;
  color: var(--oh-accent-ink) !important;
}
.card .badge.bg-secondary,
.table .badge.bg-secondary,
main .badge.bg-secondary {
  background-color: var(--oh-surface-3) !important;
  color: var(--oh-ink-2) !important;
}

/* ================================================================
   8. Pagination
   ================================================================ */

.pagination {
  --bs-pagination-padding-x: 0.5rem;
  --bs-pagination-padding-y: 0.125rem;
  --bs-pagination-font-size: var(--oh-text-xs);
  --bs-pagination-color: var(--oh-ink-2);
  --bs-pagination-bg: var(--oh-surface);
  --bs-pagination-border-color: var(--oh-border-firm);
  --bs-pagination-border-radius: var(--oh-radius);
  --bs-pagination-hover-color: var(--oh-ink);
  --bs-pagination-hover-bg: var(--oh-surface-3);
  --bs-pagination-hover-border-color: var(--oh-border-firm);
  --bs-pagination-focus-bg: var(--oh-surface-3);
  --bs-pagination-focus-box-shadow: var(--oh-focus-ring);
  --bs-pagination-active-bg: var(--oh-accent);
  --bs-pagination-active-border-color: var(--oh-accent);
  --bs-pagination-disabled-color: var(--oh-ink-3);
  --bs-pagination-disabled-bg: var(--oh-surface);
  --bs-pagination-disabled-border-color: var(--oh-border);
  gap: var(--oh-space-1);
}
.pagination .page-link { border-radius: var(--oh-radius); }

/* ================================================================
   9. Modals
   ================================================================ */

.modal {
  --bs-modal-bg: var(--oh-surface);
  --bs-modal-border-color: transparent;
  --bs-modal-border-radius: var(--oh-radius-lg);
  --bs-modal-header-border-color: var(--oh-border);
  --bs-modal-footer-border-color: var(--oh-border);
  --bs-modal-header-padding: var(--oh-space-3) var(--oh-space-4);
  --bs-modal-padding: var(--oh-space-4);
  --bs-modal-box-shadow: var(--oh-shadow-lg);
}
.modal-title {
  font-size: var(--oh-text-lg);
  font-weight: var(--oh-weight-semi);
  letter-spacing: var(--oh-tracking-tight);
}
.modal-footer { padding: var(--oh-space-3) var(--oh-space-4); gap: var(--oh-space-2); }
.modal-footer > * { margin: 0; }
.modal-backdrop { --bs-backdrop-bg: #14161a; --bs-backdrop-opacity: 0.4; }

/* ================================================================
   10. Alerts and flashes
   ================================================================ */

.alert {
  --bs-alert-padding-y: var(--oh-space-2);
  --bs-alert-padding-x: var(--oh-space-3);
  --bs-alert-border-radius: var(--oh-radius);
  font-size: var(--oh-text-sm);
  border-width: 1px;
  display: flex;
  align-items: center;
  gap: var(--oh-space-2);
}
.alert-success { --bs-alert-color: var(--oh-success); --bs-alert-bg: var(--oh-success-soft); --bs-alert-border-color: #cfe8d9; }
.alert-danger  { --bs-alert-color: var(--oh-danger);  --bs-alert-bg: var(--oh-danger-soft);  --bs-alert-border-color: #f2d2d2; }
.alert-warning { --bs-alert-color: var(--oh-warning); --bs-alert-bg: var(--oh-warning-soft); --bs-alert-border-color: #f0dcbb; }
.alert-info    { --bs-alert-color: var(--oh-info);    --bs-alert-bg: var(--oh-info-soft);    --bs-alert-border-color: #cbe5ed; }

.alert-dismissible .btn-close { padding: var(--oh-space-3); }
.btn-close { --bs-btn-close-focus-shadow: var(--oh-focus-ring); }

/* ================================================================
   11. List screens
   The pieces a Bootstrap list page has no equivalent for: status tabs
   carrying counts, a filter toolbar, the bulk-action bar, and the age
   meter. Built to be reused by any list screen, not just orders.
   ================================================================ */

.list-head {
  display: flex;
  align-items: center;
  gap: var(--oh-space-2);
  padding-bottom: var(--oh-space-3);
}
.list-head h2 { margin: 0; }
.list-head .list-summary { font-size: var(--oh-text-xs); color: var(--oh-ink-3); }

.list-panel {
  background: var(--oh-surface);
  border: 1px solid var(--oh-border);
  border-radius: var(--oh-radius);
  box-shadow: var(--oh-shadow-sm);
  overflow: hidden;
}

/* ---- Status tabs ---- */
.list-tabs {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 0 var(--oh-space-3);
  border-bottom: 1px solid var(--oh-border);
  overflow-x: auto;
}
.list-tabs a {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: var(--oh-space-2) var(--oh-space-2);
  font-size: var(--oh-text-sm);
  font-weight: var(--oh-weight-medium);
  color: var(--oh-ink-2);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  white-space: nowrap;
}
.list-tabs a:hover { color: var(--oh-ink); }
.list-tabs a.active {
  color: var(--oh-ink);
  border-bottom-color: var(--oh-accent);
  font-weight: var(--oh-weight-semi);
}
.list-tabs .tab-count {
  font-size: var(--oh-text-2xs);
  font-variant-numeric: tabular-nums;
  background: var(--oh-surface-3);
  color: var(--oh-ink-3);
  border-radius: var(--oh-radius-pill);
  padding: 0 5px;
  min-width: 18px;
  text-align: center;
}
.list-tabs a.active .tab-count { background: var(--oh-accent-soft); color: var(--oh-accent); }

/* ---- Filter toolbar ---- */
.list-toolbar {
  display: flex;
  align-items: center;
  gap: var(--oh-space-2);
  padding: var(--oh-space-2) var(--oh-space-3);
  border-bottom: 1px solid var(--oh-border);
  flex-wrap: wrap;
}

.search-field { position: relative; flex: 1 1 240px; max-width: 320px; }
.search-field > i {
  position: absolute;
  left: var(--oh-space-2);
  top: 50%;
  transform: translateY(-50%);
  color: var(--oh-ink-3);
  font-size: var(--oh-text-xs);
  pointer-events: none;
}
.search-field .form-control { padding-left: 1.625rem; }

/* A filter that isn't set is dashed and quiet; a set one is solid and
   accented, so "what am I actually looking at" is answerable at a glance. */
.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 1.625rem;
  padding: 0 var(--oh-space-2);
  border: 1px dashed var(--oh-border-firm);
  border-radius: var(--oh-radius-pill);
  font-size: var(--oh-text-xs);
  color: var(--oh-ink-2);
  background: var(--oh-surface);
  text-decoration: none;
  white-space: nowrap;
}
.filter-chip:hover { border-color: var(--oh-ink-3); color: var(--oh-ink); }
.filter-chip.is-set {
  border-style: solid;
  border-color: var(--oh-accent);
  background: var(--oh-accent-soft);
  color: var(--oh-accent-ink);
  font-weight: var(--oh-weight-medium);
}
.filter-chip.is-set:hover { border-color: var(--oh-accent); }

/* Expanding date/customer filter panel, revealed by the "Filter" chip. */
.filter-panel {
  padding: var(--oh-space-3);
  border-bottom: 1px solid var(--oh-border);
  background: var(--oh-surface-2);
}

/* ---- Bulk action bar ---- */
.bulk-bar {
  display: flex;
  align-items: center;
  gap: var(--oh-space-2);
  padding: var(--oh-space-2) var(--oh-space-3);
  background: var(--oh-ink);
  color: #fff;
  font-size: var(--oh-text-sm);
}
.bulk-bar .bulk-count { font-weight: var(--oh-weight-semi); font-variant-numeric: tabular-nums; }
.bulk-bar .btn {
  --bs-btn-bg: #2a2e35;
  --bs-btn-border-color: #3a3f47;
  --bs-btn-color: #fff;
  --bs-btn-hover-bg: #343941;
  --bs-btn-hover-border-color: #4a505a;
  --bs-btn-hover-color: #fff;
}

.select-cell { width: 2rem; padding-left: var(--oh-space-3) !important; }
/* A batch column that is revealed rather than always present. On orders,
   selecting rows is what the screen is for and the column is always there; on
   products it is an occasional job, so the column stays out of the way until the
   batch button asks for it. Hidden in CSS rather than left unrendered so a
   filter swap can't land rows that disagree with the header about the column. */
[data-bulk-optional]:not(.is-selecting) .select-cell { display: none; }
.select-cell .form-check-input { margin: 0; vertical-align: middle; }
tbody tr.is-selected { background: var(--oh-accent-soft); }
tbody tr.is-selected:hover { background: #e6ebfc; }

/* ---- Age meter ----
   How long a row has sat. Fills and warms as it ages, so a stalled order is
   visible down the column without reading a single date. */
.age-bar {
  display: inline-block;
  width: 34px;
  height: 4px;
  border-radius: 2px;
  background: var(--oh-surface-3);
  vertical-align: middle;
  margin-left: 5px;
}
.age-bar > span { display: block; height: 100%; border-radius: 2px; background: var(--oh-ink-3); }
.age-bar.is-warn > span { background: #d99413; }
.age-bar.is-hot  > span { background: #d64545; }

/* ---- Footer ---- */
.list-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--oh-space-2);
  padding: var(--oh-space-2) var(--oh-space-3);
  border-top: 1px solid var(--oh-border);
  font-size: var(--oh-text-xs);
  color: var(--oh-ink-2);
}

/* ================================================================
   12. Dashboard
   Tiles are the scoreboard; the worklist below is what to do next.
   ================================================================ */

.stat-tile { text-decoration: none; }
.stat-tile .stat-label {
  font-size: var(--oh-text-xs);
  color: var(--oh-ink-2);
  font-weight: var(--oh-weight-medium);
}
.stat-tile .stat-figure {
  font-size: var(--oh-text-3xl);
  font-weight: var(--oh-weight-bold);
  letter-spacing: var(--oh-tracking-tight);
  font-variant-numeric: tabular-nums;
  color: var(--oh-ink);
  margin-top: 2px;
  line-height: 1.1;
}

.attention-head {
  display: flex;
  align-items: center;
  gap: var(--oh-space-2);
  margin: var(--oh-space-6) 0 var(--oh-space-3);
}
.attention-head h3 { margin: 0; font-size: var(--oh-text-lg); }
.attention-total {
  font-size: var(--oh-text-xs);
  font-variant-numeric: tabular-nums;
  color: var(--oh-ink-3);
  background: var(--oh-surface-3);
  border-radius: var(--oh-radius-pill);
  padding: 1px 7px;
}

.attention-clear {
  display: flex;
  align-items: center;
  gap: var(--oh-space-2);
  padding: var(--oh-space-4);
  font-size: var(--oh-text-sm);
  color: var(--oh-ink-2);
}
.attention-clear i { color: var(--oh-success); font-size: var(--oh-text-lg); }

.attention-title {
  display: flex;
  align-items: center;
  gap: var(--oh-space-2);
  padding: var(--oh-space-2) var(--oh-space-3);
  border-bottom: 1px solid var(--oh-border);
  font-size: var(--oh-text-sm);
  font-weight: var(--oh-weight-semi);
}
.attention-title > i { color: var(--oh-ink-3); }

/* The count is the severity signal, so it carries the tone — not the whole row,
   which would make a dashboard of four coloured blocks and no hierarchy. */
.attention-count {
  font-size: var(--oh-text-xs);
  font-variant-numeric: tabular-nums;
  border-radius: var(--oh-radius-pill);
  padding: 1px 7px;
  font-weight: var(--oh-weight-semi);
}
.attention-count.tone-danger  { background: var(--oh-danger-soft);  color: var(--oh-danger); }
.attention-count.tone-warning { background: var(--oh-warning-soft); color: var(--oh-warning); }
.attention-count.tone-info    { background: var(--oh-info-soft);    color: var(--oh-info); }

.attention-more {
  font-size: var(--oh-text-xs);
  color: var(--oh-ink-2);
  text-decoration: none;
  font-weight: var(--oh-weight-normal);
  display: inline-flex;
  align-items: center;
  gap: 2px;
  white-space: nowrap;
}
.attention-more:hover { color: var(--oh-accent); }

.attention-list { list-style: none; margin: 0; padding: 0; }
.attention-list > li + li { border-top: 1px solid #f0f1f3; }
.attention-list a {
  display: flex;
  align-items: center;
  gap: var(--oh-space-2);
  padding: 0 var(--oh-space-3);
  height: var(--oh-row-h);
  text-decoration: none;
  color: var(--oh-ink);
  font-size: var(--oh-text-sm);
}
.attention-list a:hover { background: #fafbfc; }
.attention-label {
  font-variant-numeric: tabular-nums;
  color: var(--oh-accent);
  white-space: nowrap;
}
.attention-customer {
  color: var(--oh-ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.attention-meta {
  font-size: var(--oh-text-xs);
  color: var(--oh-ink-3);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.attention-empty {
  padding: var(--oh-space-3);
  font-size: var(--oh-text-xs);
  color: var(--oh-ink-3);
}

/* ================================================================
   13. Misc
   ================================================================ */

.list-group-item {
  border-color: var(--oh-border);
  font-size: var(--oh-text-sm);
  padding: var(--oh-space-2) var(--oh-space-3);
}

/* Main content column */
#wrapper > .flex-grow-1 { background: var(--oh-canvas); min-width: 0; }

hr { border-color: var(--oh-border); opacity: 1; }

::selection { background: var(--oh-accent-soft); color: var(--oh-accent-ink); }

/* Keyboard focus that isn't a click — visible everywhere, quiet on mouse. */
:focus-visible { outline: 2px solid var(--oh-accent); outline-offset: 1px; }

/* HTMX loading indicator */
.htmx-indicator {
  display: none;
}
.htmx-request .htmx-indicator {
  display: inline-block;
}

/* Print — delivery slips (三聯單) go out on paper, so drop the app chrome
   and force status pills to ink that survives a mono printer. */
@media print {
  #sidebar, .btn, .pagination, .alert-dismissible .btn-close { display: none !important; }
  #wrapper > .flex-grow-1 { padding: 0 !important; background: #fff; }
  .card { box-shadow: none; border-color: #999; }
  .badge { border: 1px solid currentColor; }
}

/* ================================================================
   14. Schedule board
   ================================================================ */

/* Structure is grid, position is percentages. The lane is the positioning
   context; only bars get inline left/width. */
.sched {
  font-size: var(--oh-text-xs);
  /* A day is 24 columns. Below this the hours are too narrow to read at all, so
     the board scrolls sideways instead of compressing into a smear — and, more
     importantly, instead of the header and the lane disagreeing about how wide
     an hour is (see the minmax note below). */
  min-width: 44rem;
}

/* Paired with .sched's min-width: the board keeps a legible hour and the card
   scrolls, rather than the grid being squeezed past the point of meaning. */
.sched-scroll { overflow-x: auto; }

.sched-row {
  display: grid;
  grid-template-columns: var(--oh-sched-label-w) 1fr;
  align-items: stretch;
  border-bottom: 1px solid var(--oh-sched-gridline);
}
.sched-row:last-child { border-bottom: 0; }
.sched-row.is-inactive { opacity: 0.55; }

.sched-label {
  display: flex;
  align-items: center;
  padding: 0 var(--oh-space-2);
  border-right: 1px solid var(--oh-sched-gridline-major);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-height: var(--oh-sched-lane-h);
}
.sched-label a { text-decoration: none; color: inherit; }
.sched-label a:hover { text-decoration: underline; }

.sched-head { background: var(--oh-surface-2, #f6f7f9); font-weight: 600; }
.sched-head-ticks {
  display: grid;
  /* minmax(0, 1fr), NOT 1fr. A bare `1fr` is minmax(auto, 1fr), and `auto`
     floors each column at its content width — so once the board got narrow the
     hour labels stopped shrinking while the lane below, whose slot anchors are
     empty and have no content to floor on, kept going. The header then ran wider
     than the lane and the two stopped describing the same hours: at 700px the
     header was 415px against a 288px lane, so the now-line, correctly placed at
     55.9% of the lane, sat under the header's "09" at 13:24. Both grids must be
     free to shrink identically or they are not the same ruler. */
  grid-template-columns: repeat(var(--sched-ticks), minmax(0, 1fr));
}
.sched-tick {
  padding: 2px 0 2px 3px;
  border-left: 1px solid var(--oh-sched-gridline);
  font-variant-numeric: tabular-nums;
  color: var(--oh-ink-muted, #6b7180);
  /* Clip rather than push the column wider — see the minmax note above. */
  overflow: hidden;
}
.sched-tick:first-child { border-left: 0; }

.sched-lane { position: relative; min-height: var(--oh-sched-lane-h); }

/* The click targets ARE the gridlines — no extra DOM for the ruling. */
.sched-slots {
  position: absolute;
  inset: 0;
  display: grid;
  /* Same ruler as .sched-head-ticks — see the note there. */
  grid-template-columns: repeat(var(--sched-slots), minmax(0, 1fr));
  z-index: 0;
}
.sched-slot {
  border-left: 1px solid var(--oh-sched-gridline);
  text-decoration: none;
}
.sched-slot:first-child { border-left: 0; }
.sched-slot:hover { background: var(--oh-sched-slot-hover); }
.sched-slot.is-past { background: repeating-linear-gradient(
  45deg, transparent, transparent 4px, rgba(0,0,0,0.02) 4px, rgba(0,0,0,0.02) 8px); }

.sched-bar {
  position: absolute;
  top: 3px;
  bottom: 3px;
  z-index: 1;
  /* So a very short run stays clickable. Done in CSS, not by padding the
     percentage server-side, which would put the bar in the wrong place. */
  min-width: 1.5rem;
  display: flex;
  align-items: center;
  padding: 0 4px;
  border-radius: var(--oh-radius-sm, 3px);
  background: var(--oh-sched-bar-bg);
  color: var(--oh-sched-bar-fg);
  border: 1px solid currentColor;
  text-decoration: none;
  overflow: hidden;
  white-space: nowrap;
}
.sched-bar:hover { filter: brightness(0.96); color: var(--oh-sched-bar-fg); }
.sched-bar.is-past {
  background: var(--oh-sched-bar-past-bg);
  color: var(--oh-sched-bar-past-fg);
}
.sched-bar-label { overflow: hidden; text-overflow: ellipsis; }

/* A run that started before this window, or runs past its end, says so rather
   than looking like it begins or ends at the edge. */
.sched-bar.is-clipped-start { border-left-style: dashed; }
.sched-bar.is-clipped-start::before { content: "‹"; margin-right: 2px; }
.sched-bar.is-clipped-end { border-right-style: dashed; }
.sched-bar.is-clipped-end::after { content: "›"; margin-left: 2px; }

.sched-now {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  z-index: 2;
  background: var(--oh-sched-now);
  pointer-events: none;
}

/* The click layer means nothing on paper. */
@media print {
  .sched-slots { display: none; }
}


/* ================================================================
   15. Attribute ordering
   ================================================================ */

/* The grip is the drag target; the ▲▼ buttons beside it do the same job from a
   keyboard. Kept narrow and quiet — this column is a control, not data, and it
   sits to the left of the names it reorders. */
.attr-order {
  white-space: nowrap;
  color: var(--oh-ink-3);
}

.attr-grip {
  cursor: grab;
  color: var(--oh-ink-3);
  padding-right: 0.15rem;
}

.attr-grip:active {
  cursor: grabbing;
}

/* Sortable's own classes. The placeholder has to read as a gap the row will
   land in, not as a row of its own. */
.sortable-ghost {
  opacity: 0.4;
  background: var(--oh-surface-3);
}

.sortable-drag {
  opacity: 0.95;
}

/* Without a mouse there is nothing to grab, so the grip would be a decoration
   promising an interaction that isn't there. The buttons remain. */
@media (hover: none) {
  .attr-grip { display: none; }
}

/* ================================================================
   Draggable split
   ================================================================
   Two panes with a handle between them, for screens where the
   evidence and the form are read against each other and no single
   ratio suits every document. Opt in with [data-split]; the widths
   come from --oh-split, which the handle writes and main.js
   persists per key.

   Below the md breakpoint the panes stack and the handle is hidden:
   there is no horizontal room to trade, and a 3px drag target on a
   phone is a mis-tap waiting to happen. */

@media (min-width: 768px) {
  [data-split] {
    display: flex;
    align-items: flex-start;
  }

  /* Prophylactic: a flex item's default min-width is auto, so it
     will not shrink below its content's min-content width. Bootstrap
     tables wrap, so the panes here shrink without it — but a future
     pane holding something that cannot wrap (a wide <pre>, an image
     at intrinsic size) would silently stop responding to the drag. */
  [data-split] > [data-split-pane] {
    min-width: 0;
  }

  [data-split] > [data-split-pane="a"] {
    flex: 0 0 var(--oh-split, 33%);
  }

  [data-split] > [data-split-pane="b"] {
    flex: 1 1 auto;
  }

  [data-split-handle] {
    flex: 0 0 var(--oh-split-handle-w);
    align-self: stretch;
    cursor: col-resize;
    position: relative;
    border: 0;
    padding: 0;
    background: transparent;
    touch-action: none;   /* the pointer stream is ours, not the scroller's */
  }

  /* The grip is drawn rather than the whole strip, so the target is
     comfortably wide while the line stays thin. */
  [data-split-handle]::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 2px;
    height: var(--oh-split-grip-h);
    transform: translate(-50%, -50%);
    border-radius: 1px;
    background: var(--oh-border-firm);
  }

  [data-split-handle]:hover::after,
  [data-split-handle]:focus-visible::after {
    background: var(--oh-accent);
  }

  [data-split-handle]:focus-visible {
    outline: 2px solid var(--oh-accent);
    outline-offset: -2px;
  }

  /* While dragging, stop the pointer selecting text across the page. */
  [data-split].is-splitting {
    user-select: none;
  }
}

@media (max-width: 767.98px) {
  /* Stacked: no horizontal room to trade, and a thin drag target on a phone
     is a mis-tap waiting to happen. The panes are plain blocks here, so the
     gutter the handle provided has to come back as margin. */
  [data-split-handle] {
    display: none;
  }

  [data-split] > [data-split-pane="a"] {
    margin-bottom: 1rem;
  }
}
