/* @devslab-kr/dds-css — generated bundle, do not edit. Source: src/*.css
 * Requires @devslab-kr/dds-tokens/tokens.css to be loaded first. */

/* dds-css shared plumbing. Components reference ONLY semantic tokens
 * (var(--dds-*)) — tokens.css must be loaded first. */
[class^="dds-"],
[class*=" dds-"] {
  box-sizing: border-box;
}

/* Button — spec §4.2: default → hover → pressed → focus-visible → disabled → loading.
 * Variants: primary / secondary / ghost / danger. Sizes: sm / md(default) / lg.
 * Control heights come from the spacing scale (32/40/48); type from the typo
 * scale (label for sm/md, body-1 size for lg). Loading = aria-busy="true" with
 * a .dds-spinner child. */

.dds-btn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--dds-space-8);
  height: var(--dds-space-40);
  padding: 0 var(--dds-space-16);
  border: 1px solid transparent;
  border-radius: var(--dds-radius-md);
  background: none;
  font-family: var(--dds-font-family-sans);
  font-size: var(--dds-typo-label-font-size);
  line-height: var(--dds-typo-label-line-height);
  font-weight: var(--dds-typo-label-font-weight);
  color: var(--dds-color-text-primary);
  cursor: pointer;
  transition: background-color var(--dds-motion-duration-fast) var(--dds-motion-easing-standard);
  -webkit-tap-highlight-color: transparent;
}

.dds-btn:focus-visible {
  outline: var(--dds-space-2) solid var(--dds-color-border-focus);
  outline-offset: var(--dds-space-2);
}

/* opacity is allowed for disabled only (spec §4.2) */
.dds-btn[disabled],
.dds-btn[aria-busy="true"] {
  opacity: 0.45;
  cursor: not-allowed;
}
.dds-btn[aria-busy="true"] {
  pointer-events: none;
}

/* variants ---------------------------------------------------------------- */

.dds-btn--primary {
  background: var(--dds-color-bg-brand);
  color: var(--dds-color-text-on-brand); /* zinc.950, not white — spec §3.1 on-brand note */
  font-weight: 600;
}
.dds-btn--primary:not([disabled]):not([aria-busy="true"]):hover {
  background: var(--dds-color-bg-brand-hover);
}
.dds-btn--primary:not([disabled]):active {
  background: var(--dds-color-bg-brand-hover);
  box-shadow: inset 0 0 0 999px var(--dds-color-overlay-pressed-tint);
}

.dds-btn--secondary {
  border-color: var(--dds-color-border-strong);
}
.dds-btn--secondary:not([disabled]):not([aria-busy="true"]):hover {
  background: var(--dds-color-overlay-hover-tint);
}
.dds-btn--secondary:not([disabled]):active {
  background: var(--dds-color-overlay-pressed-tint);
}

.dds-btn--ghost {
  color: var(--dds-color-text-brand);
}
.dds-btn--ghost:not([disabled]):not([aria-busy="true"]):hover {
  background: var(--dds-color-overlay-hover-tint);
}
.dds-btn--ghost:not([disabled]):active {
  background: var(--dds-color-overlay-pressed-tint);
}

.dds-btn--danger {
  background: var(--dds-color-status-danger);
  color: var(--dds-color-text-on-status);
  font-weight: 600;
}
.dds-btn--danger:not([disabled]):not([aria-busy="true"]):hover {
  box-shadow: inset 0 0 0 999px var(--dds-color-overlay-hover-tint);
}
.dds-btn--danger:not([disabled]):active {
  box-shadow: inset 0 0 0 999px var(--dds-color-overlay-pressed-tint);
}

/* sizes ------------------------------------------------------------------- */

.dds-btn--sm {
  height: var(--dds-space-32);
  padding: 0 var(--dds-space-12);
}

.dds-btn--lg {
  height: var(--dds-space-48);
  padding: 0 var(--dds-space-20);
  font-size: var(--dds-typo-body-1-font-size);
  line-height: var(--dds-typo-body-1-line-height);
}

/* IconButton — a Button whose only content is an icon (spec §4.3 v1 inventory).
 * Same state set and sizes as .dds-btn (32/40/48 from the spacing scale) so an
 * icon button never lands a few px off the text button next to it — the row
 * misalignment this house rejects. The icon inherits currentColor.
 * Markup MUST carry an accessible name: aria-label (spec §6). */

.dds-iconbtn {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--dds-space-40);
  height: var(--dds-space-40);
  border: 1px solid transparent;
  border-radius: var(--dds-radius-md);
  background: none;
  color: var(--dds-color-text-primary);
  cursor: pointer;
  transition: background-color var(--dds-motion-duration-fast) var(--dds-motion-easing-standard);
  -webkit-tap-highlight-color: transparent;
}

.dds-iconbtn:not([disabled]):hover {
  background: var(--dds-color-overlay-hover-tint);
}
.dds-iconbtn:not([disabled]):active {
  background: var(--dds-color-overlay-pressed-tint);
}
.dds-iconbtn:focus-visible {
  outline: var(--dds-space-2) solid var(--dds-color-border-focus);
  outline-offset: var(--dds-space-2);
}

/* opacity is allowed for disabled only (spec §4.2) */
.dds-iconbtn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

/* variants ---------------------------------------------------------------- */

.dds-iconbtn--secondary {
  border-color: var(--dds-color-border-strong);
}

.dds-iconbtn--danger {
  color: var(--dds-color-status-danger);
}

/* sizes ------------------------------------------------------------------- */

/* --sm is 32×32: still above the 24×24 web floor, but below the 44×44 mobile
 * target — on touch surfaces keep the default size (spec §6). */
.dds-iconbtn--sm {
  width: var(--dds-space-32);
  height: var(--dds-space-32);
}

.dds-iconbtn--lg {
  width: var(--dds-space-48);
  height: var(--dds-space-48);
}

/* TextField — label + input + help, error via .dds-field--error OR
 * aria-invalid="true" on the input (markup should set aria-invalid +
 * aria-describedby; error is never conveyed by border color alone — the
 * help line carries the message, spec §6). */

.dds-field {
  display: flex;
  flex-direction: column;
  gap: var(--dds-space-4);
  font-family: var(--dds-font-family-sans);
}

.dds-field__label {
  font-size: var(--dds-typo-label-font-size);
  line-height: var(--dds-typo-label-line-height);
  font-weight: var(--dds-typo-label-font-weight);
  color: var(--dds-color-text-primary);
}

.dds-input {
  font-family: var(--dds-font-family-sans);
  font-size: var(--dds-typo-body-2-font-size);
  line-height: var(--dds-typo-body-2-line-height);
  font-weight: var(--dds-typo-body-2-font-weight);
  height: var(--dds-space-40);
  padding: 0 var(--dds-space-12);
  background: var(--dds-color-bg-default);
  color: var(--dds-color-text-primary);
  border: 1px solid var(--dds-color-border-strong);
  border-radius: var(--dds-radius-md);
}

.dds-input::placeholder {
  color: var(--dds-color-text-muted);
}

.dds-input:focus-visible {
  outline: var(--dds-space-2) solid var(--dds-color-border-focus);
  outline-offset: 0;
  border-color: transparent;
}

/* opacity is allowed for disabled only (spec §4.2) */
.dds-input[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
  background: var(--dds-color-bg-subtle);
}

.dds-field__help {
  font-size: var(--dds-typo-caption-font-size);
  line-height: var(--dds-typo-caption-line-height);
  font-weight: var(--dds-typo-caption-font-weight);
  color: var(--dds-color-text-muted);
}

.dds-field--error .dds-input,
.dds-input[aria-invalid="true"] {
  border-color: var(--dds-color-status-danger);
}
.dds-field--error .dds-field__help {
  color: var(--dds-color-status-danger);
}

/* Textarea — the multi-line member of the TextField family: it lives in the
 * same .dds-field wrapper and shares label/help/error behavior with .dds-input.
 * Row rule from the house UI discipline: a textarea gets its own row, never a
 * row shared with single-line controls (their heights can't be reconciled).
 * min-height is two control heights so an empty textarea already reads as
 * multi-line. */

.dds-textarea {
  font-family: var(--dds-font-family-sans);
  font-size: var(--dds-typo-body-2-font-size);
  line-height: var(--dds-typo-body-2-line-height);
  font-weight: var(--dds-typo-body-2-font-weight);
  min-height: calc(var(--dds-space-40) * 2);
  padding: var(--dds-space-8) var(--dds-space-12);
  background: var(--dds-color-bg-default);
  color: var(--dds-color-text-primary);
  border: 1px solid var(--dds-color-border-strong);
  border-radius: var(--dds-radius-md);
  resize: vertical;
}

.dds-textarea::placeholder {
  color: var(--dds-color-text-muted);
}

.dds-textarea:focus-visible {
  outline: var(--dds-space-2) solid var(--dds-color-border-focus);
  outline-offset: 0;
  border-color: transparent;
}

/* opacity is allowed for disabled only (spec §4.2) */
.dds-textarea[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
  background: var(--dds-color-bg-subtle);
}

.dds-field--error .dds-textarea,
.dds-textarea[aria-invalid="true"] {
  border-color: var(--dds-color-status-danger);
}

/* Select — native <select> with the native arrow replaced. Leaving the OS
 * arrow in place is a rejection criterion in this house: the same control
 * then looks different on every platform and never matches the input next to
 * it. The wrapper owns the chevron so the element stays a real <select>
 * (keyboard, mobile pickers, form semantics all free).
 *
 * Markup: <span class="dds-select"><select class="dds-select__input">…
 * Lives inside .dds-field like .dds-input, so label/help/error come for free. */

.dds-select {
  position: relative;
  display: inline-flex;
  align-items: center;
  width: 100%;
}

.dds-select__input {
  appearance: none;
  font-family: var(--dds-font-family-sans);
  font-size: var(--dds-typo-body-2-font-size);
  line-height: var(--dds-typo-body-2-line-height);
  font-weight: var(--dds-typo-body-2-font-weight);
  width: 100%;
  height: var(--dds-space-40);
  /* right padding leaves the chevron lane free */
  padding: 0 var(--dds-space-32) 0 var(--dds-space-12);
  background: var(--dds-color-bg-default);
  color: var(--dds-color-text-primary);
  border: 1px solid var(--dds-color-border-strong);
  border-radius: var(--dds-radius-md);
  cursor: pointer;
}

.dds-select__input:focus-visible {
  outline: var(--dds-space-2) solid var(--dds-color-border-focus);
  outline-offset: 0;
  border-color: transparent;
}

/* opacity is allowed for disabled only (spec §4.2) */
.dds-select__input[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
  background: var(--dds-color-bg-subtle);
}

.dds-field--error .dds-select__input,
.dds-select__input[aria-invalid="true"] {
  border-color: var(--dds-color-status-danger);
}

/* Chevron glyph geometry (8px box, 1.6px stroke) is drawn, not spaced — the
 * same allowance as the 6px badge dot. translateY(-70%) centers the drawn
 * corner, which sits below the box center once rotated. */
.dds-select::after {
  content: "";
  position: absolute;
  top: 50%;
  right: var(--dds-space-12);
  width: 8px;
  height: 8px;
  border-right: 1.6px solid var(--dds-color-text-muted);
  border-bottom: 1.6px solid var(--dds-color-text-muted);
  transform: translateY(-70%) rotate(45deg);
  pointer-events: none;
}

/* Checkbox / Radio — one class for both; the input's own type decides the
 * shape (square + tick vs circle + dot), so markup never has to declare the
 * shape twice. The control stays a real <input> (keyboard, form data, AT
 * roles); only its box is redrawn.
 *
 * Box 18px, tick 10×6, dot 8px: glyph geometry, not spacing (same allowance
 * as the 6px badge dot). The 18px box sits inside a label whose hit area is
 * the whole row — keep the label text clickable rather than growing the box. */

.dds-check {
  display: inline-flex;
  align-items: center;
  gap: var(--dds-space-8);
  font-family: var(--dds-font-family-sans);
  font-size: var(--dds-typo-body-2-font-size);
  line-height: var(--dds-typo-body-2-line-height);
  color: var(--dds-color-text-primary);
  cursor: pointer;
}

.dds-check__input {
  appearance: none;
  flex: none;
  display: grid;
  place-items: center;
  margin: 0;
  width: 18px;
  height: 18px;
  border: 1px solid var(--dds-color-border-strong);
  border-radius: var(--dds-radius-sm);
  background: var(--dds-color-bg-default);
  cursor: inherit;
  transition: background-color var(--dds-motion-duration-fast) var(--dds-motion-easing-standard);
}

.dds-check__input[type="radio"] {
  border-radius: var(--dds-radius-full);
}

.dds-check__input:not([disabled]):hover {
  border-color: var(--dds-color-text-muted);
}

.dds-check__input:checked {
  background: var(--dds-color-bg-brand);
  border-color: var(--dds-color-bg-brand);
}

/* the mark is on-brand (zinc.950), never white — spec §3.1 on-brand note */
.dds-check__input[type="checkbox"]:checked::before {
  content: "";
  width: 10px;
  height: 6px;
  border-left: 2px solid var(--dds-color-text-on-brand);
  border-bottom: 2px solid var(--dds-color-text-on-brand);
  transform: translateY(-1px) rotate(-45deg);
}

.dds-check__input[type="checkbox"]:indeterminate::before {
  content: "";
  width: 10px;
  height: 2px;
  background: var(--dds-color-text-on-brand);
}
.dds-check__input[type="checkbox"]:indeterminate {
  background: var(--dds-color-bg-brand);
  border-color: var(--dds-color-bg-brand);
}

.dds-check__input[type="radio"]:checked::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: var(--dds-radius-full);
  background: var(--dds-color-text-on-brand);
}

.dds-check__input:focus-visible {
  outline: var(--dds-space-2) solid var(--dds-color-border-focus);
  outline-offset: var(--dds-space-2);
}

/* opacity is allowed for disabled only (spec §4.2) — it goes on the label so
 * the box and its text dim once, not twice (nested opacity compounds). */
.dds-check:has(.dds-check__input[disabled]) {
  opacity: 0.45;
  cursor: not-allowed;
}
.dds-check__input[disabled] {
  cursor: not-allowed;
}

/* Switch — an immediate on/off (a checkbox that applies itself). Use a
 * checkbox when the value only takes effect on submit.
 * Markup: <input type="checkbox" role="switch" class="dds-switch__input">
 *
 * Track 36×20 and knob 16px are glyph geometry, not spacing. The knob is
 * on-brand (zinc.950) when the track is brand-filled — the same rule that
 * keeps white off cyan (spec §3.1) — and an elevated surface when off, so it
 * reads as a raised knob in both themes. */

.dds-switch {
  display: inline-flex;
  align-items: center;
  gap: var(--dds-space-8);
  font-family: var(--dds-font-family-sans);
  font-size: var(--dds-typo-body-2-font-size);
  line-height: var(--dds-typo-body-2-line-height);
  color: var(--dds-color-text-primary);
  cursor: pointer;
}

.dds-switch__input {
  appearance: none;
  position: relative;
  flex: none;
  margin: 0;
  width: 36px;
  height: 20px;
  border-radius: var(--dds-radius-full);
  background: var(--dds-color-text-muted);
  cursor: inherit;
  transition: background-color var(--dds-motion-duration-base) var(--dds-motion-easing-standard);
}

.dds-switch__input::before {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: var(--dds-radius-full);
  background: var(--dds-color-bg-elevated);
  box-shadow: var(--dds-elevation-1);
  transition: transform var(--dds-motion-duration-base) var(--dds-motion-easing-standard);
}

.dds-switch__input:checked {
  background: var(--dds-color-bg-brand);
}
.dds-switch__input:checked::before {
  background: var(--dds-color-text-on-brand);
  transform: translateX(16px);
}

.dds-switch__input:focus-visible {
  outline: var(--dds-space-2) solid var(--dds-color-border-focus);
  outline-offset: var(--dds-space-2);
}

/* opacity is allowed for disabled only (spec §4.2) — on the label, once */
.dds-switch:has(.dds-switch__input[disabled]) {
  opacity: 0.45;
  cursor: not-allowed;
}
.dds-switch__input[disabled] {
  cursor: not-allowed;
}

@media (prefers-reduced-motion: reduce) {
  .dds-switch__input,
  .dds-switch__input::before {
    transition: none;
  }
}

/* Badge — brand + the four status pairs. Status colors always ship as
 * foreground + -bg pair (spec §3.1); the leading dot repeats the state so
 * color is not the only signal (spec §6). Dot size 6px is glyph geometry,
 * not spacing (same 6px as the toast dot — keep them identical). */

.dds-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--dds-space-4);
  font-family: var(--dds-font-family-sans);
  font-size: var(--dds-typo-label-font-size);
  line-height: var(--dds-typo-label-line-height);
  font-weight: var(--dds-typo-label-font-weight);
  padding: var(--dds-space-2) var(--dds-space-8);
  border-radius: var(--dds-radius-full);
  white-space: nowrap;
}

.dds-badge::before {
  content: "";
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: var(--dds-radius-full);
  background: currentColor;
}

.dds-badge--brand {
  color: var(--dds-color-text-brand);
  background: var(--dds-color-bg-brand-subtle);
}
.dds-badge--success {
  color: var(--dds-color-status-success);
  background: var(--dds-color-status-success-bg);
}
.dds-badge--warning {
  color: var(--dds-color-status-warning);
  background: var(--dds-color-status-warning-bg);
}
.dds-badge--danger {
  color: var(--dds-color-status-danger);
  background: var(--dds-color-status-danger-bg);
}
.dds-badge--info {
  color: var(--dds-color-status-info);
  background: var(--dds-color-status-info-bg);
}

/* Chip — a small interactive control: filter toggle, suggested question,
 * removable tag. Badge and Chip are different components on purpose: a badge
 * is state you read, a chip is something you press. Never restyle one into
 * the other (the duplicated-.chip collision this house has already paid for).
 *
 * Selected state is carried by aria-pressed="true" (toggle chips) so the
 * state is announced, not only colored; --selected exists for markup that
 * can't use aria-pressed (e.g. a link chip). */

.dds-chip {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: var(--dds-space-4);
  font-family: var(--dds-font-family-sans);
  font-size: var(--dds-typo-label-font-size);
  line-height: var(--dds-typo-label-line-height);
  font-weight: var(--dds-typo-label-font-weight);
  padding: var(--dds-space-4) var(--dds-space-12);
  border: 1px solid var(--dds-color-border-strong);
  border-radius: var(--dds-radius-full);
  background: none;
  color: var(--dds-color-text-primary);
  cursor: pointer;
  transition: background-color var(--dds-motion-duration-fast) var(--dds-motion-easing-standard);
  -webkit-tap-highlight-color: transparent;
}

.dds-chip:not([disabled]):hover {
  background: var(--dds-color-overlay-hover-tint);
}
.dds-chip:not([disabled]):active {
  background: var(--dds-color-overlay-pressed-tint);
}
.dds-chip:focus-visible {
  outline: var(--dds-space-2) solid var(--dds-color-border-focus);
  outline-offset: var(--dds-space-2);
}

.dds-chip[aria-pressed="true"],
.dds-chip--selected {
  background: var(--dds-color-bg-brand-subtle);
  border-color: var(--dds-color-bg-brand);
  color: var(--dds-color-text-brand);
}

/* opacity is allowed for disabled only (spec §4.2) */
.dds-chip[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

/* Avatar — a person or tenant mark: image if there is one, initials if not.
 * Sizes come from the spacing scale (32/40/48) so an avatar lines up with the
 * control of the same size in a list row. The initials fallback uses the
 * brand-subtle pair, which re-tints with the product brand automatically. */

.dds-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--dds-space-40);
  height: var(--dds-space-40);
  border-radius: var(--dds-radius-full);
  overflow: hidden;
  background: var(--dds-color-bg-brand-subtle);
  color: var(--dds-color-text-brand);
  font-family: var(--dds-font-family-sans);
  font-size: var(--dds-typo-label-font-size);
  line-height: var(--dds-typo-label-line-height);
  font-weight: 600;
  user-select: none;
}

.dds-avatar__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.dds-avatar--sm {
  width: var(--dds-space-32);
  height: var(--dds-space-32);
  font-size: var(--dds-typo-caption-font-size);
  line-height: var(--dds-typo-caption-line-height);
}

.dds-avatar--lg {
  width: var(--dds-space-48);
  height: var(--dds-space-48);
  font-size: var(--dds-typo-body-1-font-size);
  line-height: var(--dds-typo-body-1-line-height);
}

/* square variant for tenants/logos — a company mark in a circle gets clipped */
.dds-avatar--square {
  border-radius: var(--dds-radius-md);
}

/* Spinner — indeterminate progress. Inherits color via currentColor so it
 * reads correctly on any surface (inside a primary button it spins in
 * on-brand). The rotation is a continuous state indicator, not a transition,
 * so its period is not a motion token; under reduced motion it slows down
 * instead of freezing (a stopped spinner reads as a hang — the motion IS the
 * state, spec §3.6). Standalone spinners need role="status" + aria-label. */

.dds-spinner {
  display: inline-block;
  flex: none;
  width: var(--dds-space-16);
  height: var(--dds-space-16);
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: var(--dds-radius-full);
  animation: dds-spin 800ms linear infinite;
}

.dds-spinner--lg {
  width: var(--dds-space-24);
  height: var(--dds-space-24);
}

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

@media (prefers-reduced-motion: reduce) {
  .dds-spinner {
    animation-duration: 1600ms;
  }
}

/* Skeleton — loading placeholder. The fill is an alpha tint (spec §3.1:
 * overlays/tints are alpha-only) so it sits naturally on default and subtle
 * backgrounds in both themes. Size is the consumer's job (width/height on
 * the element); --text matches a line of the surrounding font. Mark the
 * loading region with aria-busy="true" and keep skeletons aria-hidden. */

.dds-skeleton {
  display: block;
  background: var(--dds-color-overlay-pressed-tint);
  border-radius: var(--dds-radius-sm);
  animation: dds-skeleton-pulse 1600ms var(--dds-motion-easing-standard) infinite;
}

.dds-skeleton--text {
  height: 1em;
}

.dds-skeleton--circle {
  border-radius: var(--dds-radius-full);
}

@keyframes dds-skeleton-pulse {
  50% {
    opacity: 0.4;
  }
}

@media (prefers-reduced-motion: reduce) {
  .dds-skeleton {
    animation: none;
  }
}

/* Divider — a rule between sections. Use <hr class="dds-divider"> when the
 * split is meaningful; when it is purely decorative next to text that already
 * separates the groups, add role="presentation".
 * The vertical variant needs a stretchable parent (flex row / grid cell). */

.dds-divider {
  border: none;
  border-top: 1px solid var(--dds-color-border-default);
  margin: var(--dds-space-16) 0;
}

.dds-divider--vertical {
  width: 0;
  align-self: stretch;
  border-top: none;
  border-left: 1px solid var(--dds-color-border-default);
  margin: 0 var(--dds-space-16);
}

/* Card — a grouped surface. Border + elevated background carry the grouping;
 * elevation.1 is the lift. The same shadow token ships in both themes (as it
 * does for Dialog and Toast): on a zinc.950 page it resolves to almost
 * nothing, which is the intended dark look — a component may not branch on
 * theme (spec §3.1: dark is a mapping swap, never an `if (dark)`).
 *
 * --subtle is the flat variant for cards inside an already elevated surface
 * (a card in a dialog), where a second shadow reads as noise. */

.dds-card {
  background: var(--dds-color-bg-elevated);
  color: var(--dds-color-text-primary);
  font-family: var(--dds-font-family-sans);
  border: 1px solid var(--dds-color-border-default);
  border-radius: var(--dds-radius-lg);
  box-shadow: var(--dds-elevation-1);
  padding: var(--dds-space-16);
}

.dds-card--subtle {
  background: var(--dds-color-bg-subtle);
  box-shadow: none;
}

.dds-card__title {
  margin: 0 0 var(--dds-space-4);
  font-size: var(--dds-typo-title-3-font-size);
  line-height: var(--dds-typo-title-3-line-height);
  font-weight: var(--dds-typo-title-3-font-weight);
}

.dds-card__body {
  margin: 0;
  font-size: var(--dds-typo-body-2-font-size);
  line-height: var(--dds-typo-body-2-line-height);
  font-weight: var(--dds-typo-body-2-font-weight);
  color: var(--dds-color-text-secondary);
}

/* ListRow — one item in a list: leading slot, body (title + sub), trailing
 * slot. House rule this encodes: rows are self-contained and rich — if the
 * information fits in the row, it does not get deferred to a detail page.
 *
 * --interactive is for rows that are themselves a button/link (use a real
 * <button> or <a> so keyboard and AT get it for free). The focus ring is
 * inset because a full-bleed row has no outer margin to draw into. */

.dds-listrow {
  display: flex;
  align-items: center;
  gap: var(--dds-space-12);
  width: 100%;
  padding: var(--dds-space-12) var(--dds-space-16);
  background: none;
  border: none;
  text-align: start;
  font-family: var(--dds-font-family-sans);
  color: var(--dds-color-text-primary);
}

.dds-listrow + .dds-listrow {
  border-top: 1px solid var(--dds-color-border-default);
}

.dds-listrow__body {
  flex: 1;
  min-width: 0;
}

/* display:block so the two lines stack whether the markup uses <p> or <span>
 * (a <span> title and sub otherwise render on one line) */
.dds-listrow__title {
  display: block;
  margin: 0;
  font-size: var(--dds-typo-body-2-font-size);
  line-height: var(--dds-typo-body-2-line-height);
  font-weight: var(--dds-typo-label-font-weight);
  /* long titles truncate rather than reflowing the row height */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dds-listrow__sub {
  display: block;
  margin: 0;
  font-size: var(--dds-typo-caption-font-size);
  line-height: var(--dds-typo-caption-line-height);
  color: var(--dds-color-text-secondary);
}

.dds-listrow__actions {
  display: flex;
  align-items: center;
  gap: var(--dds-space-4);
  flex: none;
}

/* interactive ------------------------------------------------------------- */

.dds-listrow--interactive {
  cursor: pointer;
  transition: background-color var(--dds-motion-duration-fast) var(--dds-motion-easing-standard);
}
.dds-listrow--interactive:not([disabled]):hover {
  background: var(--dds-color-overlay-hover-tint);
}
.dds-listrow--interactive:not([disabled]):active {
  background: var(--dds-color-overlay-pressed-tint);
}
.dds-listrow--interactive:focus-visible {
  outline: var(--dds-space-2) solid var(--dds-color-border-focus);
  outline-offset: calc(var(--dds-space-2) * -1);
}

/* opacity is allowed for disabled only (spec §4.2) */
.dds-listrow[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

/* Tabs — ink-underline tabs (the shell the admin surfaces converged on).
 * Markup is the ARIA tabs pattern: .dds-tabs[role="tablist"] >
 * .dds-tab[role="tab"][aria-selected] each controlling a [role="tabpanel"].
 * Selection is aria-selected, never a class alone — the state has to be
 * announced, and color is not the only signal (the underline carries it too).
 *
 * The button border-radius reset is deliberate: a shared radius reset leaking
 * into the underline bows its ends — this house has shipped that bug once. */

.dds-tabs {
  display: flex;
  gap: var(--dds-space-4);
  border-bottom: 1px solid var(--dds-color-border-default);
  font-family: var(--dds-font-family-sans);
  overflow-x: auto;
  scrollbar-width: none;
}
.dds-tabs::-webkit-scrollbar {
  display: none;
}

.dds-tab {
  appearance: none;
  flex: none;
  padding: var(--dds-space-12);
  background: none;
  border: none;
  border-radius: 0;
  border-bottom: var(--dds-space-2) solid transparent;
  /* the underline sits on the tablist's own 1px rule */
  margin-bottom: -1px;
  font-family: inherit;
  font-size: var(--dds-typo-label-font-size);
  line-height: var(--dds-typo-label-line-height);
  font-weight: var(--dds-typo-label-font-weight);
  color: var(--dds-color-text-secondary);
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--dds-motion-duration-fast) var(--dds-motion-easing-standard);
}

.dds-tab:not([disabled]):hover {
  color: var(--dds-color-text-primary);
}
.dds-tab:focus-visible {
  outline: var(--dds-space-2) solid var(--dds-color-border-focus);
  outline-offset: calc(var(--dds-space-2) * -1);
}

.dds-tab[aria-selected="true"] {
  color: var(--dds-color-text-brand);
  border-bottom-color: var(--dds-color-bg-brand);
}

/* opacity is allowed for disabled only (spec §4.2) */
.dds-tab[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

/* Dialog — overlay + panel. Two markup shapes are supported:
 *   1. div pattern: .dds-dialog-overlay > .dds-dialog[role="dialog"][aria-modal="true"]
 *   2. native <dialog class="dds-dialog"> (backdrop styled via ::backdrop)
 * Behavior contract (spec §5): closes on outside tap and Esc; focus is
 * trapped while open and returns to the opener on close — that is the
 * opener JS's job, this file only owns the look. Label the panel with
 * aria-labelledby pointing at the title. */

.dds-dialog-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  place-items: center;
  padding: var(--dds-space-16);
  background: var(--dds-color-overlay-dim);
}

.dds-dialog {
  background: var(--dds-color-bg-elevated);
  color: var(--dds-color-text-primary);
  font-family: var(--dds-font-family-sans);
  border-radius: var(--dds-radius-lg);
  box-shadow: var(--dds-elevation-3);
  padding: var(--dds-space-20);
  width: 100%;
  max-width: 400px; /* layout default, not a token — override per product */
}

/* native <dialog> resets */
dialog.dds-dialog {
  border: none;
  margin: auto;
}
dialog.dds-dialog::backdrop {
  background: var(--dds-color-overlay-dim);
}

.dds-dialog__title {
  margin: 0 0 var(--dds-space-8);
  font-size: var(--dds-typo-title-2-font-size);
  line-height: var(--dds-typo-title-2-line-height);
  font-weight: var(--dds-typo-title-2-font-weight);
}

.dds-dialog__body {
  margin: 0 0 var(--dds-space-16);
  font-size: var(--dds-typo-body-2-font-size);
  line-height: var(--dds-typo-body-2-line-height);
  font-weight: var(--dds-typo-body-2-font-weight);
  color: var(--dds-color-text-secondary);
}

.dds-dialog__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--dds-space-8);
}

/* Toast — transient notice. Region markup:
 *   .dds-toast-region[role="status"] (polite) — use role="alert" per-toast
 *   only for danger. Toasts stack bottom-end; entry animates with
 *   duration.base + easing.enter and respects reduced motion. The status
 *   dot repeats the tone so color is not the only signal (spec §6) — same
 *   6px dot as the badge. */

.dds-toast-region {
  position: fixed;
  z-index: 1100;
  inset-block-end: var(--dds-space-16);
  inset-inline-end: var(--dds-space-16);
  display: grid;
  gap: var(--dds-space-8);
  justify-items: end;
  pointer-events: none;
}

.dds-toast {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: var(--dds-space-8);
  background: var(--dds-color-bg-elevated);
  color: var(--dds-color-text-primary);
  font-family: var(--dds-font-family-sans);
  font-size: var(--dds-typo-body-2-font-size);
  line-height: var(--dds-typo-body-2-line-height);
  font-weight: var(--dds-typo-body-2-font-weight);
  border: 1px solid var(--dds-color-border-default);
  border-radius: var(--dds-radius-md);
  box-shadow: var(--dds-elevation-2);
  padding: var(--dds-space-12) var(--dds-space-16);
  max-width: min(360px, calc(100vw - var(--dds-space-32)));
  animation: dds-toast-in var(--dds-motion-duration-base) var(--dds-motion-easing-enter);
}

.dds-toast--success::before,
.dds-toast--warning::before,
.dds-toast--danger::before,
.dds-toast--info::before {
  content: "";
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: var(--dds-radius-full);
}
.dds-toast--success::before {
  background: var(--dds-color-status-success);
}
.dds-toast--warning::before {
  background: var(--dds-color-status-warning);
}
.dds-toast--danger::before {
  background: var(--dds-color-status-danger);
}
.dds-toast--info::before {
  background: var(--dds-color-status-info);
}

@keyframes dds-toast-in {
  from {
    opacity: 0;
    transform: translateY(var(--dds-space-8));
  }
}

@media (prefers-reduced-motion: reduce) {
  .dds-toast {
    animation: none;
  }
}

/* Tooltip (web only — spec §4.3; on native the same information goes in a
 * label or a sheet). A supplementary hint on an inverse surface, shown on
 * hover AND focus-within so it is reachable from the keyboard.
 *
 * Contract: the tooltip may never carry information that is required to use
 * the control — an icon button still needs its own aria-label, and the bubble
 * is referenced with aria-describedby, not aria-label (a describedby bubble
 * that never opens still reaches AT; a hover-only one does not). */

.dds-tooltip {
  position: relative;
  display: inline-flex;
}

.dds-tooltip__bubble {
  position: absolute;
  bottom: calc(100% + var(--dds-space-8));
  left: 50%;
  transform: translateX(-50%);
  z-index: 1200; /* above dialog (1000) and toast (1100) */
  padding: var(--dds-space-4) var(--dds-space-8);
  border-radius: var(--dds-radius-sm);
  background: var(--dds-color-bg-inverse);
  color: var(--dds-color-text-on-inverse);
  font-family: var(--dds-font-family-sans);
  font-size: var(--dds-typo-caption-font-size);
  line-height: var(--dds-typo-caption-line-height);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dds-motion-duration-fast) var(--dds-motion-easing-standard);
}

.dds-tooltip:hover .dds-tooltip__bubble,
.dds-tooltip:focus-within .dds-tooltip__bubble {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .dds-tooltip__bubble {
    transition: none;
  }
}

/* EmptyState — what a list shows when it has nothing yet. House rule: the
 * empty view keeps the same skeleton as the filled one (summary chips and
 * filters stay put) — the empty state replaces the rows, not the screen.
 *
 * Layout note for consumers: give the element `display: none`'s job to
 * [hidden] and never override it with a display rule that lacks a
 * `[hidden]` guard — a shared .empty that did exactly that once painted
 * "no results" on top of a populated table. */

.dds-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--dds-space-8);
  padding: var(--dds-space-40) var(--dds-space-24);
  border: 1px dashed var(--dds-color-border-strong);
  border-radius: var(--dds-radius-lg);
  text-align: center;
  font-family: var(--dds-font-family-sans);
  color: var(--dds-color-text-secondary);
}

.dds-empty[hidden] {
  display: none;
}

.dds-empty__title {
  margin: 0;
  font-size: var(--dds-typo-title-3-font-size);
  line-height: var(--dds-typo-title-3-line-height);
  font-weight: var(--dds-typo-title-3-font-weight);
  color: var(--dds-color-text-primary);
}

.dds-empty__desc {
  margin: 0;
  max-width: 36em;
  font-size: var(--dds-typo-body-2-font-size);
  line-height: var(--dds-typo-body-2-line-height);
}

.dds-empty__actions {
  display: flex;
  gap: var(--dds-space-8);
  margin-top: var(--dds-space-8);
}
