/* ═══════════════════════════════════════════════════════════════════════════
   components.css, the primitive library.

   Small on purpose. Each entry exists because the same pattern appears in
   several places; nothing here is speculative. Every interactive primitive
   defines its full state space, default, hover, active, focus, disabled and,
   where it applies, loading and invalid.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ══ Button ═══════════════════════════════════════════════════════════════ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  height: var(--control-height);
  padding: 0 var(--sp-4);
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  text-decoration: none;
  background: transparent;
  transition:
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}

.btn:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.btn:disabled,
.btn[aria-disabled='true'] {
  cursor: not-allowed;
  opacity: 0.55;
  /* Suppress hover feedback rather than relying on opacity alone to say
     "not available", opacity is easy to miss. */
  pointer-events: none;
}

/* Primary, one per view, the action the page exists for. */
.btn-primary {
  background: var(--brand);
  color: var(--text-on-brand);
  border-color: var(--brand);
}
.btn-primary:hover  { background: var(--brand-hover); border-color: var(--brand-hover); color: var(--text-on-brand); }
.btn-primary:active { background: var(--brand-active); border-color: var(--brand-active); }

/* Secondary, the default for everything else. */
.btn-secondary {
  background: var(--surface);
  color: var(--text);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-raised);
}
.btn-secondary:hover  { background: var(--surface-hover); border-color: var(--border-control); color: var(--text); }
.btn-secondary:active { background: var(--surface-active); }

/* Ghost, for dense toolbars and row actions where a border would be noise. */
.btn-ghost {
  background: transparent;
  color: var(--text-secondary);
}
.btn-ghost:hover  { background: var(--surface-hover); color: var(--text); }
.btn-ghost:active { background: var(--surface-active); }

/* Danger, destructive confirmation only, never the trigger that opens it. */
.btn-danger {
  background: var(--danger);
  color: #fff;
  border-color: var(--danger);
}
.btn-danger:hover  { background: var(--danger-hover); border-color: var(--danger-hover); color: #fff; }

/* Danger-quiet, the trigger for a destructive action, before confirmation. */
.btn-danger-quiet {
  background: transparent;
  color: var(--danger);
  border-color: var(--danger-border);
}
.btn-danger-quiet:hover { background: var(--danger-bg); color: var(--danger); }

.btn-sm { height: 28px; padding: 0 var(--sp-3); font-size: var(--fs-small); }
.btn-lg { height: var(--control-height-lg); padding: 0 var(--sp-5); font-size: var(--fs-body-lg); }
.btn-block { width: 100%; }

/* Icon-only. Always needs an aria-label; .btn-icon without one is a bug. */
.btn-icon {
  width: var(--control-height);
  padding: 0;
  flex-shrink: 0;
}
.btn-sm.btn-icon { width: 28px; }

/* Loading. `aria-busy` drives it so the state is announced, not just drawn. */
.btn[aria-busy='true'] {
  cursor: progress;
  pointer-events: none;
}
.btn[aria-busy='true'] .btn-label { opacity: 0.6; }
.btn[aria-busy='true']::before {
  content: '';
  width: 0.85em;
  height: 0.85em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 640ms linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* ══ Icon button ══════════════════════════════════════════════════════════
   A square, label-less control for a single action: the theme switch, a close
   button, a row-level menu trigger. Always needs an aria-label.

   This lived in app.css, which the marketing pages do not load, so the theme
   toggle on the public site rendered as an unstyled browser button. It is a
   shared primitive, so it belongs here. */

.icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--control-height);
  height: var(--control-height);
  flex-shrink: 0;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text-secondary);
  cursor: pointer;
  box-shadow: var(--shadow-raised);
  transition:
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}
.icon-button:hover  { background: var(--surface-hover); border-color: var(--border-control); color: var(--text); }
.icon-button:active { background: var(--surface-active); }
.icon-button:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.icon-button:disabled { opacity: 0.55; cursor: not-allowed; }

/* Inside the app header the controls are 32px and sit on a flat bar, so the
   border and shadow would read as clutter next to the picker. */
.app-header .icon-button,
.site-header .icon-button {
  width: 32px;
  height: 32px;
  border-color: transparent;
  background: transparent;
  box-shadow: none;
}
.app-header .icon-button:hover,
.site-header .icon-button:hover {
  background: var(--surface-hover);
  border-color: var(--border-strong);
}

/* ══ Field ════════════════════════════════════════════════════════════════ */

.field { display: flex; flex-direction: column; gap: var(--sp-1); }

.field-label {
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  color: var(--text-secondary);
}

.field-label .required {
  color: var(--danger);
  margin-left: 2px;
}

.field-input,
.field-select,
.field-textarea {
  width: 100%;
  min-height: var(--control-height);
  padding: 0 var(--sp-3);
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-control);
  border-radius: var(--radius);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}

.field-textarea {
  padding: var(--sp-2) var(--sp-3);
  min-height: 5rem;
  resize: vertical;
  line-height: var(--lh-normal);
}

.field-select {
  appearance: none;
  padding-right: var(--sp-8);
  /* Chevron drawn in CSS so it inherits currentColor and needs no icon font.
     stroke uses %23, a literal # would terminate the data URI. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%23767D7B' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--sp-3) center;
  background-size: 11px;
  cursor: pointer;
}
:root[data-theme='dark'] .field-select,
:root:not([data-theme='light']) .field-select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%23B7BEBC' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E");
}

.field-input::placeholder,
.field-textarea::placeholder { color: var(--text-muted); }

.field-input:hover,
.field-select:hover,
.field-textarea:hover { border-color: var(--text-muted); }

.field-input:focus-visible,
.field-select:focus-visible,
.field-textarea:focus-visible {
  outline: none;
  border-color: var(--focus-ring);
  box-shadow: 0 0 0 3px var(--focus-halo);
}

.field-input:disabled,
.field-select:disabled,
.field-textarea:disabled {
  background: var(--surface-sunken);
  color: var(--text-muted);
  cursor: not-allowed;
  border-color: var(--border);
}

.field-input[readonly] { background: var(--surface-sunken); }

/* Invalid. Driven by aria-invalid, so the styling and the announcement can
   never disagree. */
.field-input[aria-invalid='true'],
.field-select[aria-invalid='true'],
.field-textarea[aria-invalid='true'] { border-color: var(--danger); }
.field-input[aria-invalid='true']:focus-visible,
.field-textarea[aria-invalid='true']:focus-visible {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px var(--danger-bg);
}

.field-hint {
  font-size: var(--fs-caption);
  color: var(--text-muted);
}

.field-error {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-1);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  color: var(--danger);
}
/* A leading mark so the error is not signalled by colour alone. */
.field-error::before { content: '!'; font-weight: var(--fw-bold); }

.field-mono { font-family: var(--font-mono); letter-spacing: 0.02em; }

/* Checkbox and radio: keep the native control (free a11y and forced-colours
   support), just size and tint it. */
.check {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  cursor: pointer;
  min-height: var(--control-height);
  align-items: center;
}
.check input[type='checkbox'],
.check input[type='radio'] {
  width: 1.05rem;
  height: 1.05rem;
  accent-color: var(--brand);
  cursor: pointer;
  flex-shrink: 0;
}
.check-label { font-size: var(--fs-body); }
.check-hint  { font-size: var(--fs-caption); color: var(--text-muted); }

/* Switch, for settings that take effect immediately. Anything needing a Save
   button uses a checkbox instead, so the affordance matches the behaviour. */
.switch { display: inline-flex; align-items: center; cursor: pointer; flex-shrink: 0; }
.switch input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}
.switch-track {
  display: block;
  position: relative;
  width: 38px;
  height: 22px;
  background: var(--border-control);
  border-radius: var(--radius-full);
  transition: background-color var(--dur-fast) var(--ease-out);
}
.switch-track::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 16px;
  height: 16px;
  background: #fff;
  border-radius: 50%;
  transition: transform var(--dur-fast) var(--ease-out);
}
.switch input:checked + .switch-track { background: var(--brand); }
.switch input:checked + .switch-track::after { transform: translateX(16px); }
.switch input:focus-visible + .switch-track {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
.switch input:disabled + .switch-track { opacity: 0.5; cursor: not-allowed; }

/* ══ Panel ════════════════════════════════════════════════════════════════ */

.panel {
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  padding: var(--sp-5);
}

.panel-flush { padding: 0; overflow: hidden; }

.panel-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin-bottom: var(--sp-4);
}
.panel-flush .panel-header {
  margin: 0;
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--border);
}

.panel-title { font-size: var(--fs-title-sm); font-weight: var(--fw-bold); }
.panel-description {
  font-size: var(--fs-small);
  color: var(--text-muted);
  margin-top: 2px;
  max-width: var(--prose-max);
}
.panel-actions {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: wrap;
  align-items: center;
}

.panel-footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-2);
  flex-wrap: wrap;
  margin-top: var(--sp-5);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--border);
}
.panel-flush .panel-footer {
  margin: 0;
  padding: var(--sp-4) var(--sp-5);
}

/* ══ Chip ═════════════════════════════════════════════════════════════════
   Status at a glance. Always carries a text label, never colour alone. */

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: 0.15rem var(--sp-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-full);
  background: var(--surface-sunken);
  color: var(--text-secondary);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
  line-height: 1.4;
  white-space: nowrap;
}

/* A dot repeats the status non-chromatically for anyone who cannot separate
   the hues, shape and position differ, not just colour. */
.chip::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}
.chip-plain::before { display: none; }

.chip-success { background: var(--success-bg); color: var(--success); border-color: var(--success-border); }
.chip-warning { background: var(--warning-bg); color: var(--warning); border-color: var(--warning-border); }
.chip-danger  { background: var(--danger-bg);  color: var(--danger);  border-color: var(--danger-border); }
.chip-brand   { background: var(--brand-subtle); color: var(--brand-subtle-fg); border-color: var(--brand-border); }

/* Removable chip (a selected role, a filter). The × is a real button. */
.chip-removable { padding-right: var(--sp-1); }
.chip-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  margin-left: 2px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: currentColor;
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
}
.chip-remove:hover { background: rgb(0 0 0 / 0.1); }
.chip-remove:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }

/* ══ Table ════════════════════════════════════════════════════════════════ */

/* Wide tables scroll inside their own box; the page body never scrolls
   sideways. tabindex="0" is set in markup so the region is keyboard-scrollable. */
.table-scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.table-scroll:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-body);
}

.table th {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: var(--sp-2) var(--sp-3);
  text-align: left;
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
  color: var(--text-secondary);
  background: var(--surface-sunken);
  border-bottom: 1px solid var(--border-strong);
  white-space: nowrap;
}

.table td {
  padding: var(--sp-3);
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

.table tbody tr:last-child td { border-bottom: none; }
.table tbody tr:hover td { background: var(--surface-hover); }

.table-numeric { text-align: right; font-variant-numeric: tabular-nums; }
.table-actions { text-align: right; white-space: nowrap; width: 1%; }

/* Sortable header: a real button so it is reachable and announced. */
.table-sort {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.table-sort:hover { color: var(--text); }
.table-sort[aria-sort='ascending']  .table-sort-marker::after { content: '↑'; }
.table-sort[aria-sort='descending'] .table-sort-marker::after { content: '↓'; }
.table-sort-marker { opacity: 0.5; font-size: 0.9em; }
.table-sort[aria-sort]:not([aria-sort='none']) .table-sort-marker { opacity: 1; }

/* ══ Empty / loading / error states ═══════════════════════════════════════
   One shape for all three, so a page never looks structurally different when
   it has no data than when it is broken. */

.state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--sp-2);
  padding: var(--sp-10) var(--sp-5);
}
.state-title { font-size: var(--fs-title-sm); font-weight: var(--fw-bold); }
.state-body {
  font-size: var(--fs-small);
  color: var(--text-muted);
  max-width: 44ch;
}
.state-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; justify-content: center; margin-top: var(--sp-2); }

.state-error .state-title { color: var(--danger); }

/* Skeletons animate only when motion is welcome; base.css clamps the duration
   otherwise, so this degrades to a flat block rather than a strobe. */
.skeleton {
  background: linear-gradient(
    90deg,
    var(--surface-sunken) 0%,
    var(--surface-hover) 50%,
    var(--surface-sunken) 100%
  );
  background-size: 200% 100%;
  animation: skeleton-sweep 1.3s ease-in-out infinite;
  border-radius: var(--radius);
}
@keyframes skeleton-sweep {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}
.skeleton-line { height: 0.85rem; margin-bottom: var(--sp-2); }
.skeleton-line:last-child { width: 60%; margin-bottom: 0; }

/* ══ Dialog ═══════════════════════════════════════════════════════════════
   Native <dialog>: focus trapping, Escape, inertness and the top layer all
   come from the platform rather than from hand-rolled JavaScript. */

dialog.dialog {
  width: min(34rem, calc(100vw - var(--sp-8)));
  max-height: min(85vh, 46rem);
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-overlay);
  overflow: hidden;
}
dialog.dialog::backdrop {
  background: rgb(10 12 11 / 0.45);
  backdrop-filter: blur(1px);
}
dialog.dialog[open] { display: flex; flex-direction: column; }

.dialog-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--border);
}
.dialog-title { font-size: var(--fs-title-sm); font-weight: var(--fw-bold); }
.dialog-description { font-size: var(--fs-small); color: var(--text-muted); margin-top: 2px; }
.dialog-body { padding: var(--sp-5); overflow-y: auto; flex: 1; }
.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-2);
  padding: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--border);
  background: var(--surface-sunken);
  flex-wrap: wrap;
}

.dialog-wide { width: min(52rem, calc(100vw - var(--sp-8))); }

/* On a phone the dialog becomes a bottom sheet: thumb-reachable, and the
   footer actions stretch so they are easy to hit. */
@media (max-width: 640px) {
  dialog.dialog {
    width: 100vw;
    max-width: 100vw;
    max-height: 92dvh;
    margin: auto auto 0;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    border-bottom: 0;
    animation: sheet-rise var(--dur) var(--ease-out);
  }
  .dialog-footer { padding-bottom: calc(var(--sp-4) + env(safe-area-inset-bottom, 0px)); }
  .dialog-footer .btn { flex: 1; }
}
@keyframes sheet-rise {
  from { transform: translateY(2rem); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

/* ══ Menu ═════════════════════════════════════════════════════════════════
   Anchored popover. Roving focus and Escape are handled by js/core/menu.js;
   this is only the appearance. */

.menu {
  position: absolute;
  z-index: var(--z-menu);
  min-width: 12rem;
  max-width: min(20rem, calc(100vw - var(--sp-4)));
  padding: var(--sp-1);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-overlay);
}

.menu-item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  width: 100%;
  min-height: var(--control-height);
  padding: var(--sp-2) var(--sp-3);
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  color: var(--text);
  font-size: var(--fs-body);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.menu-item:hover { background: var(--surface-hover); color: var(--text); }
.menu-item:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; }
.menu-item[aria-disabled='true'] { color: var(--text-muted); pointer-events: none; }
.menu-item-danger { color: var(--danger); }
.menu-item-danger:hover { background: var(--danger-bg); color: var(--danger); }

.menu-separator { height: 1px; background: var(--border); margin: var(--sp-1) 0; }
.menu-label {
  padding: var(--sp-2) var(--sp-3) var(--sp-1);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
  color: var(--text-muted);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}
.menu-item-meta { margin-left: auto; font-size: var(--fs-caption); color: var(--text-muted); }

/* ══ Toast ════════════════════════════════════════════════════════════════
   The region is a live region so messages are announced; individual toasts
   are plain children of it. */

.toast-region {
  position: fixed;
  bottom: var(--sp-4);
  right: var(--sp-4);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  width: min(24rem, calc(100vw - var(--sp-8)));
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-left: 3px solid var(--text-muted);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-overlay);
  pointer-events: auto;
  animation: toast-in var(--dur) var(--ease-out);
}
.toast-success { border-left-color: var(--success); }
.toast-error   { border-left-color: var(--danger); }
.toast-warning { border-left-color: var(--warning); }

.toast-content { flex: 1; min-width: 0; }
.toast-title { font-size: var(--fs-body); font-weight: var(--fw-semibold); }
.toast-body  { font-size: var(--fs-small); color: var(--text-muted); margin-top: 2px; }
.toast-action {
  align-self: flex-start;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-weight: var(--fw-semibold);
  color: var(--brand);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 0.18em;
}
.toast-dismiss {
  background: none;
  border: 0;
  padding: 2px;
  color: var(--text-muted);
  cursor: pointer;
  line-height: 1;
  font-size: 15px;
}
.toast-dismiss:hover { color: var(--text); }

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

.toast-leaving { animation: toast-out var(--dur-fast) var(--ease-out) forwards; }
@keyframes toast-out {
  to { transform: translateY(0.25rem); opacity: 0; }
}

@media (max-width: 640px) {
  .toast-region {
    left: var(--sp-3);
    right: var(--sp-3);
    bottom: calc(var(--sp-3) + env(safe-area-inset-bottom, 0px));
    width: auto;
  }
}

/* ══ Notice ═══════════════════════════════════════════════════════════════
   Inline, in-flow message. Used for plan limits, setup prompts and
   non-blocking warnings. */

.notice {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--surface-sunken);
  font-size: var(--fs-small);
}
.notice-title { font-weight: var(--fw-semibold); font-size: var(--fs-body); color: var(--text); }
.notice-body  { color: var(--text-secondary); margin-top: 2px; }
.notice-actions { margin-top: var(--sp-3); display: flex; gap: var(--sp-2); flex-wrap: wrap; }

.notice-info    { background: var(--brand-subtle);  border-color: var(--brand-border); }
.notice-warning { background: var(--warning-bg);    border-color: var(--warning-border); }
.notice-danger  { background: var(--danger-bg);     border-color: var(--danger-border); }
.notice-success { background: var(--success-bg);    border-color: var(--success-border); }

.notice-info .notice-title    { color: var(--brand-subtle-fg); }
.notice-warning .notice-title { color: var(--warning); }
.notice-danger .notice-title  { color: var(--danger); }
.notice-success .notice-title { color: var(--success); }

/* ══ Meter ════════════════════════════════════════════════════════════════
   Quota and limit usage. A native <progress> would be simpler but cannot be
   coloured by threshold across browsers. */

.meter {
  height: 6px;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-full);
  overflow: hidden;
}
.meter-fill {
  height: 100%;
  background: var(--brand);
  border-radius: inherit;
  transition: width var(--dur) var(--ease-out);
}
.meter-near  .meter-fill { background: var(--warning); }
.meter-full  .meter-fill { background: var(--danger); }

.meter-labels {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-2);
  font-size: var(--fs-caption);
  color: var(--text-muted);
  margin-top: var(--sp-1);
}

/* ══ Stat ═════════════════════════════════════════════════════════════════ */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--sp-3);
}

.stat {
  padding: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
}
.stat-label { font-size: var(--fs-caption); color: var(--text-muted); font-weight: var(--fw-medium); }
.stat-value {
  font-size: var(--fs-display-sm);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-tight);
  line-height: 1.1;
  margin-top: var(--sp-1);
  font-variant-numeric: tabular-nums;
}
.stat-meta { font-size: var(--fs-caption); color: var(--text-muted); margin-top: var(--sp-1); }

/* ══ Definition list ══════════════════════════════════════════════════════ */

.definitions { display: flex; flex-direction: column; }
.definition {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-body);
}
.definition:last-child { border-bottom: none; }
.definition dt { color: var(--text-secondary); flex-shrink: 0; }
.definition dd { font-weight: var(--fw-semibold); text-align: right; min-width: 0; overflow-wrap: anywhere; }

@media (max-width: 480px) {
  .definition { flex-direction: column; align-items: flex-start; gap: var(--sp-1); }
  .definition dd { text-align: left; }
}

/* ══ Toolbar ══════════════════════════════════════════════════════════════
   Search + filters + actions above a table or list. */

.toolbar {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  margin-bottom: var(--sp-4);
}
.toolbar-search { flex: 1 1 14rem; min-width: 0; position: relative; }
.toolbar-search .field-input { padding-left: var(--sp-8); }
.toolbar-search-icon {
  position: absolute;
  left: var(--sp-3);
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-muted);
  pointer-events: none;
}
.toolbar-spacer { flex: 1; }

/* ══ Segmented control ════════════════════════════════════════════════════
   Two to four mutually exclusive views. Rendered as radios so arrow keys and
   announcement come free. */

.segmented {
  display: inline-flex;
  padding: 2px;
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  gap: 2px;
}
.segmented-option { position: relative; display: flex; }
.segmented-option input {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
  margin: 0;
}
.segmented-option span {
  display: flex;
  align-items: center;
  padding: 0 var(--sp-3);
  height: calc(var(--control-height) - 6px);
  min-height: 28px;
  border-radius: var(--radius-sm);
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  color: var(--text-secondary);
  white-space: nowrap;
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.segmented-option input:checked + span { background: var(--surface); color: var(--text); box-shadow: var(--shadow-raised); }
.segmented-option input:focus-visible + span { outline: 2px solid var(--focus-ring); outline-offset: 1px; }

/* ══ Avatar ═══════════════════════════════════════════════════════════════ */

.avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  object-fit: cover;
  flex-shrink: 0;
}
.avatar-lg { width: 44px; height: 44px; }

/* Fallback shown while the image loads or if it 404s. */
.avatar-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  color: var(--text-muted);
  text-transform: uppercase;
}

/* ══ Card list ════════════════════════════════════════════════════════════
   The mobile presentation of a table: one card per row, labels inline. */

.card-list { display: flex; flex-direction: column; gap: var(--sp-2); }

.card-row {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
}
.card-row-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); }
.card-row-title { font-weight: var(--fw-semibold); min-width: 0; overflow-wrap: anywhere; }
.card-row-fields {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--sp-1) var(--sp-3);
  font-size: var(--fs-small);
}
.card-row-fields dt { color: var(--text-muted); }
.card-row-fields dd { min-width: 0; overflow-wrap: anywhere; }
.card-row-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; }

/* ══ Tabs ═════════════════════════════════════════════════════════════════
   For sub-navigation inside a page. Top-level navigation uses the app nav,
   not tabs. */

.tabs {
  display: flex;
  gap: var(--sp-1);
  border-bottom: 1px solid var(--border-strong);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  margin-bottom: var(--sp-5);
}
.tabs::-webkit-scrollbar { display: none; }

.tab {
  flex-shrink: 0;
  padding: var(--sp-2) var(--sp-3);
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  color: var(--text-muted);
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  cursor: pointer;
  white-space: nowrap;
  margin-bottom: -1px;
}
.tab:hover { color: var(--text); }
.tab[aria-selected='true'] { color: var(--brand); border-bottom-color: var(--brand); }
.tab:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; }
.tab-count {
  margin-left: var(--sp-1);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* ══ Keyboard shortcut hint ═══════════════════════════════════════════════ */

.shortcut-hint {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  color: var(--text-muted);
  font-size: var(--fs-caption);
}
/* Hide shortcut hints where there is no physical keyboard to press. */
@media (pointer: coarse) {
  .shortcut-hint { display: none; }
}
