/* TestDossier — Primitives. The reusable component layer that makes surfaces
   feel like one product. Consumes styles/foundation.css tokens.
   See docs/DESIGN-SYSTEM.md.

   This is the TARGET implementation of the shared components. It is NOT yet
   linked into any page (Phase 0). During migration it is linked AFTER the app's
   inline <style> and the superseded inline rules are deleted page-by-page, so
   .btn / .field / .empty-state converge onto these canonical definitions. */

/* ════════════════════════ Button — variant × size ════════════════════════ */
/* Size and intent are ORTHOGONAL: a variant never changes box metrics. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3);          /* size: default */
  font: inherit; font-size: var(--text-ui-base); font-weight: var(--fw-semibold);
  line-height: 1.2; letter-spacing: -0.005em; white-space: nowrap;
  border: 1px solid transparent; border-radius: var(--radius-control);
  cursor: pointer; user-select: none;
  transition: background .12s, border-color .12s, color .12s, transform .06s;
}
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn:active:not(:disabled) { transform: translateY(0.5px); }
.btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-ring); }

/* Size axis — the only place padding/font-size live for buttons. */
.btn-sm { padding: var(--space-1) var(--space-2); font-size: var(--text-ui-sm); }

/* Variant axis — color only, no metrics. */
.btn-primary   { background: var(--primary); color: #fff;
                 box-shadow: 0 0 0 1px rgba(0,0,0,.06) inset, var(--shadow-sm); }
.btn-primary:hover:not(:disabled)   { background: var(--primary-hover); }
.btn-secondary { background: var(--panel); color: var(--text);
                 border-color: var(--border-strong); font-weight: var(--fw-medium); }
.btn-secondary:hover:not(:disabled) { background: var(--step-bg); }
.btn-danger    { background: var(--danger); color: #fff; box-shadow: var(--shadow-sm); }
.btn-danger:hover:not(:disabled)    { background: var(--danger-hover); }
/* Quiet destructive — for destructive SECONDARY actions so they don't scream
   beside a primary. The loud filled red is reserved for a confirm's main CTA. */
.btn-danger-soft { background: var(--danger-soft); color: var(--danger); border-color: transparent; }
.btn-danger-soft:hover:not(:disabled) { background: var(--danger); color: #fff; }
.btn-ghost     { background: transparent; color: var(--muted); font-weight: var(--fw-medium); }
.btn-ghost:hover:not(:disabled)     { background: var(--step-bg); color: var(--text); }

/* State: toggle-on (e.g. Compact). */
.btn-active {
  background: color-mix(in srgb, var(--primary) 14%, transparent);
  color: var(--primary);
  border-color: color-mix(in srgb, var(--primary) 38%, transparent);
}

/* Icon button — matches the app's existing contract (32x32, --radius, soft
   focus ring). Primitives.css loads after the inline <style>, so this rule
   would otherwise override the app's intentional dimensions and the soft
   ring required by the DSC button contract. Keep these in sync. */
.btn-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; padding: 0;
  background: transparent; color: var(--muted);
  border: 1px solid transparent; border-radius: var(--radius);
  cursor: pointer; transition: background .12s, color .12s;
}
.btn-icon:hover { background: var(--step-bg); color: var(--text); }
.btn-icon:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-ring); }

/* ════════════════════════════ Form control ═══════════════════════════════ */
/* Scoped to actual form elements only. The app's pre-existing `.field` is a
   <div> wrapper meaning "form row spacing" (margin-bottom + label styling);
   if the primitive's input chrome applied to those wrappers, every form row
   would render as a nested input-shaped box. Existing inputs / textareas /
   selects in the app don't carry the .field class today, so this primitive
   is opt-in for new form controls that explicitly want the shared treatment. */
input.field, textarea.field, select.field {
  width: 100%; font: inherit; font-size: var(--text-ui); color: var(--text);
  background: var(--input-bg); border: 1px solid var(--border);
  border-radius: var(--radius-control); padding: var(--space-2) var(--space-3);
  transition: border-color .12s, box-shadow .12s, background .12s;
}
input.field:hover, textarea.field:hover, select.field:hover { border-color: var(--border-strong); }
input.field:focus-visible, input.field:focus,
textarea.field:focus-visible, textarea.field:focus,
select.field:focus-visible, select.field:focus {
  outline: none; border-color: var(--accent, var(--primary));
  box-shadow: 0 0 0 3px var(--accent-ring);
}
input.field::placeholder, textarea.field::placeholder { color: var(--muted); }
input.field:disabled, textarea.field:disabled, select.field:disabled { opacity: .55; cursor: not-allowed; }
/* Selects carry the shared chevron. */
select.field {
  appearance: none; -webkit-appearance: none; cursor: pointer;
  padding-right: var(--space-8);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23667085' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--space-3) center;
}

/* ═══════════════════════════ State blocks ════════════════════════════════ */
.empty-state, .loading-state, .error-state {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-2); text-align: center;
  padding: var(--space-8) var(--space-4);
  color: var(--muted); font-size: var(--text-ui);
}
.empty-state__title { color: var(--text); font-weight: var(--fw-semibold); font-size: var(--text-ui); margin: 0; }
.empty-state__icon { color: var(--text-4); margin-bottom: var(--space-1); }
/* Reset native <p> margins so body copy respects the parent flex `gap`
   instead of stacking 1em browser defaults on top of it. Same reason
   .empty-state__title gets margin: 0 — primitives own their rhythm. */
.empty-state p { margin: 0; line-height: var(--lh-base, 1.5); }
.empty-state__actions {
  display: flex; align-items: center; justify-content: center; gap: var(--space-2);
  flex-wrap: wrap; margin-top: var(--space-1);
}
/* Compact variant for modal-list contexts where flex centering would
   balloon a fixed-height parent. Sits at the top with smaller padding;
   inherits color / text-align / font-size from the base rule. .error-state
   is already compact (12px 16px row layout) so it needs no inline variant. */
.empty-state--inline, .loading-state--inline {
  display: block;
  padding: var(--space-5);
}
.empty-state--inline .empty-state__actions { margin-top: var(--space-3); }
/* Ultra-compact variant for floating menus / dropdowns where padding must
   match the surrounding item rows (e.g. .tj-mention-ac, .project-menu).
   Smaller font too so a 20px-padded message doesn't dwarf 6-8px items. */
.empty-state--menu, .loading-state--menu {
  display: block;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-ui-sm);
}
/* Placeholder-card chrome (dashed border + subtle bg). Adds chrome only —
   no size or layout override — so it composes with the base (canvas-hero
   card) or with --inline (compact card). Used for "no data here yet"
   slots that should feel like a tangible placeholder, not just text. */
.empty-state--card {
  background: var(--step-bg);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
}

/* ═══════════════════════════ Paywall callout ════════════════════════════ */
/* Visually distinct from .empty-state — the accent tint + solid border
   signals "premium / upgrade" not "no data here". Used when a surface is
   locked behind a higher plan tier. Always pair with an upgrade CTA in
   .td-paywall__actions; the central paywall modal (showPaywall(reason))
   remains the canonical out-of-context paywall for cross-surface gates. */
.td-paywall {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-3); text-align: center;
  padding: var(--space-8) var(--space-4);
  background: color-mix(in srgb, var(--accent, var(--primary)) 5%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent, var(--primary)) 22%, var(--border));
  border-radius: var(--radius-md);
  color: var(--text);
}
.td-paywall__icon {
  color: var(--accent, var(--primary));
  margin-bottom: var(--space-1);
}
.td-paywall__title {
  color: var(--text); font-weight: var(--fw-semibold); font-size: var(--text-ui);
  margin: 0;
}
.td-paywall__body {
  color: var(--muted); font-size: var(--text-ui);
  max-width: 460px; line-height: var(--lh-base, 1.5);
  margin: 0;
}
.td-paywall__actions {
  display: flex; gap: var(--space-2); margin-top: var(--space-1);
  flex-wrap: wrap; justify-content: center;
}
.error-state {
  flex-direction: row; gap: var(--space-2); text-align: left;
  padding: var(--space-3) var(--space-4); border-radius: var(--radius-control);
  background: var(--danger-soft); color: var(--danger); font-size: var(--text-ui-base);
}

/* ═════════════════════════ Layout helpers ════════════════════════════════ */
/* Vertical rhythm without inline margin-bottom. */
.tj-stack { display: flex; flex-direction: column; gap: var(--space-4); }
.tj-stack--2 { gap: var(--space-2); }
.tj-stack--3 { gap: var(--space-3); }
.tj-stack--6 { gap: var(--space-6); }
/* Horizontal row, token gap, wraps. Replaces inline display:flex;gap:Npx. */
.tj-cluster { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.tj-cluster--tight { gap: var(--space-1); }

/* Panel chrome. */
.tj-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-card); box-shadow: var(--shadow-sm);
  padding: var(--space-6);
}

/* The shared list row for case / snapshot / trash lists — one hover, one
   divider, one height across every list canvas. */
.tj-list-row {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
  transition: background .12s;
}
.tj-list-row:last-child { border-bottom: none; }
.tj-list-row:hover { background: var(--step-bg); }
.tj-list-row__main { flex: 1; min-width: 0; }
.tj-list-row__title { font-size: var(--text-ui); color: var(--text); font-weight: var(--fw-medium);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tj-list-row__meta { font-size: var(--text-ui-sm); color: var(--muted); }
.tj-list-row__actions { display: flex; align-items: center; gap: var(--space-2); flex: none; }

/* Labeled content block; danger-zone is the home for destructive secondary
   actions (e.g. "Clear local cache") — a real footer slot, not an <hr> + inline
   <section>. */
.tj-section { padding-top: var(--space-6); }
.tj-section__title { margin: 0 0 var(--space-1); font-size: var(--text-ui); font-weight: var(--fw-semibold); color: var(--text); }
.tj-section__desc { margin: 0 0 var(--space-3); font-size: var(--text-ui-base); color: var(--muted); line-height: var(--lh-base); }
.tj-danger-zone {
  margin-top: var(--space-6); padding-top: var(--space-6);
  border-top: 1px solid var(--border);
}
