/* ================================================================== *
 *  ApniShift — shared component vocabulary (copied from TalentRally components.css)                          *
 *                                                                     *
 *  Every class here is written ONCE and used everywhere. Before this  *
 *  file, `.navbar` existed in 4 sheets, `.spinner` in 6, and badges   *
 *  were forked 5 ways (.badge-*, .status-badge.*, .jd-badge,          *
 *  .iv-round-badge, .status-pill) — which is why two tables built a   *
 *  month apart look like two different products.                      *
 *                                                                     *
 *  Rules for adding to this file:                                     *
 *    - colour comes from tokens.css, never a hex literal;             *
 *    - a class earns its place by being used on 2+ screens;           *
 *    - no screen-specific names (.applicant-row belongs in            *
 *      dashboard.css, .data-table belongs here).                      *
 *                                                                     *
 *  Load order: tokens.css -> ai_interview.css -> dashboard.css ->     *
 *  THIS FILE. It loads last so the consolidated version of a name     *
 *  wins over the older forks it replaces.                             *
 * ================================================================== */

/* ── Typography ───────────────────────────────────────────────────── */

/* The single most-reused device in the system: an 11px uppercase micro
   label. Stat-card labels, section headers, table headers and sidebar
   group labels are all this one class, which is why they agree. */
.kicker {
  font-size: .6875rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted-foreground);
  margin: 0;
}

/* ── Surfaces ─────────────────────────────────────────────────────── */

/* Soft and wide, rather than tight and dark. A tight shadow makes a
   card look like it is hovering; this makes it look like paper. */
.surface-elevated { box-shadow: var(--shadow-lg); }

/* A recessed panel INSIDE a card — sidebar footer, hero tiles. */
.soft-panel {
  border: 1px solid color-mix(in srgb, var(--border) 82%, transparent);
  background: color-mix(in srgb, var(--card) 92%, var(--surface));
  border-radius: var(--radius-lg);
}

/* Replaces the "coloured circle behind an icon" pattern. Hairline
   border + faint fill reads as an instrument rather than decoration. */
.icon-tile {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  flex-shrink: 0;
  border-radius: var(--radius-md);
  border: 1px solid color-mix(in srgb, var(--border) 78%, transparent);
  background: color-mix(in srgb, var(--surface) 62%, var(--card));
  color: var(--foreground);
}
.icon-tile svg { width: 1rem; height: 1rem; }

/* Workspace texture with no image asset: a top fade plus a 42px grid
   built from two 1px gradients. color-mix keeps it theme-aware. */
.app-workspace-bg {
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--surface) 72%, transparent) 0, transparent 22rem),
    linear-gradient(90deg,  color-mix(in srgb, var(--border) 30%, transparent) 1px, transparent 1px),
    linear-gradient(180deg, color-mix(in srgb, var(--border) 26%, transparent) 1px, transparent 1px),
    var(--background);
  background-size: auto, 42px 42px, 42px 42px, auto;
}

/* ── Tables ───────────────────────────────────────────────────────── */

/* A complete table skin. Screens write <table class="data-table"> with
   ZERO per-cell classes — that constraint is what keeps tables
   consistent, because there is nowhere to put a one-off. */
.data-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: .875rem;
}
.data-table thead tr { background: color-mix(in srgb, var(--surface) 58%, transparent); }
.data-table th {
  padding: .875rem 1rem;
  text-align: left;
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted-foreground);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.data-table td {
  padding: .95rem 1rem;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
  vertical-align: middle;
  color: var(--text);
}
.data-table tbody tr { transition: background-color var(--transition-fast); }
.data-table tbody tr:hover { background: color-mix(in srgb, var(--surface) 48%, transparent); }
.data-table tbody tr:last-child td { border-bottom: 0; }

tr.is-clickable { cursor: pointer; }

/* Row selection. The width is pinned because the form styles give every
   <input> width:100% — which is exactly how the filter panel lost its labels. */
.sel-col { width: 36px; padding-right: 0 !important; }
.sel-col input { width: 16px; height: 16px; min-width: 16px; margin: 0; padding: 0; accent-color: var(--primary); cursor: pointer; }
.sel-col input:disabled { cursor: not-allowed; opacity: .45; }

/* Actions on a selection, shown only while something is selected. */
.bulk-bar {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  padding: .55rem 1.35rem;
  background: var(--primary-soft); border-bottom: 1px solid var(--primary-soft-2);
  font-size: .82rem; color: var(--primary-hover);
}
.bulk-bar[hidden] { display: none; }

/* ai_interview.css sets `td { word-break: break-word }` on EVERY table cell,
   which lets a squeezed column break inside a word ("Backe / nd Engin / eer").
   overflow-wrap:break-word still wraps a genuinely unbreakable token, but only
   after whole words have been tried, so a column keeps its longest word. */
.data-table td, .eval-table td { word-break: normal; overflow-wrap: break-word; }

/* A secondary line under a cell's main value (an email under a name). The
   sidebar shell took 240px from the content column, and a long email then
   broke mid-word across three lines — truncate it and keep the full value
   in the title instead. */
.cell-sub { display: flex; align-items: center; gap: .3rem; font-size: .72rem; color: var(--muted); min-width: 0; }
.cell-truncate { display: inline-block; max-width: 13rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
.nowrap { white-space: nowrap; }

/* Numeric columns line up only if the digits are tabular. */
.data-table .num, .tabular { font-variant-numeric: tabular-nums; }

/* Loading / error / empty all render as a full-width row INSIDE the
   table, so the header never jumps and the page never reflows between
   states. See ui.js renderTable(). */
/* Scoped to any table, not just .data-table: the Applicants list still wears
   the older .eval-table skin and must show the same states. */
table .table-state td {
  padding: 3rem 1rem;
  text-align: center;
  color: var(--muted-foreground);
  border-bottom: 0;
}
table .table-state.is-loading td { padding: .9rem 1rem; text-align: left; }
table .table-state.is-error td { color: var(--destructive); }
table .table-state .table-retry { margin-left: .75rem; vertical-align: middle; }

/* ── Empty state ──────────────────────────────────────────────────── */

/* Replaces the two older definitions (ai_interview.css and
   dashboard.css), both now deleted. The DASHED border is the signal: a
   solid box reads as content that failed to load, a dashed box reads as
   a space waiting to be filled.

   NOTE the selectors carry BOTH the existing markup contract
   (.empty-state-icon + <h3>, used in 4 places in index.html) and the
   newer .empty-icon + <h4> shape, so consolidating this class upgrades
   the live screens instead of half-styling them. */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .4rem;
  padding: 3rem 1.5rem;
  text-align: center;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--surface) 30%, transparent);
  color: var(--muted-foreground);
  font-size: .88rem;
}
.empty-state .empty-state-icon,
.empty-state .empty-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3rem; height: 3rem;
  margin: 0 0 .25rem;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--muted-foreground);
}
.empty-state .empty-state-icon svg,
.empty-state .empty-icon svg { width: 1.25rem; height: 1.25rem; color: inherit; }
.empty-state h3,
.empty-state h4 { font-size: .95rem; font-weight: 600; color: var(--fg-strong); margin: 0; }
.empty-state p  { font-size: .85rem; line-height: 1.5; max-width: 24rem; margin: 0 auto; }

/* ── Stat card ────────────────────────────────────────────────────── */

.stat-card {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.25rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  box-shadow: var(--shadow-sm);
}
.stat-card .stat-value {
  font-size: 1.875rem;
  font-weight: 600;
  line-height: 1.15;
  color: var(--fg-strong);
  font-variant-numeric: tabular-nums;
  margin-top: .35rem;
}
.stat-card .stat-desc  { font-size: .75rem; color: var(--muted-foreground); margin-top: .35rem; }
.stat-card .stat-trend { font-size: .75rem; font-weight: 600; margin-top: .35rem; }
.stat-card .stat-trend.up   { color: var(--success); }
.stat-card .stat-trend.down { color: var(--destructive); }

/* ── Status badge ─────────────────────────────────────────────────── */

/* One badge, five variants, driven by the lookup map in ui.js. Replaces
   .badge-*, .status-badge.*, .jd-badge, .iv-round-badge and .status-pill,
   which between them had five different paddings and three type sizes. */
.ui-badge {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  padding: .15rem .5rem;
  border-radius: 999px;
  border: 1px solid transparent;
  font-size: .7rem;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
}
.ui-badge-neutral { background: var(--surface-2);      color: var(--text-soft);        border-color: var(--border); }
.ui-badge-info    { background: var(--primary-soft);   color: var(--primary-hover);    border-color: var(--primary-soft-2); }
.ui-badge-success { background: var(--success-soft);   color: var(--success-strong);   border-color: color-mix(in srgb, var(--success) 25%, transparent); }
.ui-badge-warn    { background: var(--warning-soft);   color: var(--warning-strong);   border-color: color-mix(in srgb, var(--warning) 25%, transparent); }
.ui-badge-danger  { background: var(--destructive-soft); color: var(--destructive-strong); border-color: color-mix(in srgb, var(--destructive) 25%, transparent); }

/* Copy-confirmed state, set for 1.5s by copyWithFeedback(). */
.is-copied { color: var(--success) !important; }

/* ── Filter bar ───────────────────────────────────────────────────── */

/* Controls live in their own strip ABOVE the data card. That
   separation is most of why a filtered list feels ordered rather than
   cluttered — the controls stop competing with the rows. */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .625rem;
  padding: .875rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  box-shadow: var(--shadow-sm);
  margin-bottom: 1rem;
}
/* Search input with a leading icon and a clear button. Not scoped to
   .filter-bar: the Applicants toolbar uses it inside .app-filters. */
.filter-search { position: relative; flex: 1 1 12rem; min-width: 9rem; }
.filter-search input { width: 100%; padding-left: 2.1rem !important; padding-right: 2rem !important; }
.filter-search .search-icon {
  position: absolute; left: .65rem; top: 50%; transform: translateY(-50%);
  width: 1rem; height: 1rem; color: var(--muted-foreground); pointer-events: none;
}
/* The clear affordance exists only when there is something to clear. */
.filter-search .search-clear {
  position: absolute; right: .35rem; top: 50%; transform: translateY(-50%);
  display: none; align-items: center; justify-content: center;
  border: 0; background: none; cursor: pointer;
  color: var(--muted-foreground); padding: .25rem; line-height: 0; border-radius: var(--radius-sm);
}
.filter-search.has-value .search-clear { display: inline-flex; }
.filter-search .search-clear:hover { color: var(--foreground); background: var(--surface-2); }

/* ── Multi-select filter ──────────────────────────────────────────── */

.ms-filter { display: inline-flex; }
.ms-filter .ms-trigger {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .45rem .7rem;
  font-family: inherit; font-size: .8rem; font-weight: 500;
  border: 1px solid var(--input); border-radius: var(--radius-md);
  background: var(--card); color: var(--text); cursor: pointer;
  white-space: nowrap;
  transition: background-color var(--transition-fast), border-color var(--transition-fast);
}
.ms-filter .ms-trigger:hover { background: var(--surface); }
.ms-filter.is-open .ms-trigger { border-color: var(--ring); }
/* The count chip is what lets someone scan which filters are active
   WITHOUT opening any of them. */
.ms-filter .ms-count {
  min-width: 1.1rem; padding: 0 .3rem;
  border-radius: 999px; background: var(--primary); color: var(--primary-foreground);
  font-size: .65rem; font-weight: 700; line-height: 1.5; text-align: center;
}

/* The panel is appended to <body> and positioned fixed by ui.js, because
   every .section-card is overflow:hidden and would clip it. */
.ms-panel {
  display: none; position: fixed; z-index: 9000;
  min-width: 13rem; max-width: 21rem; max-height: 20rem; overflow-y: auto;
  border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--card); box-shadow: var(--shadow-lg); padding: .25rem;
}
.ms-panel .ms-opt {
  display: flex; align-items: center; gap: .5rem; width: 100%;
  padding: .4rem .5rem; border: 0; border-radius: var(--radius-sm);
  background: none; font-family: inherit; font-size: .8rem; color: var(--text);
  text-align: left; cursor: pointer;
}
.ms-panel .ms-opt span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ms-panel .ms-opt:hover { background: var(--surface-2); }
/* The form styles give every <input> width:100%. In a flex row a checkbox then
   takes the whole line and squeezes its label to 0px, where overflow:hidden
   hides the text — this panel first rendered as a column of bare checkboxes
   (measured over DevTools: input 164.5px, span 0px). Same bug class as commit
   6bbb7d3. Pin the box to its natural size and give the text the rest. */
.ms-panel .ms-opt input {
  width: 16px; height: 16px; min-width: 16px; flex: none;
  margin: 0; padding: 0; border: none; accent-color: var(--primary);
}
.ms-panel .ms-opt span { flex: 1 1 auto; min-width: 0; }
.ms-panel .ms-all   { border-bottom: 1px solid var(--border); border-radius: 0; margin-bottom: .25rem; font-weight: 600; }
.ms-panel .ms-clear { border-top: 1px solid var(--border); border-radius: 0; margin-top: .25rem;
                      color: var(--destructive); font-weight: 600; }
.ms-panel .ms-none  { padding: .6rem .5rem; font-size: .78rem; color: var(--muted-foreground); }

/* ── Sortable header ──────────────────────────────────────────────── */

th[data-sort] { cursor: pointer; user-select: none; }
th[data-sort] .sort-arrow { display: inline-block; margin-left: .25rem; font-size: .6rem; color: var(--border-strong); }
th[data-sort][aria-sort="ascending"] .sort-arrow,
th[data-sort][aria-sort="descending"] .sort-arrow { color: var(--primary); }

/* ── Pagination ───────────────────────────────────────────────────── */

/* Sits INSIDE the card below the table, separated by a hairline — it
   belongs to the table, not to the page. */
.pager {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: .75rem;
  padding: .75rem 1rem;
  border-top: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
  font-size: .8rem; color: var(--muted-foreground);
}
.pager .pager-right { display: flex; align-items: center; gap: .5rem; }
.pager button {
  padding: .35rem .7rem; font-size: .8rem;
  border: 1px solid var(--input); border-radius: var(--radius-md);
  background: var(--card); color: var(--text); cursor: pointer;
}
.pager button:disabled { opacity: .45; cursor: not-allowed; }
.pager button:not(:disabled):hover { background: var(--surface); }
.pager select {
  padding: .3rem .4rem; font-size: .8rem;
  border: 1px solid var(--input); border-radius: var(--radius-md); background: var(--card);
}

/* ── Skeleton ─────────────────────────────────────────────────────── */

/* A skeleton says "this row is coming"; a spinner says "something is
   happening somewhere". Use this wherever the shape is known. */
.skeleton {
  background: linear-gradient(90deg,
    var(--surface-2) 25%,
    color-mix(in srgb, var(--surface-2) 55%, var(--card)) 37%,
    var(--surface-2) 63%);
  background-size: 400% 100%;
  border-radius: var(--radius-sm);
  animation: tr-skeleton 1.4s ease-in-out infinite;
}
@keyframes tr-skeleton { 0% { background-position: 100% 50% } 100% { background-position: 0 50% } }

/* ── Stage pipeline ───────────────────────────────────────────────── */

/* A whole status timeline in ~100px: dots joined by hairlines, done
   ones inked, pending ones faded. Each step carries a title so the
   meaning survives without a legend. */
.stage-pipeline { display: inline-flex; align-items: center; gap: 0; }
.stage-pipeline .stage {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.1rem; height: 1.1rem; border-radius: 999px;
  border: 1.5px solid currentColor; color: var(--muted);
  font-size: .55rem; font-weight: 700;
}
.stage-pipeline .stage.done { color: var(--success); background: var(--success-soft); }
.stage-pipeline .stage.current { color: var(--primary); background: var(--primary-soft); }
.stage-pipeline .stage-link { width: 1rem; height: 1px; background: var(--border-strong); }

/* ── Score ring ───────────────────────────────────────────────────── */

.score-ring { position: relative; display: inline-flex; }
.score-ring svg { transform: rotate(-90deg); }
.score-ring .ring-track { stroke: var(--surface-2); }
.score-ring .ring-fill  { stroke: var(--primary); transition: stroke-dasharray var(--transition-base); }
.score-ring .ring-label {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  font-weight: 600; color: var(--fg-strong); font-variant-numeric: tabular-nums;
}

/* ── Switch ───────────────────────────────────────────────────────── */

/* A real checkbox (role="switch") under a styled track, so keyboard, screen
   readers and form semantics all work without extra script. */
.switch { position: relative; display: inline-flex; flex-shrink: 0; width: 38px; height: 22px; cursor: pointer; }
.switch input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; z-index: 1; }
.switch-track { position: absolute; inset: 0; border-radius: 999px; background: var(--border-strong); transition: background-color var(--transition-fast); }
.switch-track::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%;
  background: #fff; box-shadow: var(--shadow-sm); transition: transform var(--transition-fast);
}
.switch input:checked + .switch-track { background: var(--success); }
.switch input:checked + .switch-track::after { transform: translateX(16px); }
.switch input:focus-visible + .switch-track { outline: 2px solid var(--ring); outline-offset: 2px; }
.switch input:disabled + .switch-track { opacity: .55; }

/* ── Accessibility ────────────────────────────────────────────────── */

/* Focus is a system-level guarantee, not a per-component decision.
   The dashboard shipped with 4 aria- attributes in 2,035 lines; this
   at least makes keyboard navigation visible everywhere. */
:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (prefers-reduced-motion: reduce) {
  .skeleton { animation-duration: 1ms; }
}

/* Busy spinner for icon-only buttons (setBusy in ui.js). */
@keyframes ui-spin { to { transform: rotate(360deg); } }
.ui-spin { animation: ui-spin .8s linear infinite; }
@media (prefers-reduced-motion: reduce) { .ui-spin { animation-duration: 2.4s; } }
