/* Note (haohui): pages are built from Potato's own components; this file only
   adds layout, responsive rules and the states Potato lacks, never a colour,
   font, shadow or radius of its own. */

/* Note (haohui): Potato's mono stack names ui-monospace and SFMono-Regular,
   which only Safari resolves; Chrome on macOS and Windows fell through to
   Courier, unreadable for 10-character codes at 13px. Menlo and Consolas are
   the system monospace fonts the stack already means. */
:root {
  --font-mono: ui-monospace, 'SFMono-Regular', Menlo, Consolas, 'Fira Code', monospace;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
}

[hidden] {
  display: none !important;
}

.me-1 {
  margin-right: 0.25rem;
}

.vx-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;
}

.vx-muted {
  color: var(--muted-foreground);
}

.vx-shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.vx-shell > main {
  flex: 1 0 auto;
  width: 100%;
}

.admin-main.vx-main--wide {
  max-width: 96rem;
  padding: 1rem 1.5rem;
}

.admin-main.vx-main--narrow {
  max-width: 56rem;
}

.admin-main.vx-main--reading {
  max-width: 48rem;
}

.vx-shell > .potato-footer {
  margin-top: 0;
}

.vx-skip {
  position: absolute;
  left: -9999px;
  top: 0;
}

.vx-skip:focus {
  position: fixed;
  left: 1rem;
  top: 0.5rem;
  z-index: 100;
  display: inline-flex;
  align-items: center;
  height: 2.75rem;
  padding: 0 1.25rem;
  border: 1px solid var(--primary);
  border-radius: var(--radius);
  background: var(--primary);
  color: var(--primary-foreground);
  font-weight: 500;
  text-decoration: none;
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}

.admin-header.vx-header {
  display: block;
  padding: 0;
  text-align: left;
}

.vx-header-inner {
  max-width: 1400px;
  margin: 0 auto;
  padding: 0 2rem;
  min-height: 56px;
  display: flex;
  align-items: center;
  gap: 1.5rem;
}

.vx-brand {
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--primary);
  text-decoration: none;
  white-space: nowrap;
}

/* Note (haohui): with three or more sections the tabs outgrow the room between
   brand and account menu, so the nav shrinks and scrolls sideways instead of
   pushing the account trigger, the only way to 退出登录, off the page. A
   scroller clips what hangs out of it, so the nav itself, not each tab, takes
   the -1px that lays the active underline over the header's bottom border. */
.vx-nav {
  align-self: stretch;
  min-width: 0;
  margin: 0 0 -1px;
  padding: 0;
  border-bottom: 0;
  gap: 0.25rem;
  overflow-x: auto;
  scrollbar-width: none;
}

.vx-nav::-webkit-scrollbar {
  display: none;
}

/* Note (haohui): voxlabel.js sets is-overflowing while tabs remain to the right
   and is-scrolled while some lie to the left; with the scrollbar hidden these
   fades are the only sign of the hidden tabs. A nav scrolled to its end at
   780px otherwise hid 任务包 whole, with nothing cut to hint at it. */
.vx-nav {
  --vx-nav-fade-start: 0rem;
  --vx-nav-fade-end: 0rem;
}

.vx-nav.is-scrolled {
  --vx-nav-fade-start: 2rem;
}

.vx-nav.is-overflowing {
  --vx-nav-fade-end: 2rem;
}

.vx-nav.is-scrolled,
.vx-nav.is-overflowing {
  mask-image: linear-gradient(to right, transparent, #000 var(--vx-nav-fade-start), #000 calc(100% - var(--vx-nav-fade-end)), transparent);
}

.vx-nav .potato-tab {
  display: inline-flex;
  align-items: center;
  padding: 0 0.75rem;
  font-size: var(--text-dense);
  text-decoration: none;
  white-space: nowrap;
}

.vx-nav .potato-tab:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: -2px;
}

.vx-account {
  margin-left: auto;
  position: relative;
}

.vx-account-trigger {
  list-style: none;
  display: flex;
  gap: 0.5rem;
  align-items: center;
  min-height: 44px;
  padding: 0 0.5rem;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: var(--text-dense);
  color: var(--text-color);
  transition: var(--transition);
}

.vx-account-trigger::-webkit-details-marker {
  display: none;
}

.vx-account-trigger:hover,
.vx-account[open] > .vx-account-trigger {
  background: var(--muted);
}

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

.vx-account-trigger .fa-chevron-down {
  font-size: var(--text-xs);
  color: var(--muted-foreground);
}

.vx-account-name {
  max-width: 12rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
  color: var(--heading-color);
}

.vx-account-role {
  font-size: var(--text-xs);
  color: var(--muted-foreground);
  white-space: nowrap;
}

.vx-account-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 4px);
  min-width: 15rem;
  width: max-content;
  max-width: min(20rem, calc(100vw - 2rem));
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--box-shadow-lg);
  padding: 0.5rem;
  z-index: 20;
}

.vx-account-who {
  display: flex;
  flex-direction: column;
  padding: 0.5rem 0.75rem 0.25rem;
  overflow-wrap: anywhere;
}

.vx-account-who strong {
  font-weight: 600;
  color: var(--heading-color);
}

.vx-account-who span {
  font-size: var(--text-xs);
  color: var(--muted-foreground);
}

.vx-account-roles {
  list-style: none;
  margin: 0 0 0.375rem;
  padding: 0 0.75rem 0.5rem;
  border-bottom: 1px solid var(--border);
  font-size: var(--text-xs);
  color: var(--muted-foreground);
}

.vx-account-menu form {
  margin: 0;
}

.vx-account-item {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  width: 100%;
  min-height: 44px;
  padding: 0.6rem 0.75rem;
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--text-dense);
  color: var(--text-color);
  text-align: left;
  text-decoration: none;
  border-radius: var(--radius);
  cursor: pointer;
}

.vx-account-item:hover {
  background: var(--muted);
}

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

.vx-account-item i {
  width: 1rem;
  color: var(--muted-foreground);
}

.vx-flashes {
  margin-bottom: 1.5rem;
}

.vx-flashes > .potato-alert:last-child {
  margin-bottom: 0;
}

/* Note (haohui): Potato draws the words in --destructive, a fill colour that
   reads at about 3.3:1 on the pink tint; the icon keeps the bright red. */
.potato-alert-error {
  color: var(--destructive-text);
}

.potato-alert-error > i {
  color: var(--destructive);
}

.vx-credential p {
  margin: 0;
}

.vx-credential .vx-credential-secret {
  margin-top: 0.5rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  font-family: var(--font-mono);
  font-size: var(--text-md);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  color: var(--heading-color);
  overflow-wrap: anywhere;
}

.vx-credential-secret .shadcn-button {
  font-family: var(--font-sans);
}

.vx-credential .vx-hint {
  margin-top: 0.375rem;
  color: inherit;
  font-size: var(--text-sm);
}

.vx-page-head {
  margin-bottom: 1.5rem;
}

.vx-page-head-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 1rem;
  flex-wrap: wrap;
}

.vx-page-head-text {
  min-width: 0;
}

.vx-page-head h1 {
  font-size: 1.5rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--heading-color);
  margin: 0;
  overflow-wrap: anywhere;
}

/* Note (haohui): each fact carries its own separator in a 1rem left padding
   that a -1rem margin pulls into the gap; the fact that starts a wrapped line
   pushes that padding past the clipped left edge, so no line opens with a dot. */
.vx-page-meta {
  margin: 0.375rem 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 1rem;
  overflow: hidden;
  font-size: var(--text-sm);
  color: var(--muted-foreground);
}

.vx-page-meta > span:not(.status-badge) {
  position: relative;
  max-width: calc(100% + 1rem);
  margin-left: -1rem;
  padding-left: 1rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.vx-page-meta > span:not(.status-badge)::before {
  content: '·';
  position: absolute;
  left: 0;
  width: 1rem;
  text-align: center;
}

.vx-page-actions {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}

.vx-crumbs {
  font-size: var(--text-sm);
  color: var(--muted-foreground);
}

.vx-crumbs ol {
  display: flex;
  flex-wrap: wrap;
  list-style: none;
  padding: 0;
  margin: 0 0 0.5rem;
  font-size: var(--text-sm);
  color: var(--muted-foreground);
}

.vx-crumbs li {
  max-width: 16rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.vx-crumbs li + li::before {
  content: '/';
  margin: 0 0.4rem;
  color: var(--border);
}

.vx-crumbs a {
  color: var(--primary);
  text-decoration: none;
}

.vx-crumbs a:hover {
  text-decoration: underline;
}

.vx-crumbs-back {
  display: none;
}

.dashboard-grid.vx-stat-row {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.vx-stat h3 {
  text-transform: none;
  letter-spacing: 0;
}

.vx-stat .value {
  font-size: 1.5rem;
  font-variant-numeric: tabular-nums;
}

/* Note (haohui): a description made of several counts wraps between counts,
   never inside one; at 800px a plain string left one character on its own line. */
.vx-stat-count {
  white-space: nowrap;
}

/* Note (haohui): at 320px a stat card is about 106px inside, less than the
   8rem progress minimum, so the caption ran past the card; let it drop under. */
.vx-stat .vx-progress {
  margin-top: 0.5rem;
  min-width: 0;
  flex-wrap: wrap;
  row-gap: 0.25rem;
}

.vx-stat .vx-progress .pbar-track {
  flex: 1 1 3rem;
}

.vx-stat .vx-progress .pbar-caption {
  margin-left: auto;
}

/* Note (haohui): the card's value already is the percentage in 1.5rem, so its
   caption keeps only the counts. */
.vx-stat .pbar-pct {
  display: none;
}

/* Note (lty): one person's finished items per day on the progress page; a
   single series, so the card title names it and each day prints its count. */
.vx-daily {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 2px;
  margin: 0.5rem 0 0;
  padding: 0;
  list-style: none;
}

.vx-daily-day {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 0;
}

.vx-daily-track {
  display: flex;
  align-items: flex-end;
  width: 100%;
  max-width: 1.25rem;
  height: 2.5rem;
}

.vx-daily-bar {
  width: 100%;
  min-height: 1px;
  background: var(--primary);
  border-radius: 4px 4px 0 0;
}

.vx-daily-day:not(.is-today) .vx-daily-bar {
  opacity: 0.6;
}

.vx-daily-count {
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
}

.vx-daily-label {
  font-size: var(--text-xs);
  color: var(--muted-foreground);
  white-space: nowrap;
}

a.vx-stat {
  display: block;
  text-decoration: none;
  color: inherit;
  transition: var(--transition);
}

a.vx-stat:hover {
  border-color: var(--primary);
}

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

.vx-stat--attention {
  border-left: 3px solid var(--warning-text);
}

.vx-progress-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.25rem;
  padding: 1rem 1.25rem;
  margin-bottom: 1.5rem;
}

.vx-progress-strip-label {
  font-weight: 600;
  color: var(--heading-color);
  white-space: nowrap;
}

.vx-progress-strip .vx-progress {
  flex: 1 1 18rem;
}

.vx-progress-strip-counts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
}

/* Note (haohui): the strip's caption is the number the strip exists for, so it
   reads at body weight in heading colour instead of the bar's fine print. */
.vx-progress-strip .vx-progress--md .pbar-caption {
  font-size: var(--text-sm);
  color: var(--heading-color);
  font-weight: 600;
}

.pbar-track {
  height: 12px;
  background: var(--border);
  border-radius: 999px;
  overflow: hidden;
  margin: 0.6rem 0 0.25rem;
}

.pbar-fill {
  height: 100%;
  background: var(--primary);
  border-radius: 999px;
  width: 0%;
  transition: width 0.4s ease;
}

.pbar-caption {
  font-size: var(--text-sm);
  color: var(--text-color);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

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

.vx-progress {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 8rem;
}

.vx-progress .pbar-track {
  flex: 1;
  margin: 0;
}

.vx-progress--sm .pbar-track {
  height: 6px;
}

.vx-progress--md {
  flex-wrap: wrap;
  row-gap: 0.25rem;
}

.vx-progress--md .pbar-track {
  flex: 1 1 100%;
}

.vx-progress--md .pbar-caption {
  flex: 1 1 100%;
}

.vx-progress .pbar-caption {
  white-space: nowrap;
  font-size: var(--text-xs);
  color: var(--muted-foreground);
}

/* Note (haohui): a fixed caption width so bars of equal value end at the same
   x down a table column, whatever the digit count. Left-aligned, so the caption
   starts right after its own bar; right-aligned it floated up to 3em away. */
.admin-table .vx-progress .pbar-caption {
  min-width: 7em;
  text-align: left;
}

/* Note (haohui): one person's share, set inline after their name and role tag.
   A fixed track lets the shares of several people on one row line up, and the
   percentage is dropped because "done/total" already fits the narrow slot. */
.vx-progress--xs {
  min-width: 0;
  gap: 0.375rem;
}

.vx-progress--xs .pbar-track {
  flex: 0 0 4.5rem;
  height: 4px;
}

.vx-progress--xs .pbar-pct {
  display: none;
}

.admin-table .vx-progress--xs .pbar-caption {
  min-width: 0;
}

.vx-sep {
  margin: 0 0.25rem;
}

.vx-progress.is-done .pbar-fill {
  background: var(--success-color);
}

.vx-tabs {
  margin-bottom: 1.5rem;
}

.vx-tabs .admin-tab {
  text-decoration: none;
  white-space: nowrap;
}

.vx-tab-count {
  margin-left: 0.35rem;
  font-size: var(--text-sm);
  opacity: 1;
  color: inherit;
  vertical-align: baseline;
  font-variant-numeric: tabular-nums;
}

.vx-chips {
  display: flex;
  gap: 0.375rem;
  flex-wrap: wrap;
}

.vx-chip {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  white-space: nowrap;
  color: var(--text-color);
}

/* Note (haohui): an active filter is the tinted outline of tr[aria-current] and
   potato-alert-info, not Potato's filled .pagination-button.active, so it never
   looks like the filled tab above it or the primary row buttons below it. */
.pagination-button.vx-chip.active,
.pagination-button.vx-chip.active:hover {
  background: rgba(110, 86, 207, 0.08);
  color: var(--primary);
  border-color: var(--primary);
  font-weight: 600;
}

.vx-chip-count {
  margin-left: 0.25rem;
  font-variant-numeric: tabular-nums;
  opacity: 0.8;
}

.table-controls > .shadcn-button {
  margin-left: auto;
}

/* Note (haohui): a bar that holds only a create opener empties while voxlabel.js
   hides that opener for the open panel; the empty bar would leave a gap above it. */
.table-controls:has(> [data-vx-open][hidden]:only-child) {
  display: none;
}

.admin-table-container {
  overflow-x: auto;
}

.admin-table a {
  color: var(--primary);
  text-decoration: none;
}

.admin-table a:hover {
  text-decoration: underline;
}

.admin-table select {
  font: inherit;
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--background);
}

.vx-num,
.admin-table .vx-num,
.admin-table th.vx-num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.admin-table td .status-badge,
.admin-table .shadcn-button {
  white-space: nowrap;
}

/* Note (haohui): qualified by .admin-table th/td like .vx-num above, or
   admin.css's text-align:left wins and the row actions float mid-table. */
.admin-table th.vx-cell-actions,
.admin-table td.vx-cell-actions {
  text-align: right;
  white-space: nowrap;
}

.vx-cell-actions a.vx-row-link + a.vx-row-link {
  margin-left: 0.75rem;
}

.vx-sub {
  display: block;
  font-size: var(--text-xs);
  color: var(--muted-foreground);
  margin-top: 0.125rem;
}

.admin-table tr[aria-current="true"] {
  background: rgba(110, 86, 207, 0.06);
}

.vx-group-row th {
  background: var(--light-bg);
  color: var(--heading-color);
  font-weight: 600;
  text-align: left;
}

.vx-only-mobile {
  display: none;
}

.vx-group-row .question-type {
  background: var(--card);
  box-shadow: inset 0 0 0 1px var(--border);
}

.vx-tags {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 0.25rem;
}

.vx-tags .question-type {
  white-space: nowrap;
}

.vx-name {
  color: var(--heading-color);
  font-weight: 500;
  overflow-wrap: anywhere;
}

/* Note (haohui): a name that opens something takes Potato's link colour, as
   .admin-table a and .vx-crumbs do; in heading colour it read as plain text,
   so most rows of a list looked as if nothing in them could be clicked. */
a.vx-name {
  color: var(--primary);
  font-family: var(--font-sans);
  text-decoration: none;
}

a.vx-name:hover,
a.vx-name:focus-visible {
  text-decoration: underline;
}

a.vx-name:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: 2px;
}

.vx-code {
  font-family: var(--font-mono);
  font-size: 0.9375em;
  letter-spacing: 0;
  color: inherit;
  overflow-wrap: anywhere;
  font-variant-numeric: tabular-nums;
}

.vx-true-code {
  color: var(--muted-foreground);
}

a.vx-row-link {
  color: var(--primary);
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}

a.vx-row-link:hover {
  text-decoration: underline;
}

a.vx-row-link:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: 2px;
}

.vx-link-button {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  color: var(--primary);
  cursor: pointer;
}

.vx-link-button:hover {
  text-decoration: underline;
}

/* Note (haohui): a destructive action is a quiet outline button (A.3). Red at
   rest, a column of 停用 was the loudest thing on the accounts panel, so the
   red shows only on hover and focus; touch users, who have no hover, still read
   the consequence in the data-vx-confirm dialog before anything happens. */
.vx-btn-danger {
  color: var(--text-color);
}

.vx-btn-danger:hover,
.vx-btn-danger:focus-visible {
  background: rgba(239, 68, 68, 0.08);
  color: var(--destructive-text);
}

.status-badge {
  white-space: nowrap;
}

.status-badge::before {
  background: currentColor;
}

.status-active {
  color: var(--success-text);
}

.vx-status-warn {
  background: rgba(245, 158, 11, 0.12);
  color: var(--warning-text);
}

.vx-create {
  margin-bottom: 1.5rem;
}

.vx-create:not([open]) {
  display: none;
}

.vx-create-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  list-style: none;
  cursor: pointer;
  margin-bottom: 1rem;
}

.vx-create-head::-webkit-details-marker {
  display: none;
}

.vx-create-head h3 {
  margin: 0;
}

.vx-create > .vx-flashes {
  margin-bottom: 1rem;
}

.vx-create-close {
  font-size: var(--text-sm);
  color: var(--muted-foreground);
  white-space: nowrap;
}

.vx-create-head:hover .vx-create-close {
  color: var(--primary);
}

.vx-create-head:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 4px;
  border-radius: 2px;
}

.vx-form-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0 1rem;
}

.vx-form-row > .vx-field {
  flex: 1 1 12rem;
}

.vx-form-row > .vx-field--wide {
  flex: 2 1 18rem;
}

.vx-form-row > .shadcn-button {
  margin-top: 1.55rem;
}

.vx-field {
  display: block;
}

.vx-radio-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

/* Note (haohui): Potato sizes text inputs by padding and selects by the UA,
   which leaves them 2px apart in a form row; pin both to one height. */
select.potato-form-input,
input.potato-form-input:not([type="checkbox"]):not([type="radio"]):not([type="file"]) {
  height: 2.5rem;
}

.vx-hint {
  display: block;
  margin-top: 0.25rem;
  font-size: var(--text-xs);
  color: var(--muted-foreground);
}

.vx-inline-form {
  display: inline;
  margin: 0;
}

.config-form > p {
  color: var(--muted-foreground);
  margin: 0 0 1rem;
}

.config-form + .config-form {
  margin-top: 1rem;
}

.vx-narrow {
  max-width: 28rem;
}

/* Note (haohui): Potato's shadcn-button is inline-flex with no gap, which
   collapses the space between an icon or keybinding badge and the label. */
.shadcn-button {
  white-space: nowrap;
  gap: 0.375rem;
}

.shadcn-button:disabled,
.shadcn-button[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
  pointer-events: none;
}

.shadcn-button.vx-btn-sm {
  height: 2rem;
  padding: 0 0.75rem;
  font-size: var(--text-sm);
}

.shadcn-button-primary:hover {
  text-decoration: none;
}

.shadcn-button .loading-spinner {
  width: 1rem;
  height: 1rem;
  border-width: 2px;
}

.vx-empty {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--box-shadow);
  padding: 2.5rem 1.5rem;
  text-align: center;
}

.vx-empty--quiet {
  box-shadow: none;
  padding: 1.5rem;
}

.vx-empty-title {
  font-size: 1.125rem;
  font-weight: 600;
  margin: 0 0 0.375rem;
  color: var(--heading-color);
}

.vx-empty-body {
  margin: 0 auto;
  max-width: 32rem;
  color: var(--muted-foreground);
}

.vx-empty-actions {
  margin-top: 1.25rem;
  display: flex;
  gap: 0.5rem;
  justify-content: center;
  flex-wrap: wrap;
}

a.pagination-button {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  color: var(--text-color);
}

a.pagination-button.active {
  color: var(--primary-foreground);
}

.pagination-button[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

.vx-pagination-gap {
  padding: 0 0.25rem;
  color: var(--muted-foreground);
}

.potato-page-center.vx-standalone {
  flex-direction: column;
  justify-content: flex-start;
  padding: 0;
}

.vx-standalone-main {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  padding: 2rem 1rem;
}

.vx-standalone > .potato-footer {
  width: 100%;
}

.vx-standalone-box {
  width: 100%;
  max-width: 420px;
}

.vx-standalone-title {
  text-align: center;
  margin-bottom: 2rem;
}

.vx-standalone-title .potato-page-subtitle {
  margin-bottom: 0;
}

/* Note (haohui): CJK wraps at any character, so a centred line otherwise ends
   with a lone 。 or two characters on its own row. */
.vx-standalone-title .potato-page-subtitle,
.vx-standalone-help,
.vx-status-page .potato-page-subtitle,
.vx-status-page .vx-hint,
.vx-empty-title,
.vx-empty-body {
  text-wrap: balance;
}

.vx-standalone-title strong {
  color: var(--heading-color);
  font-weight: 600;
  overflow-wrap: anywhere;
}

.vx-standalone .vx-flashes {
  margin-bottom: 1rem;
}

.vx-standalone .potato-form-group .vx-hint {
  margin-top: 0.375rem;
}

.vx-standalone-help {
  text-align: center;
  font-size: 0.875rem;
  color: #6c757d;
  margin: 0.75rem 0 0;
}

/* Note (haohui): one span per clause, so a wrapped line breaks between
   clauses instead of inside a word. */
.vx-standalone-help > span,
.vx-standalone-title .potato-page-subtitle > span,
.vx-status-page .potato-page-subtitle > span,
.vx-status-page .vx-hint > span,
.vx-empty-body > span {
  display: inline-block;
}

.vx-standalone-help .vx-link-button {
  font-size: inherit;
}

.vx-link-button:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: 2px;
}

.vx-status-page {
  display: flex;
  justify-content: center;
  padding-block: 3rem;
}

.vx-status-page .potato-page-card {
  text-align: center;
}

.vx-status-page .potato-page-card .shadcn-button {
  width: 100%;
}

.vx-status-page .vx-hint {
  margin-top: 0.75rem;
}

.vx-status-page form {
  margin: 0;
}

.vx-status-page .potato-page-subtitle strong {
  color: var(--heading-color);
  font-weight: 600;
  overflow-wrap: anywhere;
}

.vx-section {
  margin-bottom: 2rem;
}

.vx-section > h2,
.vx-section-head h2 {
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--heading-color);
  margin: 0 0 1rem;
}

.vx-section-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  margin-bottom: 0.75rem;
  flex-wrap: wrap;
}

.vx-section-head h2 {
  margin: 0;
}

.question-stat .label {
  text-transform: none;
  letter-spacing: 0;
}

.vx-split {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: 1.5rem;
  align-items: start;
}

.vx-card-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
}

.potato-navbar a.task-name {
  color: var(--light-color);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 22rem;
  display: block;
}

.potato-navbar button.logout-btn {
  border: 0;
  cursor: pointer;
  font-family: inherit;
}

.potato-navbar .user-pill a.username {
  color: inherit;
  text-decoration: none;
}

.potato-navbar .user-pill a.username:hover {
  text-decoration: underline;
}

.potato-navbar a.nav-control-btn {
  text-decoration: none;
}

.potato-navbar .nav-control-btn[aria-disabled="true"] {
  opacity: 0.4;
  pointer-events: none;
}

/* Note (haohui): rules for real table rows and wide strips only; at 640px and
   below tables become stacked cards (flex gaps, no row hover) and the strip
   stacks label, bar and counts. */
@media (min-width: 40.0625rem) {
  /* Note (haohui): label, bar, number and counts share one centre line; the
     caption used to drop under the bar's right end as small grey text. */
  .vx-progress-strip .vx-progress--md {
    flex-wrap: nowrap;
  }

  .vx-progress-strip .vx-progress--md .pbar-caption {
    flex: 0 0 auto;
  }

  /* Note (haohui): Potato's grey tags share the hovered row's grey, which
     erased the pill. */
  .admin-table tbody tr:hover .question-type,
  .admin-table tbody tr:hover .status-badge.status-login {
    background: var(--card);
    box-shadow: inset 0 0 0 1px var(--border);
  }

  /* Note (haohui): a right-aligned number otherwise runs into the next,
     left-aligned column. */
  .admin-table th.vx-num,
  .admin-table td.vx-num {
    padding-right: 1.5rem;
  }

  .admin-table td.vx-cell-status .status-badge + .status-badge {
    margin-left: 0.375rem;
  }

  /* Note (haohui): the action column shrinks to its buttons so they sit at the
     table's right edge; the stacked cards below 640px must not get this. */
  .admin-table th.vx-cell-actions,
  .admin-table td.vx-cell-actions {
    width: 1%;
  }
}

/* Note (haohui): four compact stat cards still fit at 1024px (about 228px
   each); a 2x2 block there pushed the board below the fold of a 1024x768 screen. */
@media (max-width: 1024px) {
  .vx-stat {
    padding: 1rem;
  }

  .vx-stat h3 {
    margin-bottom: 0.25rem;
  }

  .vx-stat .description {
    margin: 0;
  }

  .vx-split,
  .vx-card-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

@media (max-width: 768px) {
  .vx-header-inner {
    padding: 0 1rem;
    min-height: 52px;
    flex-wrap: wrap;
    gap: 0 0.75rem;
  }

  /* Note (haohui): the first row is 52px even when the nav wraps under it,
     so the header is 52px with one section and 96px with two. */
  .vx-brand {
    display: flex;
    align-items: center;
    min-height: 52px;
    font-size: 1.125rem;
  }

  .vx-nav {
    order: 3;
    flex: 0 0 calc(100% + 2rem);
    height: 44px;
    border-top: 1px solid var(--border);
    margin: 0 -1rem -1px;
    padding: 0 1rem;
  }

  /* Note (haohui): the first layer keeps the 1px border-top opaque, so only
     the tabs fade and the line between the two header rows stays whole. */
  .vx-nav.is-scrolled,
  .vx-nav.is-overflowing {
    mask-image: linear-gradient(#000 0 0), linear-gradient(to right, transparent, #000 var(--vx-nav-fade-start), #000 calc(100% - var(--vx-nav-fade-end)), transparent);
    mask-size: 100% 1px, auto;
    mask-repeat: no-repeat;
  }

  .vx-nav--single {
    display: none;
  }

  .vx-account-name {
    max-width: 8rem;
  }

  .vx-account-role {
    display: none;
  }

  .dashboard-grid.vx-stat-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.75rem;
  }

  .table-controls {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
  }

  /* Note (haohui): wrap rather than scroll; a scroller with a hidden bar cut
     the last chips off at the gutter with no sign there were more. */
  .vx-chips {
    flex-wrap: wrap;
    overflow: visible;
    flex-basis: 100%;
  }

  .table-controls > .shadcn-button {
    flex: 1 1 100%;
    margin-left: 0;
  }
}

@media (max-width: 40rem) {
  .vx-standalone .potato-page-card,
  .vx-status-page .potato-page-card {
    padding: 1.5rem;
  }

  .vx-standalone-title {
    margin-bottom: 1.5rem;
  }

  .vx-status-page {
    padding-block: 1.5rem;
  }

  .vx-narrow .config-actions {
    flex-direction: column;
    gap: 0.5rem;
  }

  .vx-narrow .config-actions > .shadcn-button {
    width: 100%;
  }

  .vx-page-head h1 {
    font-size: 1.25rem;
  }

  .vx-page-actions {
    flex-basis: 100%;
  }

  .vx-page-actions > .shadcn-button-primary,
  .vx-page-actions > .shadcn-button:only-child {
    flex: 1;
  }

  .vx-crumbs ol {
    display: none;
  }

  .vx-crumbs-back {
    display: block;
    min-height: 44px;
    line-height: 44px;
    margin: -0.625rem 0 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--text-sm);
    color: var(--primary);
    text-decoration: none;
  }

  .vx-crumbs-back i {
    font-size: var(--text-xs);
  }

  .vx-progress-strip .vx-progress {
    flex-basis: 100%;
  }

  /* Note (haohui): a middle page of seven needs nine controls, about 356px,
     wider than a phone's card; wrap them instead of running past its edge. */
  .pagination-controls {
    flex-wrap: wrap;
    justify-content: center;
  }

  .vx-tabs {
    overflow-x: auto;
    scrollbar-width: none;
  }

  .vx-tabs::-webkit-scrollbar {
    display: none;
  }

  .vx-tabs .admin-tab {
    flex: 1 0 auto;
    padding: 0.6rem 0.75rem;
    font-size: var(--text-dense);
  }

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

  .vx-table-stack,
  .vx-table-stack tbody {
    display: block;
    width: 100%;
  }

  .vx-table-stack tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: 0.75rem;
    padding: 0.875rem 1rem;
    border-bottom: 1px solid var(--border);
  }

  .vx-table-stack td {
    grid-column: 1 / -1;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.75rem;
    padding: 0.2rem 0;
    border: 0;
    text-align: left;
  }

  .vx-table-stack td::before {
    content: attr(data-label);
    flex: 0 0 auto;
    font-size: var(--text-xs);
    color: var(--muted-foreground);
  }

  .vx-table-stack td[data-label=""]::before {
    content: none;
  }

  .vx-table-stack td.vx-cell-title {
    grid-column: 1;
    grid-row: 1;
    display: block;
    font-size: var(--text-base);
    padding-bottom: 0.375rem;
  }

  .vx-table-stack td.vx-cell-title::before,
  .vx-table-stack td.vx-cell-status::before {
    content: none;
  }

  .vx-table-stack td.vx-cell-status {
    grid-column: 2;
    grid-row: 1;
    align-self: start;
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.25rem;
  }

  .vx-table-stack td.vx-cell-actions {
    justify-content: flex-end;
    padding-top: 0.5rem;
  }

  .vx-table-stack td.vx-cell-actions:empty {
    display: none;
  }

  .vx-table-stack td .vx-progress {
    flex: 1 1 auto;
    max-width: 65%;
  }

  .vx-table-stack td.vx-hide-mobile {
    display: none;
  }

  .vx-table-stack td .vx-tags {
    justify-content: flex-end;
  }

  .vx-only-mobile {
    display: inline;
  }

  .vx-sub.vx-only-mobile {
    display: block;
  }

  .vx-table-stack tr.vx-group-row {
    display: block;
    padding: 0;
  }

  .vx-table-stack .vx-group-row th {
    display: block;
    padding: 0.75rem 1rem;
  }

  .vx-table-stack tbody tr:hover {
    background: none;
  }

  .vx-table-stack tbody tr[aria-current="true"] {
    background: rgba(110, 86, 207, 0.06);
  }

  .vx-sticky-actions {
    position: sticky;
    bottom: 0;
    background: var(--card);
    border-top: 1px solid var(--border);
    padding: 0.75rem 1rem;
    margin: 0 -1rem;
    z-index: 5;
  }

  .vx-sticky-actions .shadcn-button-primary {
    flex: 1 1 100%;
  }
}
/* Present on authentication, administration and all three workbenches. */
.vx-language-toggle {
  position: fixed;
  top: .65rem;
  right: .8rem;
  z-index: 1200;
  min-width: 5rem;
  min-height: 2.25rem;
  padding: .35rem .65rem;
  border: 1px solid var(--border, #d9dee7);
  border-radius: .5rem;
  background: var(--background, #fff);
  color: var(--foreground, #172033);
  font: inherit;
  font-size: .85rem;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 2px 8px #10203012;
}
.vx-language-toggle:hover { background: var(--muted, #f2f5fa); }
.vx-language-toggle:focus-visible { outline: 2px solid var(--primary, #2563eb); outline-offset: 3px; }
.vx-header-inner, .vx-task-navbar .navbar-inner { padding-right: 6.7rem; }
.vx-standalone { padding-top: 3.75rem; }
[data-vx-message] { display: contents; }
html[lang="en"] .vx-table-stack th,
html[lang="en"] .shadcn-button { overflow-wrap: normal; }
@media (max-width: 40rem) {
  .vx-language-toggle { top: .5rem; right: .5rem; min-width: 4.4rem; }
  .vx-header-inner, .vx-task-navbar .navbar-inner { padding-right: 5.4rem; }
}

/* Note (lty): a task YAML shown read-only - the pipeline builder's preview, the
   wizard's pre-check review and the package page's task file card. */
.pb-yaml-details > summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--heading-color);
}

.pb-yaml-details > summary:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 4px;
  border-radius: 2px;
}

.pb-yaml {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 1.55;
  min-height: 6rem;
  max-height: 60vh;
  overflow: auto;
  margin: 1rem 0 0;
  padding: 0.75rem 1rem;
  color: var(--heading-color);
  background: var(--muted);
  border-radius: var(--radius);
  white-space: pre;
}

.pb-yaml:not(textarea):empty::before {
  content: "还没有可用的 YAML";
  color: var(--muted-foreground);
}

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

html[lang="en"] .pb-yaml:not(textarea):empty::before {
  content: "No YAML available yet";
}
