/* ═══════════════════════════════════════════════════════════════
   ds-patterns.css — DSF FRAMEWORK FILE — shell v2.1
   ───────────────────────────────────────────────────────────────
   The canonical composition classes for this package: button,
   card, badge, table, sheet, form, stats, toast, empty state,
   alert, avatar, switch.

   For a stack outside React this file — not the shadcn provenance
   in components-registry.json — is the portable contract. Port
   these classes directly.

   DO NOT redefine any class from this file inside a page <style>
   block, and do not author a second version of a composition that
   already has a class here. Two definitions of the same class in
   one stylesheet is a silent visual bug — the last one declared
   wins, with no build error and no failing test.

   Contract: design-rules.md § Canonical Classes

   Tints mix in OKLAB, never OKLCH. A surface like --color-card is achromatic,
   and OKLCH still interpolates its hue as 0° — so every tint against white got
   dragged toward red. The amber warning tint was rendering at hue 6.6, a faint
   pink. OKLAB interpolates in rectangular coordinates, where achromatic simply
   means a=b=0, and the chromatic colour keeps its direction.
   ═══════════════════════════════════════════════════════════════ */

/* ── BUTTONS ──────────────────────────────────── */
.btn-primary {
  display: inline-flex; align-items: center; gap: var(--spacing-2);
  height: 36px; padding: 0 var(--spacing-4);
  background: var(--color-primary); color: var(--color-primary-foreground);
  border: none; border-radius: var(--radius-field);
  font-family: inherit; font-size: 0.875rem; font-weight: 500;
  cursor: pointer; transition: opacity var(--duration-fast);
}

.btn-primary:hover { opacity: 0.88; }
.btn-primary:focus-visible { outline: 2px solid var(--color-ring); outline-offset: 2px; }

.btn-outline {
  display: inline-flex; align-items: center; gap: var(--spacing-2);
  height: 36px; padding: 0 var(--spacing-4);
  background: transparent; color: var(--color-foreground);
  border: var(--border-width) solid var(--color-border); border-radius: var(--radius-field);
  font-family: inherit; font-size: 0.875rem; font-weight: 500;
  cursor: pointer; text-decoration: none;
  transition: background var(--duration-fast), border-color var(--duration-fast);
}

.btn-outline:hover { background: var(--color-muted); border-color: var(--color-foreground); }
.btn-outline:focus-visible { outline: 2px solid var(--color-ring); outline-offset: 2px; }

.btn-ghost {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--spacing-2); height: 36px; padding: 0 var(--spacing-3);
  background: transparent; color: var(--color-muted-foreground);
  border: none; border-radius: var(--radius-field);
  font-family: inherit; font-size: 0.875rem;
  cursor: pointer; transition: background var(--duration-fast), color var(--duration-fast);
}

.btn-ghost:hover { background: var(--color-accent); color: var(--color-foreground); }
.btn-ghost:focus-visible { outline: 2px solid var(--color-ring); outline-offset: 2px; }

/* ── CARDS ────────────────────────────────────── */
.card {
  background: var(--color-card); border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-box);
}

/* ── BADGES (status badge — the ONE canonical composition, see
   design-system/design-rules.md § Canonical Classes and design-system/patterns.json
   "status_badge" — do NOT invent another badge variant on a per-page
   basis: dot markers, uppercase, or text-prefix badges are forbidden) ── */
.badge {
  display: inline-flex; align-items: center; gap: var(--spacing-1);
  padding: 2px var(--spacing-2); border-radius: var(--radius-selector);
  font-size: 0.75rem; font-weight: 500;
  border: var(--border-width) solid transparent; white-space: nowrap;
}

.badge-primary     { color: var(--color-primary);     background: color-mix(in oklab, var(--color-primary)     15%, transparent); border-color: color-mix(in oklab, var(--color-primary)     30%, transparent); }
.badge-success     { color: var(--color-success);     background: color-mix(in oklab, var(--color-success)     15%, transparent); border-color: color-mix(in oklab, var(--color-success)     30%, transparent); }
.badge-warning     { color: var(--color-warning);     background: color-mix(in oklab, var(--color-warning)     15%, transparent); border-color: color-mix(in oklab, var(--color-warning)     30%, transparent); }
.badge-destructive { color: var(--color-destructive); background: color-mix(in oklab, var(--color-destructive) 15%, transparent); border-color: color-mix(in oklab, var(--color-destructive) 30%, transparent); }
.badge-muted       { color: var(--color-muted-foreground); background: var(--color-muted); border-color: var(--color-border); }
.badge-outline     { color: var(--color-foreground); background: transparent; border-color: var(--color-border); }

/* ── TABLE ────────────────────────────────────── */
.table-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--spacing-3); padding: var(--spacing-3) var(--spacing-5);
  border-bottom: var(--border-width) solid var(--color-border);
}

.table-toolbar-left { display: flex; align-items: center; gap: var(--spacing-3); flex: 1; }
.table-toolbar-right { display: flex; align-items: center; gap: var(--spacing-2); }

.table-search {
  display: flex; align-items: center; gap: var(--spacing-2);
  height: 32px; padding: 0 var(--spacing-3);
  background: var(--color-muted); border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-field); color: var(--color-muted-foreground);
  /* The general inline search field, not only a table toolbar one — so it grows
     with its container instead of pinning to one width. */
  flex: 1 1 240px; min-width: 200px; max-width: 420px;
  transition: border-color var(--duration-fast);
}

.table-search:focus-within { border-color: var(--color-ring); }

.table-search input {
  background: transparent; border: none; outline: none;
  font-family: inherit; font-size: 0.8125rem;
  color: var(--color-foreground); width: 100%;
}

.table-search input::placeholder { color: var(--color-muted-foreground); }

.filter-select {
  height: 32px; padding: 0 28px 0 var(--spacing-3);
  background: var(--color-muted); border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-field); color: var(--color-foreground);
  font-family: inherit; font-size: 0.8125rem;
  -webkit-appearance: none; appearance: none; cursor: pointer;
  background-image:
    linear-gradient(45deg,  transparent 0 calc(50% - 0.9px), var(--color-muted-foreground) calc(50% - 0.9px) calc(50% + 0.9px), transparent calc(50% + 0.9px) 100%),
    linear-gradient(135deg, transparent 0 calc(50% - 0.9px), var(--color-muted-foreground) calc(50% - 0.9px) calc(50% + 0.9px), transparent calc(50% + 0.9px) 100%);
  background-position: right 13px center, right 8px center;
  background-size: 6px 6px;
  background-repeat: no-repeat;
  transition: border-color var(--duration-fast);
}

.filter-select:focus { outline: none; border-color: var(--color-ring); }

.data-table { width: 100%; border-collapse: collapse; }

.data-table thead tr {
  background: var(--color-muted); border-bottom: var(--border-width) solid var(--color-border);
}

.data-table th {
  padding: 0 var(--spacing-4);
  height: 36px; text-align: left;
  font-size: 0.75rem; font-weight: 600;
  color: var(--color-muted-foreground); white-space: nowrap;
}

.data-table tbody tr {
  border-bottom: var(--border-width) solid var(--color-border);
  transition: background var(--duration-fast);
}

.data-table tbody tr:last-child { border-bottom: none; }
.data-table tbody tr:hover { background: color-mix(in oklab, var(--color-accent) 50%, transparent); }

.data-table td {
  padding: 0 var(--spacing-4); height: 48px;
  font-size: 0.875rem; color: var(--color-foreground);
  vertical-align: middle;
}

.mono { font-family: var(--font-family-mono); font-size: 0.75rem; }

/* .table-checkbox was renamed .form-checkbox in v2.0 — see the CHECKBOX section below. */

.table-action {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; background: transparent; border: none;
  border-radius: var(--radius-sm); color: var(--color-muted-foreground);
  cursor: pointer; transition: color var(--duration-fast), background var(--duration-fast);
}

.table-action:hover { color: var(--color-foreground); background: var(--color-muted); }
.table-action:focus-visible { outline: 2px solid var(--color-ring); outline-offset: 2px; }

.pagination {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--spacing-3) var(--spacing-5);
  border-top: var(--border-width) solid var(--color-border);
}

.pagination-info { font-size: 0.75rem; color: var(--color-muted-foreground); }
.pagination-btns { display: flex; align-items: center; gap: var(--spacing-1); }

.page-btn {
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; background: transparent;
  border: var(--border-width) solid var(--color-border); border-radius: var(--radius-sm);
  color: var(--color-muted-foreground); font-size: 0.75rem;
  cursor: pointer; font-family: inherit;
  transition: background var(--duration-fast), color var(--duration-fast);
}

.page-btn:hover:not(:disabled) { background: var(--color-muted); color: var(--color-foreground); }
.page-btn.active { background: var(--color-primary); color: var(--color-primary-foreground); border-color: var(--color-primary); font-weight: 600; }
.page-btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* ── SHEET ────────────────────────────────────── */
.sheet-overlay {
  position: fixed; inset: 0; z-index: 200;
  display: flex; justify-content: flex-end;
  pointer-events: none; visibility: hidden;
}

.sheet-overlay.open { pointer-events: all; visibility: visible; }

.sheet-backdrop {
  position: absolute; inset: 0;
  background: var(--color-overlay);
  opacity: 0; transition: opacity var(--duration-normal);
}

.sheet-overlay.open .sheet-backdrop { opacity: 1; }

.sheet {
  position: relative; z-index: 1; width: 520px; max-width: 100vw;
  height: 100vh; background: var(--color-card);
  border-left: var(--border-width) solid var(--color-border);
  display: flex; flex-direction: column;
  transform: translateX(100%);
  transition: transform var(--duration-normal) var(--ease-default);
  box-shadow: var(--shadow-md);
}

.sheet-overlay.open .sheet { transform: translateX(0); }

.sheet-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--spacing-4) var(--spacing-5);
  border-bottom: var(--border-width) solid var(--color-border); flex-shrink: 0;
}

.sheet-title-group { display: flex; flex-direction: column; gap: 2px; }

.sheet-title { font-size: 1rem; font-weight: 600; color: var(--color-foreground); letter-spacing: var(--tracking-title); }
.sheet-subtitle { font-size: 0.75rem; color: var(--color-muted-foreground); }

.sheet-close {
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; background: transparent; border: none;
  border-radius: var(--radius-sm); color: var(--color-muted-foreground);
  cursor: pointer; flex-shrink: 0;
  transition: color var(--duration-fast), background var(--duration-fast);
}

.sheet-close:hover { color: var(--color-foreground); background: var(--color-accent); }
.sheet-close:focus-visible { outline: 2px solid var(--color-ring); outline-offset: 2px; }

.sheet-body {
  flex: 1; overflow-y: auto; padding: var(--spacing-5);
  display: flex; flex-direction: column; gap: var(--spacing-5);
  scrollbar-width: thin; scrollbar-color: var(--color-border) transparent;
}

.sheet-body::-webkit-scrollbar { width: 4px; }
.sheet-body::-webkit-scrollbar-thumb { background: var(--color-border); border-radius: var(--radius-full); }

.sheet-footer {
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--spacing-3); padding: var(--spacing-4) var(--spacing-5);
  border-top: var(--border-width) solid var(--color-border); flex-shrink: 0;
}

/* ── FORM ELEMENTS ────────────────────────────── */
.form-group { display: flex; flex-direction: column; gap: var(--spacing-2); }

.form-label { font-size: 0.8125rem; font-weight: 500; color: var(--color-foreground); }
/* Only for a project whose DNA chose "asterisk" as the required indicator. A project
   that chose a label suffix leaves this unused — do not reach for it because it is
   here. design-rules.md § Forms states which one this project picked, and shipping
   both without saying so is how a page ends up marking required fields two ways. */
.form-label .req { color: var(--color-destructive); margin-left: 2px; }

.form-input, .form-select, .form-textarea {
  font-family: inherit; font-size: 0.875rem; color: var(--color-foreground);
  background: var(--color-background); border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-field); width: 100%;
  transition: border-color var(--duration-fast), box-shadow var(--duration-fast);
}

.form-input, .form-select { height: 36px; padding: 0 var(--spacing-3); }

.form-input:focus, .form-select:focus, .form-textarea:focus {
  outline: none; border-color: var(--color-ring);
  box-shadow: 0 0 0 2px color-mix(in oklab, var(--color-ring) 22%, transparent);
}

.form-input::placeholder, .form-textarea::placeholder { color: var(--color-muted-foreground); }

/* The chevron is drawn with two gradients rather than an SVG data URI: url()
   does not interpolate var(), so an encoded stroke could not follow the theme. */
.form-select {
  -webkit-appearance: none; appearance: none; cursor: pointer;
  background-image:
    linear-gradient(45deg,  transparent 0 calc(50% - 0.9px), var(--color-muted-foreground) calc(50% - 0.9px) calc(50% + 0.9px), transparent calc(50% + 0.9px) 100%),
    linear-gradient(135deg, transparent 0 calc(50% - 0.9px), var(--color-muted-foreground) calc(50% - 0.9px) calc(50% + 0.9px), transparent calc(50% + 0.9px) 100%);
  background-position: right 15px center, right 10px center;
  background-size: 6px 6px;
  background-repeat: no-repeat;
  padding-right: 32px;
}

.form-textarea { padding: var(--spacing-3); resize: vertical; min-height: 120px; line-height: 1.6; }

.form-hint { font-size: 0.75rem; color: var(--color-muted-foreground); }
.form-error { font-size: 0.75rem; color: var(--color-destructive); display: none; }
.form-group.has-error .form-input,
.form-group.has-error .form-select,
.form-group.has-error .form-textarea { border-color: var(--color-destructive); }
.form-group.has-error .form-error { display: block; }

.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--spacing-4); }

.form-section-title {
  font-size: 0.75rem; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--color-muted-foreground);
  padding-bottom: var(--spacing-3);
  border-bottom: var(--border-width) solid var(--color-border);
}

/* ── STATS ────────────────────────────────────── */
.stats-grid {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: var(--spacing-4); margin-bottom: var(--spacing-6);
}

.stat-card {
  background: var(--color-card); border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-box);
  padding: var(--spacing-4) var(--spacing-5);
  position: relative; overflow: hidden;
}

.stat-label {
  font-size: 0.75rem; font-weight: 500; color: var(--color-muted-foreground);
  letter-spacing: var(--tracking-caption);
  display: flex; align-items: center; gap: var(--spacing-2);
  margin-bottom: var(--spacing-3);
}

.stat-value {
  font-size: 1.5rem; font-weight: 600;
  letter-spacing: var(--tracking-heading); color: var(--color-foreground);
  font-family: var(--font-family-mono); margin-bottom: var(--spacing-2);
}

.stat-sub { font-size: 0.75rem; color: var(--color-muted-foreground); }

.stat-icon {
  position: absolute; top: var(--spacing-4); right: var(--spacing-4);
  color: var(--color-muted-foreground); opacity: 0.25;
}

/* ── TOAST ────────────────────────────────────── */
.toast {
  position: fixed; bottom: var(--spacing-6); right: var(--spacing-6);
  z-index: 300; display: flex; align-items: center; gap: var(--spacing-3);
  padding: var(--spacing-3) var(--spacing-4);
  background: var(--color-card); border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-box); box-shadow: var(--shadow-md);
  font-size: 0.875rem; color: var(--color-foreground);
  transform: translateY(calc(100% + var(--spacing-6)));
  transition: transform var(--duration-normal) var(--ease-default);
  max-width: 360px;
}

.toast.show { transform: translateY(0); }
.toast-icon { flex-shrink: 0; }

/* ── EMPTY STATE ──────────────────────────────── */
.empty-state {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--spacing-3); padding: var(--spacing-12) var(--spacing-6);
  text-align: center;
}

.empty-icon { color: var(--color-muted-foreground); opacity: 0.5; margin-bottom: var(--spacing-2); }
.empty-title { font-size: 1rem; font-weight: 600; color: var(--color-foreground); }
.empty-desc { font-size: 0.875rem; color: var(--color-muted-foreground); max-width: 320px; }

/* ── BUTTON VARIANTS ──────────────────────────── */
/* Each button carries exactly one class. A variant does not depend on a
   base class, so a variant copied on its own still renders correctly —
   the failure mode of a base+modifier convention is a button that comes
   out unstyled with no error. */

.btn-default {
  display: inline-flex; align-items: center; gap: var(--spacing-2);
  height: 36px; padding: 0 var(--spacing-4);
  background: var(--color-secondary); color: var(--color-secondary-foreground);
  border: none; border-radius: var(--radius-field);
  font-family: inherit; font-size: 0.875rem; font-weight: 500;
  cursor: pointer; text-decoration: none; transition: opacity var(--duration-fast);
}
.btn-default:hover { opacity: 0.88; }
.btn-default:focus-visible { outline: 2px solid var(--color-ring); outline-offset: 2px; }

.btn-destructive {
  display: inline-flex; align-items: center; gap: var(--spacing-2);
  height: 36px; padding: 0 var(--spacing-4);
  background: var(--color-destructive); color: var(--color-destructive-foreground);
  border: none; border-radius: var(--radius-field);
  font-family: inherit; font-size: 0.875rem; font-weight: 500;
  cursor: pointer; text-decoration: none; transition: opacity var(--duration-fast);
}
.btn-destructive:hover { opacity: 0.88; }
.btn-destructive:focus-visible { outline: 2px solid var(--color-ring); outline-offset: 2px; }

/* Size modifier — the one case where a second class is correct, because it
   composes with any variant instead of replacing it. */
.btn-sm { height: 30px; padding: 0 var(--spacing-3); font-size: 0.8125rem; }

/* ── TABLE WRAPPER ────────────────────────────── */
/* The card surface a .data-table sits on. Named in patterns.json, but before
   v2.0 it existed only inside dashboard.html's local <style>. */
.data-table-wrap {
  background: var(--color-card);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-box);
  overflow-x: auto;
}

/* ── CHECKBOX ─────────────────────────────────── */
/* Canonical name is .form-checkbox: the composition is used outside tables
   (a "remember me" on the login screen), so a table-scoped name misleads. */
.form-checkbox {
  width: 16px; height: 16px; border-radius: var(--radius-selector);
  border: calc(var(--border-width) * 1.5) solid var(--color-border); background: var(--color-background);
  appearance: none; cursor: pointer; display: flex; color: var(--color-foreground);
  align-items: center; justify-content: center; flex-shrink: 0;
  transition: border-color var(--duration-fast), background var(--duration-fast);
}
.form-checkbox:checked { background: var(--color-primary); border-color: var(--color-primary); }
.form-checkbox:focus-visible { outline: 2px solid var(--color-ring); outline-offset: 2px; }

/* ── SWITCH ───────────────────────────────────── */
.switch {
  position: relative; display: inline-flex; align-items: center;
  width: 36px; height: 20px; flex-shrink: 0;
  background: var(--color-muted); border: none; color: var(--color-foreground);
  border-radius: var(--radius-full); cursor: pointer; padding: 0;
  transition: background var(--duration-fast);
}
.switch[aria-checked="true"] { background: var(--color-primary); }
.switch:focus-visible { outline: 2px solid var(--color-ring); outline-offset: 2px; }

.switch-thumb {
  position: absolute; left: 2px;
  width: 16px; height: 16px; border-radius: var(--radius-full);
  background: var(--color-background);
  transition: transform var(--duration-fast) var(--ease-default);
}
.switch[aria-checked="true"] .switch-thumb { transform: translateX(16px); }
/* Prototype JS toggles a class rather than the attribute, so the canonical switch
   accepts both. aria-checked stays the accessible source of truth — .is-on only
   mirrors it visually. Keep them in sync when you wire this up for real. */
.switch.is-on { background: var(--color-primary); }
.switch.is-on .switch-thumb { transform: translateX(16px); }

/* ── SEARCH CLEAR ─────────────────────────────── */
/* Optional child of .table-search — which is the general inline search field,
   not only a table toolbar one. */
.search-clear {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border: none; background: none;
  color: var(--color-muted-foreground); cursor: pointer; padding: 0;
  border-radius: var(--radius-full); transition: color var(--duration-fast);
}
.search-clear:hover { color: var(--color-foreground); }

/* ── ALERT ────────────────────────────────────── */
/* A toast confirms an action that just happened and then disappears. An alert
   announces a condition that is still true and stays until someone resolves
   it. If the information would still be true after a reload, it is an alert.
   See patterns.json → feedback.alert_vs_toast. */
.alert {
  display: flex; align-items: flex-start; gap: var(--spacing-3);
  padding: var(--spacing-4) var(--spacing-5);
  border-radius: var(--radius-box);
  border: var(--border-width) solid var(--color-border);
  background: var(--color-card);
  margin-bottom: var(--spacing-5);
}

.alert-info {
  border-color: color-mix(in oklab, var(--color-primary) 40%, transparent);
  background:   color-mix(in oklab, var(--color-primary) 8%, var(--color-card));
}
.alert-warning {
  border-color: color-mix(in oklab, var(--color-warning) 40%, transparent);
  background:   color-mix(in oklab, var(--color-warning) 8%, var(--color-card));
}
.alert-destructive {
  border-color: color-mix(in oklab, var(--color-destructive) 40%, transparent);
  background:   color-mix(in oklab, var(--color-destructive) 8%, var(--color-card));
}
.alert-success {
  border-color: color-mix(in oklab, var(--color-success) 40%, transparent);
  background:   color-mix(in oklab, var(--color-success) 8%, var(--color-card));
}

.alert-icon { display: flex; flex-shrink: 0; margin-top: 1px; }
.alert-info .alert-icon        { color: var(--color-primary); }
.alert-warning .alert-icon     { color: var(--color-warning); }
.alert-destructive .alert-icon { color: var(--color-destructive); }
.alert-success .alert-icon     { color: var(--color-success); }

.alert-body { flex: 1; min-width: 0; }
.alert-title {
  font-size: 0.875rem; font-weight: 600;
  color: var(--color-foreground); margin: 0 0 var(--spacing-1);
}
.alert-text { font-size: 0.8125rem; color: var(--color-muted-foreground); margin: 0; }
.alert-actions { display: flex; align-items: center; gap: var(--spacing-2); flex-shrink: 0; }

/* ── AVATAR ───────────────────────────────────── */
/* Pick a colour by index — .avatar-c{n} — never by writing a colour into JS.
   An index class follows the theme; a colour array in JS does not. */
.avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: var(--radius-full);
  font-size: 0.75rem; font-weight: 600; flex-shrink: 0;
  color: var(--color-primary-foreground);
}
.avatar-c1 { background: var(--avatar-1); }
.avatar-c2 { background: var(--avatar-2); }
.avatar-c3 { background: var(--avatar-3); }
.avatar-c4 { background: var(--avatar-4); }
.avatar-c5 { background: var(--avatar-5); }
.avatar-c6 { background: var(--avatar-6); }
.avatar-c7 { background: var(--avatar-7); }
.avatar-c8 { background: var(--avatar-8); }

/* ── RESPONSIVE (patterns) ────────────────────── */
@media (max-width: 1024px) {
  .stats-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 768px) {
  .stats-grid { grid-template-columns: 1fr 1fr; }
  .sheet { width: 100vw; }
  .form-row { grid-template-columns: 1fr; }
  .table-toolbar { flex-wrap: wrap; }
  /* O lado esquerdo e flex: 1 com busca e filtro dentro. Sem quebrar linha e
     sem min-width: 0 ele nao encolhe abaixo do conteudo, e a pagina inteira
     ganha scroll horizontal na largura de telefone. */
  .table-toolbar-left { flex-wrap: wrap; min-width: 0; }
  .table-toolbar-right { flex-wrap: wrap; }
}

/* ── PERMISSION MATRIX ────────────────────────── */
/* Three cell states, not two: granted, not granted, and not declared by that
   module. Without the third the matrix implies a role could be given an action
   that does not exist — the reader cannot tell "denied" from "impossible". */
.perm-matrix {
  width: 100%; border-collapse: collapse;
  background: var(--color-card);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-box);
  overflow: hidden;
}
.perm-matrix th,
.perm-matrix td {
  padding: var(--spacing-2) var(--spacing-3);
  border-bottom: var(--border-width) solid var(--color-border);
  font-size: 0.8125rem; text-align: center;
}
.perm-matrix th:first-child,
.perm-matrix td:first-child { text-align: left; font-weight: 500; }
.perm-matrix thead th {
  background: var(--color-muted);
  font-weight: 600; color: var(--color-muted-foreground);
  font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em;
}
.perm-matrix tbody tr:last-child td { border-bottom: none; }

.perm-cell {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: var(--radius-selector);
  border: calc(var(--border-width) * 1.5) solid var(--color-border); background: transparent;
  color: var(--color-foreground); cursor: pointer; padding: 0;
  transition: background var(--duration-fast), border-color var(--duration-fast);
}
/* granted */
.perm-cell.granted {
  background: var(--color-primary); border-color: var(--color-primary);
  color: var(--color-primary-foreground);
}
/* not granted — the module offers it, this role does not have it */
.perm-cell.denied { background: transparent; border-color: var(--color-border); }
/* not declared — the module does not offer this action at all. Not interactive. */
.perm-cell.unavailable {
  border-style: dashed; border-color: var(--color-muted);
  background: var(--color-muted); cursor: not-allowed; position: relative;
}
.perm-cell.unavailable::after {
  content: ''; position: absolute; left: 2px; right: 2px; top: 50%;
  height: 1px; background: var(--color-muted-foreground); opacity: 0.5;
}

/* ── ORDERED LIST (order is the meaning) ──────── */
/* The ordinal is rendered, never merely implied by position: someone referring
   to "level 2" has to see which one that is, and position alone conveys no
   ordering to a screen reader. */
.ordered-list { list-style: none; margin: 0; padding: 0; }

.ordered-item {
  display: flex; align-items: flex-start; gap: var(--spacing-3);
  padding: var(--spacing-3) var(--spacing-4);
  border: var(--border-width) solid var(--color-border);
  background: var(--color-card);
  border-radius: var(--radius-box);
  margin-bottom: var(--spacing-2);
}
.ordered-rank {
  display: flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; flex-shrink: 0;
  border-radius: var(--radius-full);
  background: var(--color-muted); color: var(--color-foreground);
  font-size: 0.75rem; font-weight: 600; font-family: var(--font-family-mono);
}
.ordered-body { flex: 1; min-width: 0; }
.ordered-title { font-size: 0.875rem; font-weight: 500; color: var(--color-foreground); margin: 0; }
.ordered-meta  { font-size: 0.75rem; color: var(--color-muted-foreground); margin: 2px 0 0; }
/* An item that cannot resolve — inactive participant, empty role. Say it here,
   in the row, not in a tooltip. */
.ordered-item.unresolvable {
  border-color: color-mix(in oklab, var(--color-warning) 40%, transparent);
  background:   color-mix(in oklab, var(--color-warning) 8%, var(--color-card));
}
.ordered-controls { display: flex; flex-direction: column; gap: 2px; flex-shrink: 0; }

/* ── MODULE TOGGLE ROW ────────────────────────── */
/* The consequence of switching OFF is stated in the row, beside the switch —
   not in a dialog that appears after the decision has been made. */
.module-row {
  display: flex; align-items: flex-start; gap: var(--spacing-3);
  padding: var(--spacing-4);
  border-bottom: var(--border-width) solid var(--color-border);
}
.module-row:last-child { border-bottom: none; }
.module-row-icon {
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex-shrink: 0;
  border-radius: var(--radius-md);
  background: var(--color-muted); color: var(--color-muted-foreground);
}
.module-row-body { flex: 1; min-width: 0; }
.module-row-title { font-size: 0.875rem; font-weight: 500; color: var(--color-foreground); margin: 0; }
.module-row-desc  { font-size: 0.75rem; color: var(--color-muted-foreground); margin: 2px 0 0; }
.module-row-consequence {
  font-size: 0.75rem; color: var(--color-warning);
  margin: var(--spacing-2) 0 0; display: flex; align-items: center; gap: var(--spacing-1);
}

/* ── CARD ANATOMY ─────────────────────────────── */
/* .card existed; its parts did not, so four pages each grew their own version.
   Consolidated from the most complete of them. */
.card-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--spacing-4) var(--spacing-5);
  border-bottom: var(--border-width) solid var(--color-border);
}
.card-title {
  font-size: 0.9375rem; font-weight: 600;
  letter-spacing: var(--tracking-title); color: var(--color-foreground);
}
.card-desc { font-size: 0.8125rem; color: var(--color-muted-foreground); margin-top: 2px; }
.card-body { padding: var(--spacing-4) var(--spacing-5); }
/* Modifier, for a body whose children stack with even spacing. */
.card-body-stack { display: flex; flex-direction: column; gap: var(--spacing-4); }
.card-footer {
  padding: var(--spacing-4) var(--spacing-5);
  border-top: var(--border-width) solid var(--color-border);
  display: flex; justify-content: flex-end; gap: var(--spacing-2);
}


/* ── FILE UPLOAD LIST ───────────────────────── */
.file-list { display: flex; flex-direction: column; gap: var(--spacing-2); margin-top: var(--spacing-3); }
.file-item {
      display: flex; align-items: center; gap: var(--spacing-3);
      padding: var(--spacing-2) var(--spacing-3);
      background: var(--color-muted); border: var(--border-width) solid var(--color-border);
      border-radius: var(--radius-box);
    }
.file-name { flex: 1; font-size: 0.8125rem; color: var(--color-foreground); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-size { font-size: 0.6875rem; color: var(--color-muted-foreground); font-family: var(--font-family-mono); flex-shrink: 0; }
.file-remove {
      background: transparent; border: none; cursor: pointer;
      color: var(--color-muted-foreground); display: flex; align-items: center;
      padding: 2px; border-radius: var(--radius-xs);
      transition: color var(--duration-fast);
    }
.file-remove:hover { color: var(--color-destructive); }


/* ── TABLE HELPERS ───────────────────────── */
.col-check { width: 40px; }
.user-cell { display: flex; align-items: center; gap: var(--spacing-2); }

/* ── PROGRESS · TIMELINE · SKELETON · SEPARATOR ─ */
/* These were only ever defined inside the showroom's own <style>, so the
   component the documentation displayed had no counterpart in the delivered
   CSS. They are real entries in components-registry.json; they belong here. */
.progress-track { height: 8px; border-radius: var(--radius-full); background: var(--color-muted); overflow: hidden; }
.progress-track-sm { height: 4px; border-radius: var(--radius-full); background: var(--color-muted); overflow: hidden; }
.progress-track-lg { height: 12px; border-radius: var(--radius-full); background: var(--color-muted); overflow: hidden; }
.progress-fill { height: 100%; border-radius: var(--radius-full); background: var(--color-primary); transition: width var(--duration-slow) var(--ease-out); }
.progress-fill-indeterminate { height: 100%; width: 30%; border-radius: var(--radius-full); background: var(--color-primary); animation: progress-indeterminate 1.4s ease-in-out infinite; }
.separator { border: none; border-top: var(--border-width) solid var(--color-border); }
.timeline { display: flex; flex-direction: column; }
.timeline-item { display: flex; gap: var(--spacing-3); }
.timeline-connector { display: flex; flex-direction: column; align-items: center; flex-shrink: 0; }
.timeline-dot { width: 2rem; height: 2rem; border-radius: var(--radius-full); display: flex; align-items: center; justify-content: center; flex-shrink: 0; font-size: 0.75rem; font-weight: 600; border: 2px solid transparent; }
.timeline-dot.completed { background: var(--color-primary); color: var(--color-primary-foreground); border-color: var(--color-primary); }
.timeline-dot.current { background: color-mix(in oklab, var(--color-primary) 14%, transparent); color: var(--color-primary); border-color: var(--color-primary); }
.timeline-dot.upcoming { background: var(--color-muted); color: var(--color-muted-foreground); border-color: var(--color-border); }
.timeline-dot.error { background: var(--color-destructive); color: var(--color-destructive-foreground); border-color: var(--color-destructive); }
.timeline-line { width: 2px; flex: 1; min-height: 1.25rem; margin: 0.25rem 0; background: var(--color-border); }
.timeline-line.completed { background: var(--color-primary); }
.timeline-content { padding-bottom: var(--spacing-5); padding-top: 0.25rem; }
.timeline-h { display: flex; }
.timeline-h-item { display: flex; flex-direction: column; align-items: center; flex: 1; }
.timeline-h-track { display: flex; align-items: center; width: 100%; }
.timeline-h-line { flex: 1; height: 2px; background: var(--color-border); }
.timeline-h-line.completed { background: var(--color-primary); }
.timeline-h-line.invisible { visibility: hidden; }
.timeline-h-content { margin-top: var(--spacing-2); text-align: center; max-width: 80px; }
.skeleton { background: var(--color-muted); border-radius: var(--radius-sm); animation: skeleton-pulse 1.5s ease-in-out infinite; }

/* Size and state modifiers. They compose with any variant.
   These lived only in the showroom until v2.0, so the documentation
   displayed a button size the delivered CSS did not have. */
.btn-lg { padding: 0.625rem 1.25rem; font-size: 1rem; }
.btn-icon { padding: 0.4375rem; width: 2.25rem; height: 2.25rem; }
.btn-disabled { opacity: 0.5; pointer-events: none; }

/* ── AUTH SCREEN ──────────────────────────────── */
/* Login, password reset, second factor: the pages that exist before the shell
   does. They have no sidebar and no topbar, so they cannot inherit the layout
   from anywhere — which is why this lives here rather than being re-authored in
   each package, with a different prefix each time.

   The card is capped rather than fluid: a login form stretched across a wide
   monitor reads as a mistake, and the fields become harder to scan, not easier. */

.auth-page {
  min-height: 100vh; min-height: 100dvh;
  display: flex; align-items: center; justify-content: center;
  padding: var(--spacing-6);
  background: var(--color-background);
  /* Leave room for the fixed theme control: without it the card slides under
     the button on short or narrow viewports. */
  padding-top: max(var(--spacing-6), 4rem);
}

/* Opt-in brand ground. The entry screen is one of the few places a gradient is
   usually allowed, and the project's own rules say where — but a gradient must
   never be assumed, so it is a modifier rather than the default. Text never
   sits on it: everything lives inside the opaque card. */
.auth-page-brand {
  background-image: var(--gradient-brand, none);
}

.auth-card {
  width: 100%; max-width: 26rem;
  background: var(--color-card);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--spacing-8) var(--spacing-6);
}

.auth-brand {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--spacing-3); margin-bottom: var(--spacing-6);
}
.auth-title {
  font-size: 1.125rem; font-weight: 600; margin: 0;
  color: var(--color-foreground); text-align: center;
}
.auth-subtitle {
  font-size: 0.8125rem; color: var(--color-muted-foreground);
  margin: var(--spacing-1) 0 0; text-align: center;
}

.auth-form { display: flex; flex-direction: column; gap: var(--spacing-4); }

/* A row that carries "remember me" on one side and "forgot password" on the
   other. Wraps rather than squeezing, because both halves are interactive. */
.auth-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--spacing-3); flex-wrap: wrap;
}

.auth-footer {
  margin-top: var(--spacing-6); padding-top: var(--spacing-5);
  border-top: var(--border-width) solid var(--color-border);
  text-align: center;
  font-size: 0.75rem; color: var(--color-muted-foreground);
}

/* The theme control has nowhere else to live on a page with no topbar, and a
   person who needs high contrast needs it before signing in, not after. */
.auth-theme {
  position: fixed; top: var(--spacing-4); right: var(--spacing-4);
  z-index: 10;
}

@media (max-width: 480px) {
  /* flex-start so a tall form scrolls from the top instead of being centred and
     clipped; the top padding still has to clear the fixed theme control. */
  .auth-page { padding: var(--spacing-4); align-items: flex-start; padding-top: 4rem; }
  .auth-card { padding: var(--spacing-6) var(--spacing-5); }
}

/* ── CHOICE CARDS ─────────────────────────────── */
/* A small, fixed set of options rendered as cards instead of a <select>: the
   whole set is visible at once, each option carries an icon, and picking one is
   a single tap. Use it when the set is short enough to show entirely and the
   choice steers what happens next — a contact type that routes to a queue, a
   severity, an environment. Past roughly eight options, or when the labels are
   long, a <select> is the honest answer.

   It stays a real radio group: a visually hidden <input type="radio"> inside
   each <label>, with role="radiogroup" on the container. Keyboard arrows and
   screen readers get the semantics for free; the cards are only the skin. */

.choice-grid {
  display: grid;
  /* auto-fit, not a column count: the set reflows at every width instead of
     being crushed between two breakpoints. */
  grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
  gap: var(--spacing-2);
}

.choice-card {
  position: relative; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: var(--spacing-2);
  padding: var(--spacing-3) var(--spacing-2);
  background: var(--color-background); border: calc(var(--border-width) * 1.5) solid var(--color-border);
  border-radius: var(--radius-box); cursor: pointer; font-family: inherit;
  color: var(--color-muted-foreground);
  transition: border-color var(--duration-fast), background var(--duration-fast), color var(--duration-fast);
}

.choice-card:hover {
  border-color: var(--color-primary); color: var(--color-foreground);
  background: color-mix(in oklab, var(--color-primary) 6%, var(--color-background));
}

/* The radio is visually hidden, so the label never takes focus and a ring on
   the label would never fire. It has to come from the input, through :has(). */
.choice-card:has(input:focus-visible) {
  outline: 2px solid var(--color-ring); outline-offset: 2px;
}

/* Selected carries three signals, not one: border, an inset ring that thickens
   it without shifting layout, and a check mark. Colour alone is too weak in
   dark and high contrast — and colour alone never communicates state. */
.choice-card:has(input:checked) {
  border-color: var(--color-primary); color: var(--color-primary);
  background: color-mix(in oklab, var(--color-primary) 10%, var(--color-card));
  box-shadow: inset 0 0 0 1px var(--color-primary);
}

.choice-check {
  position: absolute; top: var(--spacing-2); right: var(--spacing-2);
  display: none;
}
.choice-card:has(input:checked) .choice-check { display: flex; }

.choice-icon {
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: var(--radius-sm);
  background: color-mix(in oklab, currentColor 12%, transparent);
}

.choice-label {
  font-size: 0.6875rem; font-weight: 600;
  letter-spacing: 0.04em; text-align: center; line-height: 1.2;
}

/* ── TABS ─────────────────────────────────────── */
/* components-registry declared tabs with canonical_in: ds-patterns.css while the
   class did not exist here — so a generator either invented one or downgraded the
   entry to page-local. Neither is right; the registry was telling the truth about
   where it belongs. */
.tabs {
  display: flex; align-items: center; gap: var(--spacing-1);
  border-bottom: var(--border-width) solid var(--color-border);
}
.tab {
  display: inline-flex; align-items: center; gap: var(--spacing-2);
  height: 34px; padding: 0 var(--spacing-3);
  background: none; border: none; border-bottom: 2px solid transparent;
  font-family: inherit; font-size: 0.8125rem; font-weight: 500;
  color: var(--color-muted-foreground); cursor: pointer;
  transition: color var(--duration-fast), border-color var(--duration-fast);
}
.tab:hover { color: var(--color-foreground); }
.tab.active {
  color: var(--color-foreground); border-bottom-color: var(--color-primary);
}
.tab:focus-visible { outline: 2px solid var(--color-ring); outline-offset: -2px; }
.tab-panel[hidden] { display: none; }

/* ═══════════════════════════════════════════════════════════════════
   BASELINE ADDITIONS — v2.1

   The set below is what an administrative product needs on an ordinary
   day and kept having to invent. It is deliberately a baseline, not a
   catalogue: anything past this, the consuming agent composes from
   these pieces. Every tint mixes in OKLAB, never OKLCH — against an
   achromatic surface OKLCH interpolates hue as an angle and drags the
   tint toward red.
   ═══════════════════════════════════════════════════════════════════ */

/* ── STATUS ───────────────────────────────────────
   Quieter than a badge: a badge is a labelled object, a status is the
   current state of one. The dot NEVER travels alone — colour by itself
   is not an accessible signal, and "green" means nothing to a screen
   reader. The text is part of the composition, not decoration. */
.status {
  display: inline-flex; align-items: center; gap: var(--spacing-2);
  font-size: 0.75rem; color: var(--color-muted-foreground);
  white-space: nowrap;
}
.status::before {
  content: ""; width: 6px; height: 6px; flex-shrink: 0;
  border-radius: var(--radius-full);
  background: var(--color-muted-foreground);
}
.status-success::before     { background: var(--color-success); }
.status-warning::before     { background: var(--color-warning); }
.status-destructive::before { background: var(--color-destructive); }
.status-info::before        { background: var(--color-info); }
/* A live state that is still settling. Pair with aria-live on the text. */
.status-pulse::before { animation: dsf-status-pulse 1.6s var(--ease-default) infinite; }
@keyframes dsf-status-pulse { 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) { .status-pulse::before { animation: none; } }

/* ── PROGRESS, RADIAL AND VERTICAL ────────────────
   .progress-track above is the horizontal bar. These two are the same
   measurement in the two other shapes a dashboard asks for. Both read
   their value from --value (0-100) so the markup carries one number and
   no inline colour. */
.progress-radial {
  --value: 0;
  --size: 72px;
  --thickness: 6px;
  position: relative; flex-shrink: 0;
  width: var(--size); height: var(--size);
  display: inline-grid; place-items: center;
  border-radius: var(--radius-full);
  background:
    conic-gradient(var(--color-primary) calc(var(--value) * 1%), var(--color-muted) 0);
}
/* The hole. A mask would be cleaner but is not safe on every target. */
.progress-radial::before {
  content: ""; position: absolute;
  inset: var(--thickness);
  border-radius: var(--radius-full);
  background: var(--color-card);
}
.progress-radial-value {
  position: relative;
  font-size: 0.8125rem; font-weight: 600;
  font-family: var(--font-family-mono);
  color: var(--color-foreground);
}
.progress-radial-sm { --size: 48px; --thickness: 5px; }
.progress-radial-lg { --size: 104px; --thickness: 8px; }
.progress-radial-success     { background: conic-gradient(var(--color-success)     calc(var(--value) * 1%), var(--color-muted) 0); }
.progress-radial-warning     { background: conic-gradient(var(--color-warning)     calc(var(--value) * 1%), var(--color-muted) 0); }
.progress-radial-destructive { background: conic-gradient(var(--color-destructive) calc(var(--value) * 1%), var(--color-muted) 0); }

/* Modifier on the existing track, not a second component: same bar, rotated. */
.progress-vertical {
  width: 8px; height: 120px;
  display: flex; align-items: flex-end;
}
.progress-vertical .progress-fill { width: 100%; height: var(--value, 0%); }

/* ── KEY–VALUE LIST ───────────────────────────────
   The most common composition in an admin product after the table:
   every detail panel, every "environment" card, every read-only summary.
   It kept being hand-written per page under a different name. */
.kv { display: flex; flex-direction: column; }
.kv-row {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--spacing-4);
  padding: var(--spacing-3) 0;
  border-bottom: var(--border-width) solid var(--color-border);
}
.kv-row:last-child { border-bottom: none; }
.kv-key   { font-size: 0.8125rem; color: var(--color-muted-foreground); }
.kv-value { font-size: 0.8125rem; color: var(--color-foreground); text-align: right; }
/* Stacked, for a narrow column where the two would collide. */
.kv-stacked .kv-row { flex-direction: column; align-items: stretch; gap: 2px; }
.kv-stacked .kv-value { text-align: left; }

/* ── COPY FIELD ───────────────────────────────────
   An identifier, an API key, a connection string. Read-only by
   construction — the value is selectable but not editable, and the
   button owns the only interaction. The confirmation is a state on the
   button, so the value never shifts and the layout never jumps. */
.copy-field {
  display: flex; align-items: stretch;
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-box);
  background: var(--color-muted);
  overflow: hidden;
}
.copy-field-value {
  flex: 1; min-width: 0;
  padding: var(--spacing-2) var(--spacing-3);
  font-family: var(--font-family-mono); font-size: 0.75rem;
  color: var(--color-foreground);
  white-space: nowrap; overflow-x: auto; text-overflow: ellipsis;
  display: flex; align-items: center;
}
.copy-field-btn {
  flex-shrink: 0;
  display: inline-flex; align-items: center; gap: var(--spacing-2);
  padding: 0 var(--spacing-3);
  border: none; border-left: var(--border-width) solid var(--color-border);
  background: var(--color-card); color: var(--color-muted-foreground);
  font-size: 0.75rem; cursor: pointer;
  transition: color var(--duration-fast) var(--ease-default),
              background var(--duration-fast) var(--ease-default);
}
.copy-field-btn:hover { color: var(--color-foreground); background: var(--color-accent); }
.copy-field-btn:focus-visible { outline: 2px solid var(--color-ring); outline-offset: -2px; }
.copy-field-btn.is-copied { color: var(--color-success); }

/* ── STAT DELTA ───────────────────────────────────
   The trend line under a .stat-value. Direction and meaning are two
   different things and the component keeps them apart on purpose: a
   rising error rate points up and is bad. Using the arrow to pick the
   colour is how a dashboard ends up celebrating its own outage. */
.stat-delta {
  display: inline-flex; align-items: center; gap: var(--spacing-1);
  font-size: 0.75rem; color: var(--color-muted-foreground);
}
.stat-delta-good { color: var(--color-success); }
.stat-delta-bad  { color: var(--color-destructive); }
.stat-delta-flat { color: var(--color-muted-foreground); }

/* ── LOG / CODE BLOCK ─────────────────────────────
   Administrative products show logs, payloads and stack traces
   constantly, and every page was inventing a <pre> with its own
   padding. Severity is a class on the line, never a bare colour. */
.log-block {
  margin: 0;
  max-height: 320px; overflow: auto;
  padding: var(--spacing-3) var(--spacing-4);
  background: var(--color-muted);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-box);
  font-family: var(--font-family-mono); font-size: 0.6875rem;
  line-height: 1.7; color: var(--color-foreground);
  white-space: pre; tab-size: 2;
}
.log-line { display: flex; gap: var(--spacing-3); white-space: pre-wrap; word-break: break-word; }
.log-time { flex-shrink: 0; color: var(--color-muted-foreground); }
/* Puxados para o foreground porque a superfície aqui é --color-muted, não o card:
   o âmbar puro dá 4.2:1 contra ela no tema claro, abaixo dos 4.5 de AA. A mistura
   é em OKLAB e derivada do token — a cor continua sendo a do projeto, só com
   contraste suficiente para texto pequeno em monoespaçada. */
.log-line.is-error { color: color-mix(in oklab, var(--color-destructive) 88%, var(--color-foreground)); }
.log-line.is-warn  { color: color-mix(in oklab, var(--color-warning) 72%, var(--color-foreground)); }
.log-line.is-ok    { color: color-mix(in oklab, var(--color-success) 88%, var(--color-foreground)); }

/* ── TOOLTIP ──────────────────────────────────────
   The rich one: a title, a line of explanation, optionally a shortcut.
   The collapsed sidebar's data-tooltip is a different thing — a label
   for an icon that lost its text, not an explanation. This one is a
   real element so it can hold markup and be referenced by
   aria-describedby; a ::after cannot be.
   Requires a positioned ancestor carrying .has-tooltip. */
.has-tooltip { position: relative; }
.tooltip {
  position: absolute; bottom: calc(100% + var(--spacing-2)); left: 50%;
  transform: translateX(-50%) translateY(2px);
  z-index: 300; width: max-content; max-width: 260px;
  padding: var(--spacing-2) var(--spacing-3);
  background: var(--color-popover); color: var(--color-popover-foreground);
  border: var(--border-width) solid var(--color-border); border-radius: var(--radius-box);
  box-shadow: var(--shadow-md);
  font-size: 0.75rem; line-height: 1.5;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--duration-fast) var(--ease-default),
              transform var(--duration-fast) var(--ease-default),
              visibility var(--duration-fast);
}
/* Keyboard reaches it too: focus-within, not hover alone. */
.has-tooltip:hover > .tooltip,
.has-tooltip:focus-within > .tooltip {
  opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0);
}
.tooltip-title { font-weight: 600; color: var(--color-foreground); margin: 0 0 2px; }
.tooltip-body  { margin: 0; color: var(--color-muted-foreground); }
.tooltip-kbd   { margin-top: var(--spacing-2); display: flex; gap: var(--spacing-1); align-items: center; }
.tooltip-bottom { bottom: auto; top: calc(100% + var(--spacing-2)); }

/* ── CHAT ─────────────────────────────────────────
   Support threads and assistant panels. Two directions, one class:
   .is-own flips the row. The timestamp is outside the bubble so a long
   message never pushes it out of reach. */
.chat { display: flex; flex-direction: column; gap: var(--spacing-4); }
.chat-row { display: flex; gap: var(--spacing-3); align-items: flex-end; max-width: 78%; }
.chat-row.is-own { flex-direction: row-reverse; align-self: flex-end; }
.chat-bubble {
  padding: var(--spacing-3) var(--spacing-4);
  border-radius: var(--radius-lg);
  background: var(--color-muted); color: var(--color-foreground);
  font-size: 0.8125rem; line-height: 1.55;
  border: var(--border-width) solid var(--color-border);
}
.chat-row.is-own .chat-bubble {
  background: var(--color-primary); color: var(--color-primary-foreground);
  border-color: transparent;
}
.chat-meta {
  margin-top: var(--spacing-1);
  font-size: 0.6875rem; color: var(--color-muted-foreground);
}
.chat-row.is-own .chat-meta { text-align: right; }
/* Someone is typing. Three dots, no layout shift when it disappears. */
.chat-typing { display: inline-flex; gap: 3px; align-items: center; }
.chat-typing span {
  width: 5px; height: 5px; border-radius: var(--radius-full);
  background: var(--color-muted-foreground);
  animation: dsf-typing 1.2s var(--ease-default) infinite;
}
.chat-typing span:nth-child(2) { animation-delay: 0.15s; }
.chat-typing span:nth-child(3) { animation-delay: 0.3s; }
@keyframes dsf-typing { 50% { opacity: 0.25; } }
@media (prefers-reduced-motion: reduce) { .chat-typing span { animation: none; } }

/* ── CALENDAR ─────────────────────────────────────
   THE VISUAL CONTRACT ONLY. This package ships static HTML and CSS, so
   what lives here is the shape and the cell states — which is the part
   that drifts when every screen redraws it. The behaviour (month
   arithmetic, keyboard grid navigation, locale, range selection) belongs
   to the consuming stack: shadcn's calendar in React, or the date
   library the project already has. Do not fake it with CSS.
   State classes carry is- so a page cannot collide with them by naming
   something "selected". */
.calendar {
  width: max-content;
  padding: var(--spacing-4);
  background: var(--color-card);
  border: var(--border-width) solid var(--color-border);
  border-radius: var(--radius-box);
}
.calendar-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--spacing-4); margin-bottom: var(--spacing-3);
}
.calendar-title { font-size: 0.8125rem; font-weight: 600; color: var(--color-foreground); }
.calendar-grid { display: grid; grid-template-columns: repeat(7, 32px); gap: 2px; }
.calendar-dow {
  display: grid; place-items: center; height: 24px;
  font-size: 0.6875rem; font-weight: 500;
  color: var(--color-muted-foreground);
}
.calendar-day {
  display: grid; place-items: center;
  height: 32px; border: none; background: none;
  border-radius: var(--radius-sm);
  font-size: 0.75rem; color: var(--color-foreground);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-default);
}
.calendar-day:hover { background: var(--color-accent); }
.calendar-day:focus-visible { outline: 2px solid var(--color-ring); outline-offset: -2px; }
/* Today is marked, not selected — the two say different things and a
   calendar that conflates them lies about what is chosen. */
.calendar-day.is-today { box-shadow: inset 0 0 0 1px var(--color-primary); font-weight: 600; }
.calendar-day.is-selected { background: var(--color-primary); color: var(--color-primary-foreground); }
.calendar-day.is-range { background: color-mix(in oklab, var(--color-primary) 12%, var(--color-card)); border-radius: 0; }
/* Fora do mês e indisponível não podem se parecer: um dia não pertence a este
   mês, o outro não pode ser escolhido. Só opacidade deixava os dois iguais, e a
   diferença importa na hora de clicar. O risco não é cor, então continua
   legível em alto contraste e para quem não distingue matiz. */
.calendar-day.is-outside  { color: var(--color-muted-foreground); opacity: 0.55; }
.calendar-day.is-disabled {
  color: var(--color-muted-foreground); opacity: 0.6;
  cursor: not-allowed; text-decoration: line-through;
  text-decoration-thickness: 1px;
}
.calendar-day.is-disabled:hover { background: none; }

/* ── TIMELINE, HORIZONTAL ─────────────────────────
   The vertical .timeline above is for a feed. This one is for a process
   with a known number of steps — an approval chain, a deploy pipeline.
   Steps are numbered in the markup, because position alone is not
   available to a screen reader. */
.timeline-h { width: 100%; overflow-x: auto; padding-bottom: var(--spacing-2); }
.timeline-h-track { display: flex; align-items: flex-start; min-width: max-content; }
.timeline-h-item {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--spacing-2); flex: 1; min-width: 120px; text-align: center;
}
.timeline-h-dot {
  width: 12px; height: 12px; border-radius: var(--radius-full);
  background: var(--color-muted); border: 2px solid var(--color-card);
  box-shadow: 0 0 0 1px var(--color-border);
  flex-shrink: 0;
}
.timeline-h-item.is-done    .timeline-h-dot { background: var(--color-success); box-shadow: 0 0 0 1px var(--color-success); }
.timeline-h-item.is-current .timeline-h-dot { background: var(--color-primary); box-shadow: 0 0 0 1px var(--color-primary); }
.timeline-h-line { height: 2px; flex: 1; background: var(--color-border); margin-top: 5px; }
.timeline-h-line.is-done { background: var(--color-success); }
.timeline-h-content { display: flex; flex-direction: column; gap: 2px; }
.timeline-h-title { font-size: 0.75rem; font-weight: 500; color: var(--color-foreground); }
.timeline-h-meta  { font-size: 0.6875rem; color: var(--color-muted-foreground); }

/* ══════════════════════════════════════════════════════════════════
   MOBILE CONTENT PATTERNS

   The shell handles the chrome below 768px (sidebar becomes a drawer, topbar
   sheds labels); these handle page content.

   Layout compositions, not components: nothing here replaces a shadcn primitive.
   ══════════════════════════════════════════════════════════════════ */

/* ── TABLE → STACKED CARDS ────────────────────── */
/* A table with six columns on a 375px screen is a horizontal scroll nobody
   performs. Below md each row becomes a card and each cell carries its own
   label, taken from data-label so the header is not lost with the <thead>. */
.table-stack { width: 100%; border-collapse: collapse; }

@media (max-width: 768px) {
  /* The alert is icon + text + actions in one row. At phone width the actions
     no longer fit beside the text and push the page into a horizontal scroll,
     so they wrap onto a line of their own. */
  .alert { flex-wrap: wrap; }
  .alert > .page-actions { width: 100%; }

  .table-stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .table-stack tr {
    display: block; margin-bottom: var(--spacing-3);
    border: var(--border-width) solid var(--color-border);
    border-radius: var(--radius-box); background: var(--color-card);
    padding: var(--spacing-3);
  }
  .table-stack td {
    display: flex; justify-content: space-between; align-items: baseline;
    gap: var(--spacing-4); padding: var(--spacing-2) 0; border: 0;
    text-align: right;
  }
  .table-stack td + td { border-top: var(--border-width) solid var(--color-border); }
  .table-stack td::before {
    content: attr(data-label);
    font-size: 0.75rem; font-weight: 500; color: var(--color-muted-foreground);
    text-align: left; flex-shrink: 0;
  }
  /* A cell with no label is the row's actions: it keeps the full width. */
  .table-stack td[data-label=""]::before,
  .table-stack td:not([data-label])::before { content: none; }
  .table-stack td:not([data-label]) { justify-content: flex-end; }
}

/* ── KPI ROW ──────────────────────────────────── */
/* Four metrics side by side become two, then one. They never shrink below
   readable: a KPI whose number wraps has stopped being a KPI. */
.kpi-row {
  display: grid; gap: var(--spacing-4);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
@media (max-width: 768px) { .kpi-row { grid-template-columns: 1fr 1fr; gap: var(--spacing-3); } }
@media (max-width: 480px) { .kpi-row { grid-template-columns: 1fr; } }

/* ── FORM STACK ───────────────────────────────── */
/* Two columns collapse to one below md. Paired fields that belong together on
   one line — day/month, value/currency — opt out with .form-stack-inline. */
.form-stack { display: grid; grid-template-columns: 1fr 1fr; gap: var(--spacing-4); }
.form-stack-full { grid-column: 1 / -1; }
@media (max-width: 768px) {
  .form-stack { grid-template-columns: 1fr; }
  .form-stack-inline { display: grid; grid-template-columns: 1fr 1fr; gap: var(--spacing-3); }
}

/* ── STICKY ACTION BAR ────────────────────────── */
/* On a long form the Save button sits below the fold, and on a phone the fold
   is most of the page. Below md the primary actions pin to the bottom.
   env(safe-area-inset-bottom) keeps them clear of the iOS home indicator —
   without it the button sits under the gesture bar and takes no taps.

   It must be the LAST child of the content container. Sticky keeps it in flow,
   so whatever follows it renders below and is never covered — but anything
   placed after it pushes the bar up the page and the effect is lost. */
.action-bar-sticky { display: flex; gap: var(--spacing-3); justify-content: flex-end; }
@media (max-width: 768px) {
  .action-bar-sticky {
    position: sticky; bottom: 0; z-index: 20;
    margin: var(--spacing-4) calc(var(--spacing-4) * -1) 0;
    padding: var(--spacing-3) var(--spacing-4);
    padding-bottom: calc(var(--spacing-3) + env(safe-area-inset-bottom, 0px));
    background: var(--color-card);
    border-top: var(--border-width) solid var(--color-border);
  }
  .action-bar-sticky > * { flex: 1; }
}

/* ── SCROLLABLE TABS ──────────────────────────── */
/* Tabs do not wrap to a second line: a tab strip that becomes two rows reads as
   two groups. They scroll, with the scrollbar hidden and a fade on the right
   edge saying there is more. The fade is decorative and never takes a tap. */
.scroll-tabs { display: flex; gap: var(--spacing-1); position: relative; }
@media (max-width: 768px) {
  .scroll-tabs {
    overflow-x: auto; flex-wrap: nowrap;
    scrollbar-width: none; -ms-overflow-style: none;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
  }
  .scroll-tabs::-webkit-scrollbar { display: none; }
  .scroll-tabs > * { flex-shrink: 0; scroll-snap-align: start; }
  .scroll-tabs-wrap { position: relative; }
  .scroll-tabs-wrap::after {
    content: ""; position: absolute; top: 0; right: 0; bottom: 0; width: 24px;
    pointer-events: none;
    background: linear-gradient(to right, transparent, var(--color-background));
  }
}
