@charset "UTF-8";
/* components.css — Shared pieces: buttons, forms, cards, tables, toolbar, badges, empty
   state, pagination, plus the phone layout and motion rules.
   Loaded in order by templates/partials/styles.html. */

/* ============================== 8. Buttons ================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  height: 42px;
  padding: 0 var(--s-5);
  font: inherit;
  font-size: var(--fs-md);
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  cursor: pointer;
  transition: background-color var(--t-fast), border-color var(--t-fast),
    color var(--t-fast), transform var(--t-fast), box-shadow var(--t-fast);
}
.btn ion-icon {
  font-size: 1.15rem;
}
.btn:active {
  transform: translateY(1px);
}

.btn--primary {
  background: var(--primary-600);
  color: var(--text-on-primary);
  box-shadow: 0 4px 12px rgba(81, 69, 205, 0.28);
}
.btn--primary:hover {
  background: var(--primary-500);
  color: #fff;
  box-shadow: 0 6px 18px rgba(81, 69, 205, 0.36);
}

.btn--ghost {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--text-2);
}
.btn--ghost:hover {
  background: var(--surface-3);
  color: var(--text);
  border-color: var(--text-3);
}

.btn--subtle {
  background: var(--primary-50);
  color: var(--primary-600);
}
.btn--subtle:hover {
  background: var(--primary-100);
  color: var(--primary-600);
}

.btn--danger {
  background: var(--danger);
  color: #fff;
}
.btn--danger:hover {
  background: #a32830;
  color: #fff;
}

.btn--sm {
  height: 34px;
  padding: 0 var(--s-3);
  font-size: var(--fs-sm);
}
.btn--icon {
  width: 42px;
  padding: 0;
}
.btn--block {
  width: 100%;
}
.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* =============================== 9. Forms ================================= */
/* Forms are kept compact: a 36px box, a small label just above it */
.field {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.field > label,
.label {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text);
}
.hint {
  font-size: var(--fs-sm);
  color: var(--text-3);
}

.input,
.select,
.textarea {
  width: 100%;
  min-height: 36px;
  padding: 6px var(--s-3);
  font: inherit;
  font-size: var(--fs-md);
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  outline: none;
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.input::placeholder,
.textarea::placeholder {
  color: var(--text-3);
}
.input:hover,
.select:hover,
.textarea:hover {
  border-color: var(--text-3);
}
.input:focus,
.select:focus,
.textarea:focus {
  border-color: var(--primary-500);
  box-shadow: var(--ring);
}
/* a textarea is as tall as its rows="" — every form gives 2 lines; it can
   still be dragged taller */
.textarea {
  min-height: 0;
  resize: vertical;
}
.select {
  appearance: none;
  padding-right: var(--s-7);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 17px) 50%, calc(100% - 11px) 50%;
  background-size: 6px 6px;
  background-repeat: no-repeat;
  cursor: pointer;
}

/* switch */
.switch {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  cursor: pointer;
}
.switch input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.switch__track {
  position: relative;
  flex: 0 0 auto;
  width: 44px;
  height: 25px;
  border-radius: var(--r-full);
  background: var(--border-strong);
  transition: background-color var(--t);
}
.switch__track::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 19px;
  height: 19px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-xs);
  transition: transform var(--t);
}
.switch input:checked + .switch__track {
  background: var(--accent-500);
}
.switch input:checked + .switch__track::after {
  transform: translateX(19px);
}
.switch input:focus-visible + .switch__track {
  box-shadow: var(--ring);
}

/* =========================== 10. Cards & panels =========================== */
.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.panel__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-5) var(--s-5) var(--s-4);
}
.panel__title {
  font-size: var(--fs-lg);
}
.panel__sub {
  font-size: var(--fs-sm);
  color: var(--text-3);
  margin-top: 2px;
}
.panel__body {
  padding: 0 var(--s-5) var(--s-5);
}
.panel__body--flush {
  padding: 0;
}
.panel__foot {
  padding: var(--s-4) var(--s-5);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
}

/* ============================ 11. Stat cards ============================== */
.stat {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  padding: var(--s-5);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  transition: transform var(--t), box-shadow var(--t), border-color var(--t);
}
.stat::after {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--stat-color, var(--primary-500));
  opacity: 0;
  transition: opacity var(--t);
}
.stat:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-color: var(--border-strong);
}
.stat:hover::after {
  opacity: 1;
}

.stat__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-3);
}
.stat__label {
  font-size: var(--fs-md);
  font-weight: 500;
  color: var(--text-2);
}
.stat__icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: var(--r-md);
  font-size: 1.4rem;
  color: var(--stat-color, var(--primary-600));
  background: var(--surface-3); /* fallback where color-mix() is unsupported */
  background: color-mix(in srgb, var(--stat-color, var(--primary-500)) 12%, transparent);
}
.stat__value {
  font-family: var(--font-display);
  font-size: var(--fs-3xl);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1;
  color: var(--text);
}
.stat__foot {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--fs-sm);
  color: var(--text-3);
}
.delta {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 3px 8px;
  border-radius: var(--r-full);
  font-size: var(--fs-xs);
  font-weight: 600;
}
.delta--up {
  color: var(--success);
  background: var(--success-bg);
}
.delta--down {
  color: var(--danger);
  background: var(--danger-bg);
}

/* ---- topic card: linked help/category tile ---- */
.topic-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-3);
  padding: var(--s-5);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  color: var(--text);
  transition: transform var(--t), box-shadow var(--t), border-color var(--t);
}
.topic-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-color: var(--primary-500);
  color: var(--text);
}
.topic-card h3 {
  font-size: var(--fs-lg);
}
.topic-card p {
  color: var(--text-2);
  font-size: var(--fs-md);
  flex: 1;
}
.topic-card__link {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--primary-600);
}
.topic-card:hover .topic-card__link ion-icon {
  transform: translateX(3px);
}
.topic-card__link ion-icon {
  transition: transform var(--t);
}

/* ---- person card: profile summary tile ---- */
.person-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--s-4);
  padding: var(--s-6) var(--s-5);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  transition: transform var(--t), box-shadow var(--t), border-color var(--t);
}
.person-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-color: var(--border-strong);
}
.person-card__name {
  font-size: var(--fs-lg);
}
.person-card__meta {
  font-size: var(--fs-sm);
  color: var(--text-3);
}
.person-card__stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s-2);
  width: 100%;
  padding: var(--s-4) 0;
  border-block: 1px solid var(--border);
}
.person-card__stats strong {
  display: block;
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  color: var(--text);
}
.person-card__stats small {
  font-size: var(--fs-xs);
  color: var(--text-3);
}
.person-card__actions {
  display: flex;
  gap: var(--s-2);
  width: 100%;
}

/* ============================== 12. Tables ================================ */
/* Every table lives in one of these. A wide table scrolls sideways *inside*
   this box; the page itself never gets a horizontal scrollbar. */
.table-wrap {
  width: 100%;
  max-width: 100%;
  /* Without this a wrap that is a flex or grid child takes its width from the
     table's content and refuses to shrink — which is exactly how a wide table
     pushes the whole page sideways instead of scrolling by itself. */
  min-width: 0;
  overflow-x: auto;
  /* the sideways swipe stops at the ends of the table rather than carrying on
     into the page behind it */
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}

/* ---- a long table scrolls inside itself ----
   A report can run to hundreds of rows. Capping the box gives the table its
   own vertical scrollbar, so the page heading, the filter bar and the column
   headers all stay put while the rows move. The page keeps its own scrollbar
   too, and the two do not fight: scrolling past the last row inside the table
   carries on scrolling the page, which is how the mouse wheel already
   behaves. */
.table-wrap--tall {
  max-height: min(70vh, 720px);
  overflow-y: auto;
}

/* thead is already sticky; pin the totals row to the bottom edge as well, so
   the figures stay readable no matter where you are in the rows. Both rows
   already carry an opaque background, so nothing shows through them. */
.table-wrap--tall .table tfoot td {
  position: sticky;
  bottom: 0;
  z-index: 2;
  /* border-collapse:collapse hands the borders to the table, and a sticky cell
     that has left its normal position loses them — so the divider above the
     totals is drawn as an inset shadow, which travels with the cell. */
  box-shadow: inset 0 1px 0 var(--border-strong);
}

/* the same reason, one level up: a panel holding a table must be allowed to
   be narrower than the table it holds */
.panel:has(> .table-wrap),
.card:has(> .table-wrap),
.panel:has(.table-wrap),
.card:has(.table-wrap) {
  min-width: 0;
}
/* Small by default, everywhere. These tables carry ten or eleven columns and
   several money figures, and at the old --fs-md an amount like 1,24,536.75 ran
   out of column and wrapped onto a second line, which pushed every row taller
   and broke the alignment down the page. One step down in type, and one step in
   on the cell padding, buys each column roughly 15% more room for the same
   width — the .num rule below then guarantees a figure never breaks at all. */
.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-sm);
}
.table thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: var(--s-2) var(--s-4);
  text-align: left;
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-3);
  background: var(--surface-2);
  border-block: 1px solid var(--border);
  white-space: nowrap;
}
.table tbody td {
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--border);
  color: var(--text);
  vertical-align: middle;
}
.table tbody tr {
  transition: background-color var(--t-fast);
}
.table tbody tr:hover {
  background: var(--surface-2);
}
.table tbody tr:last-child td {
  border-bottom: none;
}
/* A totals row. Without this it inherits nothing from thead or tbody and
   lands flush against the last row with no padding at all. */
.table tfoot td {
  padding: var(--s-3) var(--s-4);
  border-top: 1px solid var(--border-strong);
  background: var(--surface-2);
  color: var(--text);
  vertical-align: middle;
}
.table tfoot .num {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
/* ---- fixed column widths ---- */
/* Left to itself a table sizes each column to its widest cell, so an eight
   or ten column order table spills into a sideways scroll and the figures
   never line up down the page. These widths are relative weights: with
   table-layout:fixed the browser scales them to the panel, so the same set
   of classes serves tables with different numbers of columns. */
.table--fixed {
  table-layout: fixed;
}
.table--fixed th,
.table--fixed td {
  padding-left: var(--s-3);
  padding-right: var(--s-3);
  /* a long part number breaks rather than pushing its column wider */
  overflow-wrap: anywhere;
}
/* ...but never a figure. "anywhere" means exactly that, and it was breaking
   amounts after the comma — 1,24,536.75 arriving as two lines. Money and
   quantity columns opt out and keep their own single line. */
.table--fixed th.num,
.table--fixed td.num {
  overflow-wrap: normal;
}

/* Shorter rows: these tables are scanned down a column, and the default
   16px above and below put only a handful of orders on a screen. The row
   height is now set by its tallest control, not by padding. */
.table--fixed thead th,
.table--fixed tbody td,
.table--fixed tfoot td {
  padding-top: var(--s-2);
  padding-bottom: var(--s-2);
}
.table--fixed .btn--sm {
  height: 30px;
}
.table--fixed .cell-img {
  width: 34px;
  height: 34px;
}
/* the outer edges keep the panel's own gutter */
.table--fixed th:first-child,
.table--fixed td:first-child {
  padding-left: var(--s-5);
}
.table--fixed th:last-child,
.table--fixed td:last-child {
  padding-right: var(--s-5);
}

/* the row number — three digits at most, so it takes the least of any column */
.table--fixed col.w-no { width: 4%; }
.table--fixed col.w-img { width: 9%; }
/* These are relative weights, not real percentages — with table-layout:fixed
   the browser scales the set to the panel. Weight moved off the name columns
   and onto the money ones: a name wraps to a second line harmlessly, an amount
   must not, so the figures get the room and the names give it up. */
.table--fixed col.w-name { width: 15%; }
/* the dealer's name, which is what these tables are read by. It gets the width
   the Net and GST columns used to take. */
.table--fixed col.w-dealer { width: 22%; }
/* ORD-2026-0032 — 13 monospace characters, the widest fixed-size cell in
   either table, so it gets its own width rather than sharing w-code */
.table--fixed col.w-orderno { width: 16%; }
.table--fixed col.w-code { width: 11%; }
.table--fixed col.w-qty { width: 7%; }
.table--fixed col.w-money { width: 12%; }
.table--fixed col.w-pct { width: 7%; }
/* "19 Sep 2026" needs about 77px and was breaking after the month */
.table--fixed col.w-date { width: 12%; }
.table--fixed col.w-status { width: 8%; }
.table--fixed col.w-act { width: 5%; }
.table--fixed col.w-act--wide { width: 12%; }

/* the date is always "j M Y", so it can never be long enough to need a
   second line — keep it on one */
.table--fixed .cell-date {
  white-space: nowrap;
}

/* The order number reads as one token. Monospace at the table's own size
   needs about 109px for thirteen characters, which the column only clears
   on a wide window — so it is set a step smaller, and if the panel does get
   tight it breaks at its hyphens rather than mid-group the way the
   overflow-wrap rule above would. */
.table--fixed .cell-orderno {
  overflow-wrap: normal;
  word-break: keep-all;
}
.table--fixed .cell-orderno code {
  font-size: var(--fs-sm);
}

/* Icon-only actions, so all three keep to one line. Three 30px buttons plus
   two 4px gaps is 98px, inside the ~106px the column gives them. */
.table--fixed .row-actions {
  flex-wrap: nowrap;
  gap: var(--s-1);
}

/* Every figure column. nowrap is the real fix for an amount splitting over two
   lines: a number is one word to a person but the browser will happily break it
   after a comma or a space. tabular-nums makes each digit the same width, so
   the decimal points line up down the column. */
.table .num {
  text-align: right;
  font-weight: 600;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.table .center {
  text-align: center;
}
.table .actions {
  text-align: right;
  white-space: nowrap;
}
/* row actions stay quiet until you're on the row */
.table .actions .icon-btn {
  width: 32px;
  height: 32px;
  font-size: 1.05rem;
  opacity: 0;
  transition: opacity var(--t-fast), background-color var(--t-fast);
}
.table tbody tr:hover .actions .icon-btn,
.table .actions .icon-btn:focus-visible {
  opacity: 1;
}

/* name + avatar cell */
.cell-user {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-width: 0;
}
.cell-user strong {
  display: block;
  font-weight: 600;
  color: var(--text);
}
.cell-user small {
  color: var(--text-3);
  font-size: var(--fs-sm);
}

/* ============================== 13. Toolbar =============================== */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--border);
}
.toolbar .search {
  max-width: 300px;
  flex: 1 1 200px;
}
.toolbar .search input {
  height: 38px;
  background: var(--surface-3);
}
.toolbar__spacer {
  flex: 1 1 auto;
}

/* segmented filter */
.segment {
  display: inline-flex;
  padding: 3px;
  gap: 2px;
  background: var(--surface-3);
  border-radius: var(--r-md);
}
/* A label is a phrase, not a paragraph. Without this, a squeezed item breaks
   "Last 30 days" into three lines inside a box that is a fixed 32px tall, so
   the lines spill over the item above and below it. */
.segment a,
.segment button {
  white-space: nowrap;
}
.segment button {
  height: 32px;
  padding: 0 var(--s-4);
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-2);
  background: transparent;
  border: none;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background-color var(--t-fast), color var(--t-fast);
}
.segment button:hover {
  color: var(--text);
}
.segment button.is-active {
  background: var(--surface);
  color: var(--primary-600);
  box-shadow: var(--shadow-xs);
}

/* ========================= 14. Badges & status ============================ */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 11px;
  border-radius: var(--r-full);
  font-size: var(--fs-xs);
  font-weight: 600;
  white-space: nowrap;
}
.badge::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}
.badge--success {
  color: var(--success);
  background: var(--success-bg);
}
.badge--warning {
  color: var(--warning);
  background: var(--warning-bg);
}
.badge--danger {
  color: var(--danger);
  background: var(--danger-bg);
}
.badge--info {
  color: var(--info);
  background: var(--info-bg);
}
.badge--neutral {
  color: var(--text-2);
  background: var(--surface-3);
}
.badge--plain::before {
  display: none;
}

/* ============================== 15. Avatars =============================== */
.avatar {
  position: relative;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  flex: 0 0 auto;
  border-radius: 50%;
  overflow: hidden;
  background: var(--primary-100);
  color: var(--primary-600);
  font-size: var(--fs-md);
  font-weight: 700;
  font-family: var(--font-display);
  text-transform: uppercase;
}
.avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.avatar--sm {
  width: 32px;
  height: 32px;
  font-size: var(--fs-xs);
}
.avatar--lg {
  width: 64px;
  height: 64px;
  font-size: var(--fs-lg);
}
.avatar--xl {
  width: 96px;
  height: 96px;
  font-size: var(--fs-2xl);
}
/* presence dot */
.avatar[data-status]::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: 0;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  border: 2px solid var(--surface);
  background: var(--text-3);
}
.avatar[data-status="online"]::after {
  background: var(--accent-500);
}
.avatar[data-status="away"]::after {
  background: var(--warning);
}

.avatar-group {
  display: flex;
}
.avatar-group .avatar {
  border: 2px solid var(--surface);
  margin-left: -10px;
}
.avatar-group .avatar:first-child {
  margin-left: 0;
}

/* =============================== 16. Tabs ================================= */
.tabs {
  display: flex;
  gap: var(--s-1);
  overflow-x: auto;
  border-bottom: 1px solid var(--border);
  padding: 0 var(--s-5);
}
.tabs button {
  position: relative;
  padding: var(--s-4) var(--s-4);
  font: inherit;
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--text-2);
  background: none;
  border: none;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--t-fast);
}
.tabs button::after {
  content: "";
  position: absolute;
  left: var(--s-3);
  right: var(--s-3);
  bottom: -1px;
  height: 2px;
  border-radius: 2px 2px 0 0;
  background: var(--primary-600);
  transform: scaleX(0);
  transition: transform var(--t);
}
.tabs button:hover {
  color: var(--text);
}
.tabs button.is-active {
  color: var(--primary-600);
}
.tabs button.is-active::after {
  transform: scaleX(1);
}
.tab-panel[hidden] {
  display: none;
}

/* ============================= 17. Accordion ============================== */
.accordion details {
  border-bottom: 1px solid var(--border);
}
.accordion details:last-child {
  border-bottom: none;
}
.accordion summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-4) 0;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
  list-style: none;
}
.accordion summary::-webkit-details-marker {
  display: none;
}
.accordion summary::after {
  content: "+";
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--surface-3);
  color: var(--text-2);
  font-size: 1.1rem;
  transition: transform var(--t), background-color var(--t);
}
.accordion details[open] summary::after {
  content: "−";
  background: var(--primary-50);
  color: var(--primary-600);
}
.accordion summary:hover {
  color: var(--primary-600);
}
.accordion__body {
  padding: 0 0 var(--s-4);
  color: var(--text-2);
  font-size: var(--fs-md);
  max-width: 70ch;
}

/* =========================== 18. Empty state ============================== */
.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--s-3);
  padding: var(--s-8) var(--s-5);
}
/* display:flex above beats the browser's own [hidden] rule, so the
   "no matches" block a filter keeps hidden showed all the time */
.empty[hidden] {
  display: none;
}
.empty ion-icon {
  font-size: 2.4rem;
  color: var(--primary-500);
  background: var(--primary-50);
  padding: var(--s-4);
  border-radius: var(--r-full);
}
.empty h3 {
  font-size: var(--fs-lg);
}
.empty p {
  color: var(--text-2);
  max-width: 42ch;
  font-size: var(--fs-md);
}

/* ======================= 19. Progress & mini chart ======================== */
.progress {
  height: 8px;
  border-radius: var(--r-full);
  background: var(--surface-3);
  overflow: hidden;
}
.progress span {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--bar-color, var(--primary-500));
  transition: width var(--t-slow);
}

/* CSS-only bar chart — bars set their own height via --h */
.chart {
  display: flex;
  align-items: flex-end;
  gap: clamp(6px, 1.4vw, 16px);
  height: 220px;
  padding-top: var(--s-4);
}
.chart__bar {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  gap: var(--s-2);
  height: 100%;
  color: var(--text-3);
  font-size: var(--fs-xs);
}
.chart__bar i {
  width: 100%;
  height: var(--h, 40%);
  border-radius: var(--r-sm) var(--r-sm) 4px 4px;
  background: linear-gradient(180deg, var(--primary-400), var(--primary-600));
  opacity: 0.85;
  transition: opacity var(--t), transform var(--t);
}
.chart__bar:hover i {
  opacity: 1;
  transform: scaleY(1.03);
  transform-origin: bottom;
}
.chart__bar[data-peak] i {
  background: linear-gradient(180deg, var(--accent-400), var(--accent-600));
}
/* value tooltip on hover */
.chart__bar::before {
  content: attr(data-value);
  position: absolute;
  bottom: calc(var(--h, 40%) + 28px);
  padding: 4px 8px;
  border-radius: var(--r-sm);
  background: var(--text);
  color: var(--bg);
  font-size: var(--fs-xs);
  font-weight: 600;
  white-space: nowrap;
  opacity: 0;
  transition: opacity var(--t-fast);
  pointer-events: none;
}
.chart__bar:hover::before {
  opacity: 1;
}

/* legend + donut-ish breakdown list */
.breakdown {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}
.breakdown__row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--s-2) var(--s-3);
  font-size: var(--fs-md);
}
.breakdown__row .progress {
  grid-column: 1 / -1;
}

/* ============================ 20. Pagination ============================== */
.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
}
.pagination__pages {
  display: flex;
  gap: var(--s-1);
}
.pagination button {
  min-width: 36px;
  height: 36px;
  padding: 0 var(--s-2);
  font: inherit;
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--text-2);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background-color var(--t-fast), color var(--t-fast);
}
.pagination button:hover:not(:disabled) {
  background: var(--surface-3);
  color: var(--text);
}
.pagination button.is-active {
  background: var(--primary-600);
  color: #fff;
}
.pagination button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* =============================== 21. Inbox ================================ */
.inbox {
  display: grid;
  grid-template-columns: minmax(260px, 340px) minmax(0, 1fr);
  min-height: 560px;
}
.inbox__list {
  border-right: 1px solid var(--border);
  overflow-y: auto;
  max-height: 640px;
}
.thread {
  display: flex;
  gap: var(--s-3);
  width: 100%;
  padding: var(--s-4) var(--s-5);
  text-align: left;
  background: none;
  border: none;
  border-bottom: 1px solid var(--border);
  border-left: 3px solid transparent;
  font: inherit;
  cursor: pointer;
  transition: background-color var(--t-fast);
}
.thread:hover {
  background: var(--surface-2);
}
.thread.is-active {
  background: var(--primary-50);
  border-left-color: var(--primary-600);
}
.thread__body {
  min-width: 0;
  flex: 1;
}
.thread__top {
  display: flex;
  justify-content: space-between;
  gap: var(--s-2);
}
.thread__name {
  font-weight: 600;
  font-size: var(--fs-md);
  color: var(--text);
}
.thread__time {
  font-size: var(--fs-xs);
  color: var(--text-3);
  white-space: nowrap;
}
.thread__preview {
  font-size: var(--fs-sm);
  color: var(--text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.thread.is-unread .thread__name::after {
  content: "";
  display: inline-block;
  margin-left: 6px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--primary-500);
}

.inbox__view {
  display: flex;
  flex-direction: column;
}
.inbox__head {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--border);
}
.inbox__messages {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  padding: var(--s-5);
  overflow-y: auto;
  max-height: 420px;
}
.bubble {
  max-width: 68%;
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-lg);
  font-size: var(--fs-md);
  line-height: 1.5;
}
.bubble--in {
  align-self: flex-start;
  background: var(--surface-3);
  color: var(--text);
  border-bottom-left-radius: var(--s-1);
}
.bubble--out {
  align-self: flex-end;
  background: var(--primary-600);
  color: #fff;
  border-bottom-right-radius: var(--s-1);
}
.bubble time {
  display: block;
  margin-top: var(--s-1);
  font-size: var(--fs-xs);
  opacity: 0.7;
}
.inbox__composer {
  display: flex;
  gap: var(--s-3);
  padding: var(--s-4) var(--s-5);
  border-top: 1px solid var(--border);
}

/* ============================= 22. Responsive ============================= */

/* NOTE: no auto-collapse breakpoint here on purpose. Pinning .app to the rail
   width by media query narrowed the column without adding .is-collapsed, so
   the full-width labels stayed in an 80px column and the toggle could no
   longer widen it. Sidebar width is driven by .is-collapsed alone. */

/* tablet & phone: sidebar becomes an overlay drawer */
@media (max-width: 900px) {
  .app,
  .app.is-collapsed {
    grid-template-columns: minmax(0, 1fr);
  }
  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: var(--nav-w);
    transform: translateX(-100%);
    transition: transform var(--t-slow);
  }
  .app.is-drawer-open .sidebar {
    transform: translateX(0);
    box-shadow: var(--shadow-lg);
  }
  .app.is-drawer-open .app__backdrop {
    display: block;
  }
  /* the rail styles are scoped to min-width: 901px, so the drawer keeps its
     full labels here with no overrides needed */

  .inbox {
    grid-template-columns: minmax(0, 1fr);
  }
  .inbox__list {
    border-right: none;
    border-bottom: 1px solid var(--border);
    max-height: 260px;
  }
}

/* phone: search moves to its own row, tables become stacked cards */
@media (max-width: 640px) {
  .topbar {
    flex-wrap: wrap;
    padding-bottom: var(--s-3);
  }
  .search {
    order: 3;
    flex-basis: 100%;
    max-width: none;
  }
  .search kbd {
    display: none;
  }
  .page-head__actions .btn {
    flex: 1 1 auto;
  }

  /* A stacking table turns into cards down here, so its wrap has nothing left
     to scroll and the overflow box would only clip the row shadows.
     Scoped with :has() on purpose — this used to apply to every .table-wrap,
     which switched off sideways scrolling for the tables that do NOT stack
     (the mini tables on the dashboard, the catalogue, the specs) and left them
     dragging the whole page sideways instead. */
  .table-wrap:has(.table--stack) {
    overflow-x: visible;
  }

  /* Down here the rows have become cards and the screen is short. A box
     within a box to scroll is no help — let the page carry them. */
  .table-wrap--tall:has(.table--stack) {
    max-height: none;
    overflow-y: visible;
  }
  .table-wrap--tall:has(.table--stack) .table tfoot td {
    position: static;
  }

  /* A table that does not stack keeps its own scrollbar, and keeps a width
     worth reading: squeezing ten columns into 360px is not "responsive". */
  .table-wrap:not(:has(.table--stack)) > .table {
    min-width: 560px;
  }

  .table--stack thead {
    display: none;
  }
  .table--stack,
  .table--stack tbody,
  .table--stack tfoot,
  .table--stack tr,
  .table--stack td {
    display: block;
    width: 100%;
  }
  .table--stack tr {
    padding: var(--s-3) var(--s-5);
    border-bottom: 8px solid var(--bg);
  }
  .table--stack td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-4);
    padding: var(--s-2) 0;
    text-align: right;
    border-bottom: 1px dashed var(--border);
  }
  .table--stack td:last-child {
    border-bottom: none;
  }
  .table--stack td::before {
    content: attr(data-label);
    flex: 0 0 auto;
    font-size: var(--fs-xs);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-3);
    text-align: left;
  }
  /* the identity cell reads better as a normal row, unlabelled */
  .table--stack td:has(.cell-user) {
    text-align: left;
  }
  .table--stack td:has(.cell-user)::before {
    display: none;
  }
  .table--stack .actions .icon-btn {
    opacity: 1;
  }

  .bubble {
    max-width: 85%;
  }
}

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

/* gentle entrance for content panels — not for a modal that a page renders
   inside its content: one that loads already open (a form sent back with
   errors) would otherwise drift up and fade in with the page around it */
@media (prefers-reduced-motion: no-preference) {
  .content > *:not(.modal) {
    animation: rise 0.45s var(--ease) both;
  }
  .content > *:nth-child(2) {
    animation-delay: 0.05s;
  }
  .content > *:nth-child(3) {
    animation-delay: 0.1s;
  }
  .content > *:nth-child(4) {
    animation-delay: 0.15s;
  }
}
@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
