/* Components. Every entry here is listed in DESIGN.md -> Component catalogue, and every
   interactive one ships seven states: default, hover, focus-visible, active, disabled, loading,
   error. Collections add empty, overflow and long-text. Half a state matrix is not a component. */

/* ============================================================ BUTTON */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-4);
  height: 38px; padding-inline: var(--s-6);
  border-radius: var(--r-control); border: 1px solid transparent;
  font-size: var(--t-xs); font-weight: 500; white-space: nowrap;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.btn:hover { text-decoration: none; }
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; }
.btn:disabled:hover, .btn[aria-disabled="true"]:hover { background: revert; }

.btn-primary { background: var(--brand); color: var(--ink-on-brand); }
.btn-primary:hover:not(:disabled) { background: var(--brand-hover); }
.btn-primary:active:not(:disabled) { background: var(--brand-active); }

.btn-secondary { background: var(--surface); color: var(--ink); border-color: var(--line-strong); }
.btn-secondary:hover:not(:disabled) { background: var(--surface-2); border-color: var(--ink-3); }
.btn-secondary:active:not(:disabled) { background: var(--surface-3); }

.btn-ghost { color: var(--ink-2); }
.btn-ghost:hover:not(:disabled) { background: var(--surface-2); color: var(--ink); }
.btn-ghost:active:not(:disabled) { background: var(--surface-3); }

.btn-danger { background: var(--bad); color: var(--ink-on-brand); }
.btn-danger:hover:not(:disabled) { filter: brightness(0.94); }

.btn-sm { height: 32px; padding-inline: var(--s-5); }
.btn-lg { height: 46px; padding-inline: var(--s-8); font-size: var(--t-base); }
.btn-icon { width: 38px; padding-inline: 0; }
.btn-icon.btn-sm { width: 32px; }
.btn-block { width: 100%; }

/* Loading: the label stays so the button keeps its width, the icon slot spins. A button that
   changes width mid-click moves the pointer target out from under the finger. */
.btn[data-loading="true"] { pointer-events: none; }
.btn[data-loading="true"] .icon { animation: spin 700ms linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

@media (max-width: 760px) {
  .btn { height: 44px; }
  .btn-sm { height: 40px; }
  .btn-icon { width: 44px; }
}

/* ============================================================ FORM CONTROLS */

.field + .field { margin-top: var(--s-6); }
.label { display: block; font-size: var(--t-xs); font-weight: 500; color: var(--ink-2); margin-bottom: var(--s-3); }
.field-req { color: var(--bad); margin-inline-start: var(--s-1); }
.hint { font-size: var(--t-2xs); color: var(--ink-3); margin-bottom: var(--s-3); }
.error-text { font-size: var(--t-2xs); color: var(--bad); margin-top: var(--s-3); display: flex; gap: var(--s-3); align-items: center; }

.input, .select, .textarea {
  width: 100%; min-height: 38px; padding: var(--s-4) var(--s-5);
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line-strong); border-radius: var(--r-control);
  transition: border-color var(--fast) var(--ease);
}
.input::placeholder, .textarea::placeholder { color: var(--ink-3); }
.input:hover, .select:hover, .textarea:hover { border-color: var(--ink-3); }
.input:disabled, .select:disabled, .textarea:disabled { background: var(--surface-2); color: var(--ink-3); cursor: not-allowed; }
.textarea { min-height: 96px; resize: vertical; }
.select { appearance: none; padding-inline-end: var(--s-9); background-image: none; }
[data-invalid="true"] .input, [data-invalid="true"] .select, [data-invalid="true"] .textarea,
.input[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--bad); }

@media (max-width: 760px) { .input, .select, .textarea { min-height: 44px; } }

.check { display: flex; align-items: flex-start; gap: var(--s-4); font-size: var(--t-xs); cursor: pointer; }
.check input { flex: none; width: 18px; height: 18px; margin-top: 1px; accent-color: var(--brand); }
.check-card {
  display: flex; gap: var(--s-5); padding: var(--s-6);
  border: 1px solid var(--line); border-radius: var(--r-panel); background: var(--surface);
  cursor: pointer; transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.check-card:hover { border-color: var(--line-strong); }
.check-card[data-selected="true"] { border-color: var(--brand); background: var(--brand-soft); }

/* ============================================================ PANEL */

/* `display: block` is not cosmetic here: a panel is often an <a> wrapping the whole row, and an
   anchor defaults to inline, so its own box collapses to a couple of pixels while the block
   children paint outside it. Anything positioned against the panel (the Karabakh band) then
   lands in a zero-width box and disappears. */
.panel { display: block; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-panel); }
a.panel { color: inherit; }
a.panel:hover { text-decoration: none; border-color: var(--line-strong); }
.panel-head {
  display: flex; align-items: center; gap: var(--s-5);
  padding: var(--s-6) var(--s-7); border-bottom: 1px solid var(--line);
}
.panel-body { padding: var(--s-7); }
.panel-body-flush { padding: 0; }
.panel-foot { padding: var(--s-6) var(--s-7); border-top: 1px solid var(--line); background: var(--surface-2); }
.panel-quiet { background: var(--surface-2); }
.divider { height: 1px; background: var(--line); border: 0; margin: var(--s-7) 0; }

/* ============================================================ PILL, BADGE, CHIP */

.pill {
  display: inline-flex; align-items: center; gap: var(--s-3);
  padding: var(--s-2) var(--s-5); border-radius: var(--r-pill);
  font-size: var(--t-2xs); font-weight: 500; letter-spacing: 0.01em;
  background: var(--surface-2); color: var(--ink-2); border: 1px solid var(--line);
}
.pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
.pill-plain::before { display: none; }
.pill-ok   { background: var(--ok-soft);    color: var(--ok);    border-color: var(--ok-border); }
.pill-warn { background: var(--warn-soft);  color: var(--warn);  border-color: var(--warn-border); }
.pill-bad  { background: var(--bad-soft);   color: var(--bad);   border-color: var(--bad-border); }
.pill-info { background: var(--brand-soft); color: var(--brand); border-color: var(--brand-border); }

.badge-count {
  display: inline-grid; place-items: center; min-width: 18px; height: 18px; padding-inline: var(--s-2);
  border-radius: var(--r-pill); background: var(--brand); color: var(--ink-on-brand);
  font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums;
}

.chip {
  display: inline-flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-5); border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--surface);
  font-size: var(--t-xs); color: var(--ink-2);
}
.chip[aria-pressed="true"] { background: var(--brand-soft); color: var(--brand); border-color: var(--brand-border); }

.avatar {
  display: inline-grid; place-items: center; width: 28px; height: 28px; flex: none;
  border-radius: var(--r-pill); background: var(--brand-soft); color: var(--brand);
  font-size: var(--t-2xs); font-weight: 600;
}
.kbd {
  font-family: var(--mono); font-size: 11px; padding: 1px var(--s-3);
  border: 1px solid var(--line-strong); border-bottom-width: 2px; border-radius: var(--s-2); color: var(--ink-3);
}

/* ============================================================ SLA
   Colour carries emphasis, the figure carries meaning. A bar without its remaining-time text is
   a defect: a colour-blind user must get the same answer as everyone else. */

.sla { display: flex; align-items: center; gap: var(--s-4); }
.sla-track { flex: 1; min-width: 60px; height: 6px; border-radius: var(--r-pill); background: var(--surface-3); overflow: hidden; }
/* `display: block` for the same reason the panel needs it: the fill is a <span>, and width and
   height do not apply to an inline box, so the bar rendered as an empty grey track. The track
   itself escapes the bug only because it is a flex item of .sla and gets blockified for free. */
.sla-fill { display: block; height: 100%; border-radius: var(--r-pill); background: var(--ok); }
.sla[data-tone="warn"] .sla-fill { background: var(--warn); }
.sla[data-tone="bad"] .sla-fill { background: var(--bad); }
.sla-text { font-size: var(--t-2xs); font-variant-numeric: tabular-nums; color: var(--ink-2); white-space: nowrap; }
.sla[data-tone="bad"] .sla-text { color: var(--bad); font-weight: 500; }

/* ============================================================ TABLE */

.table-wrap { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-panel); overflow: hidden; }
.table-scroll { overflow-x: auto; }
.table { font-size: var(--t-xs); }
.table th {
  position: sticky; top: 0; z-index: 1;
  text-align: start; font-size: var(--t-2xs); font-weight: 500; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--ink-3); background: var(--surface-2);
  padding: var(--s-5) var(--s-6); border-bottom: 1px solid var(--line); white-space: nowrap;
}
/* A sortable header puts its label inside a <button>, and the UA stylesheet resets
   text-transform on form controls, so that one column rendered in sentence case while every other
   header was uppercase. The button keeps its hit area and its focus ring; it borrows the header's
   typography rather than defining its own. */
.table th .btn {
  font: inherit; text-transform: inherit; letter-spacing: inherit; color: inherit;
  height: auto; padding: 0; gap: var(--s-2);
}
.table th .btn:hover { background: none; color: var(--ink); }

.table td { padding: var(--s-5) var(--s-6); border-bottom: 1px solid var(--line); vertical-align: middle; }
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--surface-2); }
.table tbody tr[aria-selected="true"] { background: var(--brand-soft); }
.table .num, .table [data-num] { text-align: end; font-variant-numeric: tabular-nums; }

/* Under 760px a row becomes a card: the header is repeated as a label on each cell, so a dense
   queue stays readable on a phone without a horizontal scroll. */
@media (max-width: 760px) {
  .table-stack thead { display: none; }
  .table-stack tr { display: block; padding: var(--s-5); border-bottom: 1px solid var(--line); }
  .table-stack td { display: flex; justify-content: space-between; gap: var(--s-5); padding: var(--s-3) 0; border: 0; }
  .table-stack td::before { content: attr(data-label); color: var(--ink-3); font-size: var(--t-2xs); }
}

/* ============================================================ TABS */

.tabs { display: flex; gap: var(--s-2); border-bottom: 1px solid var(--line); overflow-x: auto; }
.tab {
  padding: var(--s-5) var(--s-5); font-size: var(--t-xs); font-weight: 500; color: var(--ink-2);
  border-bottom: 2px solid transparent; margin-bottom: -1px; white-space: nowrap;
}
.tab:hover { color: var(--ink); }
.tab[aria-selected="true"] { color: var(--brand); border-bottom-color: var(--brand); }

/* ============================================================ STEPS */

.steps { display: flex; align-items: center; gap: var(--s-4); }
.step { display: flex; align-items: center; gap: var(--s-4); }
.step-dot {
  display: grid; place-items: center; width: 26px; height: 26px; flex: none;
  border-radius: var(--r-pill); border: 1px solid var(--line-strong);
  font-size: var(--t-2xs); font-weight: 600; color: var(--ink-3); background: var(--surface);
}
.step-label { font-size: var(--t-xs); font-weight: 500; color: var(--ink-3); }
.step[data-state="current"] .step-dot { background: var(--brand); border-color: var(--brand); color: var(--ink-on-brand); }
.step[data-state="current"] .step-label { color: var(--ink); }
.step[data-state="done"] .step-dot { background: var(--brand-soft); border-color: var(--brand-border); color: var(--brand); }
.step[data-state="done"] .step-label { color: var(--ink-2); }
.step-bar { flex: 1; min-width: 16px; height: 1px; background: var(--line-strong); }

@media (max-width: 560px) {
  .step:not([data-state="current"]) .step-label { display: none; }
}

/* ============================================================ MODAL
   Backdrop click does not close. ESC does not close. The only exits are the X, the cancel button
   and a successful action: a stray click must never discard a half-filled application form. */

.modal-backdrop {
  position: fixed; inset: 0; z-index: var(--z-backdrop);
  background: oklch(0.24 0.02 262 / 0.45);
  display: grid; place-items: center; padding: var(--s-6);
}
.modal {
  position: relative; z-index: var(--z-modal);
  width: min(560px, 100%); max-height: calc(100vh - var(--s-12));
  display: flex; flex-direction: column;
  background: var(--surface); border-radius: var(--r-panel); box-shadow: var(--shadow-3);
}
.modal-wide { width: min(760px, 100%); }
.modal-head { display: flex; align-items: center; gap: var(--s-5); padding: var(--s-6) var(--s-7); border-bottom: 1px solid var(--line); }
.modal-body { padding: var(--s-7); overflow-y: auto; }
.modal-foot { display: flex; justify-content: flex-end; gap: var(--s-4); padding: var(--s-6) var(--s-7); border-top: 1px solid var(--line); }

/* ============================================================ TOAST */

.toasts {
  position: fixed; inset-block-end: var(--s-7); inset-inline-end: var(--s-7); z-index: var(--z-toast);
  display: flex; flex-direction: column; gap: var(--s-4); pointer-events: none;
}
.toast {
  display: flex; align-items: center; gap: var(--s-5); pointer-events: auto;
  padding: var(--s-5) var(--s-6); min-width: 260px; max-width: 380px;
  background: var(--surface); border: 1px solid var(--line); border-inline-start: 1px solid var(--line);
  border-radius: var(--r-panel); box-shadow: var(--shadow-3); font-size: var(--t-xs);
}
.toast[data-tone="ok"] .icon { color: var(--ok); }
.toast[data-tone="bad"] .icon { color: var(--bad); }
.toast[data-tone="warn"] .icon { color: var(--warn); }

@media (max-width: 560px) {
  .toasts { inset-inline: var(--s-5); inset-block-end: var(--s-5); }
  .toast { max-width: none; }
}

/* ============================================================ NOTICE */

.notice {
  display: flex; gap: var(--s-5); padding: var(--s-6);
  border: 1px solid var(--line); border-radius: var(--r-panel); background: var(--surface-2);
  font-size: var(--t-xs); color: var(--ink-2);
}
.notice .icon { flex: none; color: var(--ink-3); }
.notice-title { font-weight: 600; color: var(--ink); margin-bottom: var(--s-2); }
.notice-ok   { background: var(--ok-soft);   border-color: var(--ok-border); }
.notice-ok .icon { color: var(--ok); }
.notice-warn { background: var(--warn-soft); border-color: var(--warn-border); }
.notice-warn .icon { color: var(--warn); }
.notice-bad  { background: var(--bad-soft);  border-color: var(--bad-border); }
.notice-bad .icon { color: var(--bad); }

/* ============================================================ TIMELINE
   Every entry names an actor. A timeline row without a responsible party is exactly the anonymous
   process this product exists to replace. */

.timeline { position: relative; padding-inline-start: var(--s-8); }
.timeline::before {
  content: ""; position: absolute; inset-block: 6px 6px; inset-inline-start: 4px;
  /* --line-strong, not --line: the spine runs between coloured dots, and a 0.905-lightness
     hairline simply disappears next to them. It is structure, not decoration. */
  width: 1px; background: var(--line-strong);
}
.tl-item { position: relative; padding-bottom: var(--s-7); }
.tl-item:last-child { padding-bottom: 0; }
.tl-dot {
  position: absolute; inset-inline-start: calc(-1 * var(--s-8) + 1px); top: 5px;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--surface); border: 2px solid var(--line-strong);
}
.tl-item[data-state="done"] .tl-dot { background: var(--ok); border-color: var(--ok); }
.tl-item[data-state="current"] .tl-dot { background: var(--brand); border-color: var(--brand); }
.tl-item[data-state="blocked"] .tl-dot { background: var(--bad); border-color: var(--bad); }
.tl-title { font-size: var(--t-xs); font-weight: 600; }
.tl-meta { font-size: var(--t-2xs); color: var(--ink-3); margin-top: var(--s-2); }
.tl-body { font-size: var(--t-xs); color: var(--ink-2); margin-top: var(--s-3); }

/* ============================================================ KEY-VALUE */

.kv { display: grid; grid-template-columns: minmax(120px, 34%) 1fr; gap: var(--s-4) var(--s-6); font-size: var(--t-xs); }
.kv dt { color: var(--ink-3); }
.kv dd { color: var(--ink); }
@media (max-width: 560px) { .kv { grid-template-columns: 1fr; gap: var(--s-2); } .kv dd { margin-bottom: var(--s-4); } }

/* ============================================================ STAT */

.stat { padding: var(--s-7); }
.stat-label { font-size: var(--t-2xs); color: var(--ink-3); }
.stat-value { font-size: var(--t-2xl); font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: -0.02em; margin-top: var(--s-3); }
.stat-delta { font-size: var(--t-2xs); margin-top: var(--s-3); display: inline-flex; align-items: center; gap: var(--s-2); }
.stat-delta-up { color: var(--ok); }
.stat-delta-down { color: var(--bad); }

/* ============================================================ EMPTY + SKELETON */

.empty { text-align: center; padding: var(--s-11) var(--s-8); }
.empty-title { font-size: var(--t-md); font-weight: 600; }
.empty-text { font-size: var(--t-xs); color: var(--ink-2); margin: var(--s-3) auto 0; max-width: 46ch; text-wrap: pretty; }
.empty .btn { margin-top: var(--s-6); }

.skeleton {
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
  background-size: 400% 100%; border-radius: var(--r-control);
}
.sk-line { height: 12px; margin-bottom: var(--s-5); }
[data-motion="full"] .skeleton { animation: shimmer 1.4s ease-in-out infinite; }
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }

/* ============================================================ MENU */

.menu-wrap { position: relative; }
.menu {
  position: absolute; inset-inline-end: 0; top: calc(100% + var(--s-3)); z-index: var(--z-dropdown);
  min-width: 240px; padding: var(--s-3);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-panel); box-shadow: var(--shadow-2);
}
.menu-item {
  display: flex; align-items: center; gap: var(--s-4); width: 100%;
  padding: var(--s-4) var(--s-5); border-radius: var(--r-control);
  font-size: var(--t-xs); color: var(--ink-2); text-align: start;
}
.menu-item:hover { background: var(--surface-2); color: var(--ink); text-decoration: none; }
/* Selection needs a surface, not just a hue: in a list of threads a colour-only marker is lost
   among the other brand-coloured text on the row (the case number is already brand). */
.menu-item[aria-checked="true"] { color: var(--brand); background: var(--brand-soft); }
.menu-sep { height: 1px; background: var(--line); margin: var(--s-3) 0; }

/* ============================================================ LANG SWITCH
   Text codes only. Emoji flags render as bare letters on Windows, which produced "AZ AZ" in the
   first build, and a flag is the wrong signifier for a language anyway. */

.lang-switch { display: flex; gap: var(--s-1); }
.lang-switch button {
  padding: var(--s-3) var(--s-4); border-radius: var(--r-control);
  font-size: var(--t-2xs); font-weight: 500; color: var(--ink-3);
}
.lang-switch button:hover { background: var(--surface-2); color: var(--ink); }
.lang-switch button[aria-pressed="true"] { background: var(--brand-soft); color: var(--brand); }

/* ============================================================ COMMAND PALETTE */

.palette-trigger {
  display: flex; align-items: center; gap: var(--s-4);
  height: 34px; padding-inline: var(--s-5); min-width: 220px;
  border: 1px solid var(--line); border-radius: var(--r-control);
  background: var(--surface-2); color: var(--ink-3); font-size: var(--t-xs);
}
.palette-trigger:hover { border-color: var(--line-strong); color: var(--ink-2); }
.palette-trigger .kbd { margin-inline-start: auto; }

.palette-backdrop { position: fixed; inset: 0; z-index: var(--z-backdrop); background: oklch(0.24 0.02 262 / 0.45); padding-top: 12vh; display: grid; justify-items: center; align-items: start; }
.palette { width: min(620px, calc(100vw - var(--s-8))); background: var(--surface); border-radius: var(--r-panel); box-shadow: var(--shadow-3); overflow: hidden; }
.palette-search { display: flex; align-items: center; gap: var(--s-5); padding: var(--s-6); border-bottom: 1px solid var(--line); }
.palette-input { flex: 1; border: 0; background: none; outline: none; font-size: var(--t-md); }
.palette-list { max-height: 52vh; overflow-y: auto; padding: var(--s-3); }
.palette-item { display: flex; align-items: center; gap: var(--s-5); width: 100%; padding: var(--s-5); border-radius: var(--r-control); font-size: var(--t-xs); text-align: start; }
.palette-item[aria-selected="true"] { background: var(--brand-soft); color: var(--brand); }
.palette-label { flex: 1; min-width: 0; }
.palette-foot { display: flex; gap: var(--s-6); padding: var(--s-5) var(--s-6); border-top: 1px solid var(--line); font-size: var(--t-2xs); color: var(--ink-3); }

@media (max-width: 1180px) { .palette-trigger span { display: none; } .palette-trigger { min-width: 0; } .palette-trigger .kbd { display: none; } }
@media (max-width: 560px) { .palette-trigger { display: none; } }

/* ============================================================ ICON */

.icon { width: 18px; height: 18px; flex: none; stroke-width: 1.75; }
.icon-sm { width: 15px; height: 15px; }
.icon-lg { width: 22px; height: 22px; }

/* ============================================================ MEDIA
   Photography is either at full strength inside its frame, or absent. There is no faded, tinted
   or overlaid variant: a washed-out photo keeps neither its colour nor its subject, and ornament
   laid across one is the cage effect. These two classes are the only sanctioned frames. */

/* Catalogue tile. Fixed 16:9 so rows stay aligned whatever the source photo's composition is.
   120px matches the inset the Karabakh band expects, so the band starts at the content column. */
.thumb {
  position: relative; flex: none; width: 120px; aspect-ratio: 16 / 9;
  border-radius: var(--r-control); overflow: hidden; background: var(--surface-2);
}
.thumb img { width: 100%; height: 100%; object-fit: cover; }
.thumb-badge {
  position: absolute; inset-block-end: var(--s-3); inset-inline-start: var(--s-3);
  display: grid; place-items: center; width: 24px; height: 24px;
  border-radius: var(--r-control); background: var(--surface); color: var(--brand);
}

/* Detail banner: the arch aperture. The photograph fills it completely and nothing is drawn on
   top of it - the şəbəkə stands at the edge of the opening, never across the view. */
.media-frame {
  position: relative; width: 100%; aspect-ratio: 21 / 9;
  border-radius: var(--r-panel); overflow: hidden; background: var(--surface-2);
  border: 1px solid var(--line);
}
.media-frame img { width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 560px) { .media-frame { aspect-ratio: 16 / 9; } .thumb { width: 88px; } }

/* ============================================================ SPLIT
   Detail-plus-context: the working column carries the case, the narrower one carries who owns it
   and when it is due. Equal columns (.g-2) are wrong here - a summary panel that matches the width
   of the case body competes with it for the eye. Collapses to one column at 1000px, summary first,
   because on a phone "who has this and when is it due" is the question you open the screen with. */

.split { display: grid; gap: var(--s-8); grid-template-columns: minmax(0, 2fr) minmax(260px, 1fr); align-items: start; }
.split-even { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* List plus detail (message threads, document previews): the list is a fixed reading width, the
   detail takes the rest. An auto-fit grid gives the list the same width as the conversation,
   which is the wrong emphasis - the thread is the content, the list is the index. */
.split-list { grid-template-columns: minmax(0, 320px) minmax(0, 1fr); }
@media (max-width: 1000px) {
  .split, .split-even { grid-template-columns: minmax(0, 1fr); }
  .split > .split-aside { order: -1; }
}

/* ============================================================ ACTION BAR
   Bulk selection needs its actions reachable without scrolling back up, and on a phone the
   primary action of a long screen must sit under the thumb. Sticky rather than fixed, so it
   never covers the last row of the table it belongs to. */

.action-bar {
  position: sticky; inset-block-end: 0; z-index: var(--z-sticky);
  display: flex; align-items: center; gap: var(--s-5); flex-wrap: wrap;
  padding: var(--s-5) var(--s-6); margin-top: var(--s-6);
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--r-panel);
  box-shadow: var(--shadow-2);
}
.action-bar .push { margin-inline-start: auto; }
@media (max-width: 560px) {
  .action-bar { border-radius: var(--r-panel) var(--r-panel) 0 0; margin-inline: calc(-1 * var(--s-5)); }
  .action-bar .btn { flex: 1; }
}

/* ============================================================ OVERFLOW GUARDS
   A grid or flex item defaults to min-width:auto, which means a child that scrolls horizontally
   (the tab strip, a wide table) can still stretch its parent and push the whole page sideways.
   The page body must never scroll horizontally - only the region that owns the overflow may.
   These guards are the fix, applied where the scroll containers actually live. */

.page > *, .panel, .panel-body, .split > *, .grid > * { min-width: 0; }
.tabs, .table-scroll, .table-wrap { min-width: 0; max-width: 100%; }

/* Touch targets stay at 44px on a phone even for the compact variant: -sm is a density choice for
   a mouse, not a licence to shrink a finger target. */
@media (max-width: 760px) {
  .btn-sm { height: 44px; }
  .btn-icon.btn-sm { width: 44px; }
}

/* An icon placed directly in an empty state is a block-level SVG, and text-align cannot centre a
   block box: the upload dropzone rendered its glyph hard against the left edge while its label sat
   in the middle. Any direct icon child of .empty centres itself. */
.empty > .icon { margin-inline: auto; }
