/* The `<style scoped>` blocks of the ported views and components. Vue scoped each block to
   its component with a data attribute; here each is under the component's root class,
   which the component always renders — the same rule as eidolic.css. One block per
   component, in the order of src/components and src/pages. */

/* The shell — sidebar, top bar, drawer — is atlas.css's; AppBar.vue and StaffPane.vue,
   whose blocks stood here, were replaced by src/components/sidebar.rs. */

/* HomeView.vue (src/pages/home.rs): the "Home · Select project" board. */
.home-view__toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.home-view__count {
  margin-left: auto;
  font-size: var(--text-sm);
  color: var(--fg-muted);
}
.a-search {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  width: 280px;
  max-width: 100%;
  height: var(--input-h);
  padding: 0 10px;
  box-sizing: border-box;
  border: 1px solid var(--border);
  border-radius: var(--radius-base);
  background: var(--card);
  color: var(--fg-subtle);
  cursor: text;
}
.a-search:hover { border-color: var(--border-strong); }
.a-search:focus-within { border-color: var(--accent); box-shadow: var(--ring); }
.a-search input {
  flex: 1;
  min-width: 0;
  height: 100%;
  padding: 0;
  border: none;
  outline: none;
  background: transparent;
  font: inherit;
  font-size: var(--text-base);
  color: var(--fg);
}
.a-search input::placeholder { color: var(--fg-subtle); }
.home-view .projects {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--space-3);
}
.home-view .projects__link,
.home-view .projects__link:visited {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  color: var(--fg);
}
.home-view .projects__link:hover { border-color: var(--border-strong); color: var(--fg); }
.home-view .projects__link:focus-visible { outline: none; box-shadow: var(--ring); }
.home-view .projects__link.is-current { border-color: var(--accent); }
.home-view .projects__link .a-icon { color: var(--fg-subtle); }
.projects__text { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.projects__name { overflow: hidden; font-size: var(--text-base); font-weight: var(--weight-semibold); line-height: 17px; text-overflow: ellipsis; white-space: nowrap; }
.projects__id { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--fg-subtle); }

/* LoginView.vue (src/pages/login.rs): the "Login" board. */
.login-page {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: var(--space-4);
  box-sizing: border-box;
  background: var(--bg-subtle);
}
.my-login {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  width: 380px;
  max-width: 100%;
}
.my-login__card {
  width: 100%;
  gap: var(--space-4);
  padding: var(--space-6);
  box-shadow: var(--shadow-sm);
}
.my-login__header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  text-align: center;
}
.logo-mark--lg { width: 36px; height: 36px; border-radius: 9px; font-size: 17px; }
.my-login__title { margin: 0; font-size: var(--text-2xl); font-weight: var(--weight-semibold); line-height: 26px; }
.my-login__subtitle { margin: 0; font-size: var(--text-base); color: var(--fg-muted); }
.my-login__fields { display: flex; flex-direction: column; gap: var(--space-4); }
.my-login__submit.el-button { width: 100%; margin-left: 0; font-size: var(--text-base); }
.my-login__or { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); color: var(--fg-subtle); }
.my-login__or::before, .my-login__or::after { content: ""; flex: 1; height: 1px; background: var(--border); }
.my-login__version { margin: 0; font-size: var(--text-sm); color: var(--fg-subtle); }
.a-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.a-field__label { font-size: var(--text-base); font-weight: var(--weight-medium); color: var(--fg); }
.a-field__help { font-size: var(--text-sm); color: var(--fg-muted); }

/* ProjectView.vue (src/pages/project.rs): the "Project · Overview" board. */
.shortcuts {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-4);
}
.a-card.shortcut,
.a-card.shortcut:visited {
  gap: 6px;
  color: var(--fg);
}
.a-card.shortcut:hover { border-color: var(--border-strong); color: var(--fg); }
.a-card.shortcut:focus-visible { outline: none; box-shadow: var(--ring); }
.shortcut__icon {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  margin-bottom: 6px;
  border-radius: var(--radius-md);
  background: var(--accent-soft);
  color: var(--accent-ink);
}
.shortcut__title { font-size: var(--text-md); font-weight: var(--weight-semibold); }
.shortcut__description { font-size: var(--text-sm); line-height: 17px; color: var(--fg-muted); }
.shortcut__link { display: inline-flex; align-items: center; gap: 4px; margin-top: 6px; font-size: var(--text-base); font-weight: var(--weight-medium); color: var(--accent-ink); }

/* NotFound (src/pages/not_found.rs): the "Not found" board. */
.not-found {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 70vh;
  text-align: center;
}
.not-found__icon { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%; background: var(--muted); color: var(--fg-muted); }
.not-found__title { margin: 0; font-size: var(--text-2xl); font-weight: var(--weight-semibold); }
.not-found__text { margin: 0 0 6px; color: var(--fg-muted); }
.not-found .el-button, .not-found .el-button:visited { color: var(--accent-fg); }

/* LevelsView.vue (src/pages/levels.rs): the "Levels" board and its state cards. */
.levels-view .no-levels {
  padding: var(--space-6) 12px;
  text-align: center;
  color: var(--fg-muted);
}
.levels-view__shared {
  flex-direction: row;
  align-items: center;
}
.levels-view__shared-icon { color: var(--info); }
.levels-view__pager { justify-content: space-between; }
.levels-table td { height: 52px; }
.levels-table__picture { width: 40px; }
.level-delete { display: flex; flex-direction: column; gap: var(--space-3); }
.level-delete__picture { max-width: 100%; max-height: 240px; align-self: flex-start; border-radius: var(--radius-md); }
.level-delete__text { margin: 0; color: var(--fg-muted); }
.level-delete__text strong { font-weight: var(--weight-semibold); color: var(--fg); }

/* LevelsFilter.vue (src/components/levels/filter.rs): the filter card. */
.filter__row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-4);
}
.filter__field--stage { width: 220px; }
.filter__field--group { width: 200px; }
.filter__search { display: flex; align-items: center; gap: var(--space-2); }
.filter__search .el-input { width: 140px; }
.filter__search .el-button { margin-left: 0; }
.filter__spacer { flex: 1; }
.filter .el-checkbox { height: var(--input-h); margin-right: 0; }

/* LevelRow.vue (src/components/levels/row.rs): a row of the Levels table. */
.level-row .picture {
  display: block;
  width: 40px;
  height: 40px;
  object-fit: cover;
  border-radius: var(--radius-md);
  background: var(--muted);
}
.level-row .level-num { white-space: nowrap; }
.level-row .level-num__num {
  display: block;
  font-weight: var(--weight-semibold);
  line-height: 17px;
}
.level-row .level-num .version {
  display: block;
  font-size: var(--text-xs);
  line-height: 15px;
  color: var(--fg-muted);
}
.level-row .publish-flags {
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}
.level-row .publish-flags:hover .a-badge { border-color: var(--border-strong); }
.level-row__actions { display: inline-flex; flex-wrap: wrap; gap: 14px; }
.level-row .level-id { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--fg-subtle); }
.a-link--danger, .a-link--danger:visited { color: var(--danger); }
.a-link--danger:hover { color: color-mix(in srgb, var(--danger) 80%, black); }

/* A level's picture beside its number, version, group and id (publish dialog). */
.level-summary { display: flex; align-items: center; gap: var(--space-3); margin-bottom: 14px; }
.level-summary__picture { width: 64px; height: 64px; object-fit: cover; border-radius: var(--radius-md); background: var(--muted); }
.level-summary__text { display: flex; flex-direction: column; gap: 2px; font-size: var(--text-sm); }
.level-summary__text .a-strong { font-size: var(--text-base); }

/* LevelMoveDialog.vue and LevelMoveDialogRow.vue (src/components/levels/move_dialog.rs) */
.level-move-dialog .el-dialog__body { display: flex; flex-direction: column; gap: var(--space-3); }
.level-move-dialog__to { display: flex; align-items: center; gap: var(--space-2); }
.level-move-dialog .move-to-field { width: 80px; }
.level-move-dialog .move-to-btn { margin-left: 0; }
.level-move-dialog .a-field__help { margin: 0; }
.level-move-dialog .td-inner {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--space-2);
}
.level-move-dialog .picture {
  display: block;
  width: 32px;
  height: 32px;
  object-fit: cover;
  border-radius: var(--radius-base);
  background: var(--muted);
}
.level-move-dialog .level-num { font-weight: var(--weight-semibold); }
.level-move-dialog .level-num .version {
  display: block;
  font-size: var(--text-xs);
  font-weight: var(--weight-regular);
  color: var(--fg-muted);
}
.level-move-dialog .to-num,
.level-move-dialog .to-version {
  display: block;
  width: 64px;
}
/* The legacy row passed `error-messages`, a Vuetify prop Element never showed. */
.level-move-dialog tr.is-duplicated .el-input__wrapper {
  box-shadow: 0 0 0 1px var(--danger) inset;
}
.level-move-dialog .duplicated {
  padding: 0 8px;
  border-radius: var(--radius-pill);
  background: var(--danger-soft);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  line-height: 18px;
  color: var(--danger);
}

/* LevelPublishDialog.vue (src/components/levels/publish_dialog.rs) */
.level-publish-dialog__flags { display: flex; flex-direction: column; gap: 4px; }
.level-publish-dialog .a-field__help { margin: 8px 0 0; }

/* UserSorter.vue (src/components/user/sorter.rs): the "Order of drafts" dialog. */
.user-sorter { display: flex; flex-direction: column; gap: var(--space-3); }
.user-sorter .a-field__help { margin: 0; }
.user-sorter__list { padding: 4px; border: 1px solid var(--border); border-radius: var(--radius-md); }
.user-sorter__actions { display: flex; justify-content: flex-end; }
.user-sorter .no-select {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

/* LocalizedText.vue (src/components/localized_text.rs); the delete button is this port's. */
.localized-text__row {
  display: flex;
  align-items: flex-end;
}
.localized-text__row .ei-text-input {
  flex: 1 1 auto;
}
.localized-text__delete {
  margin: 0 0 1.2em 4px;
}

/* UpdatePopupView.vue (src/pages/update_popup.rs): the "Update popup" board. */
.update-popup-view .no-items { padding: var(--space-6) 0; }

/* UpdatePopupRow.vue (src/components/update_popup/row.rs): a row of the popup table. */
.update-popup-row .id { width: 48px; }
.update-popup-row .startOn { white-space: nowrap; }
.update-popup-row .image img { display: block; max-width: 48px; max-height: 32px; border-radius: var(--radius-sm); }
.update-popup-row .rewards { font-size: var(--text-sm); max-width: 200px; }
.update-popup-row .hidden-link {
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  font-weight: var(--weight-medium);
  color: var(--accent-ink);
  cursor: pointer;
}
.update-popup-row .hidden-link:hover { color: var(--accent); text-decoration: underline; }
.update-popup-table { overflow: visible; }
.update-popup-row .actions { width: 40px; text-align: right; }
.update-popup-row .actions .el-dropdown__popper.el-popper { left: auto; right: 0; }
.el-dropdown-menu__item.is-danger { color: var(--danger); }

/* UpdatePopupDialog.vue (src/components/update_popup/dialog.rs) */
.update-popup-dialog .content {
  width: fit-content;
}
.update-popup-dialog .card-actions {
  padding-bottom: 15px;
  display: flex;
  justify-content: center;
}
.update-popup-dialog .gray {
  color: #777;
}
.update-popup-dialog .pane {
  display: inline-block;
  width: 500px;
  vertical-align: top;
  padding: 0 10px;
}
/* A field dropped straight into a row took its content's width; the Vue one did too. */
.update-popup-dialog .el-row > .ei-text-input,
.update-popup-dialog .el-row > .el-select {
  width: 100%;
}

/* AdmUsersView.vue (src/pages/adm_users.rs): the "Adm users" board. */
.adm-users-view__filters { gap: 16px; }
.adm-users-view .pagination .el-pagination { flex: 1; }
.adm-users-view__actions .el-dropdown__popper.el-popper { left: auto; right: 0; }
@media (max-width: 899px) {
  /* A phone scrolls the table sideways inside its card, not the whole page. */
  .adm-users-view .el-table { overflow-x: auto; }
  /* That scroll box clips vertically too: while a row menu is open, make room under the
     last rows for it (the popper is 12px off its trigger and two items tall). */
  .adm-users-view .el-table:has(.el-dropdown__popper) { padding-bottom: 96px; }
}

/* UserCreate.vue (src/components/user/create.rs); the warning's colour was inline. */
.user-create .warning {
  color: var(--warning);
  margin-bottom: 10px;
}

/* NewUserCard.vue (src/components/user/new_user_card.rs) */
.new-user-card .copyable {
  display: flex;
  align-items: center;
}
.new-user-card .copy-btn {
  margin-left: 10px;
  cursor: pointer;
}
.new-user-card .copy-text {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: monospace;
}
.new-user-card .warning {
  color: orange;
  padding: 20px 0;
}

/* ProfileView.vue (src/pages/profile.rs): the "Profile" board. */
.profile { max-width: 960px; }
.profile .el-card__body { font-size: var(--text-base); line-height: 1.7; }
.profile .el-card__body strong { font-weight: var(--weight-medium); color: var(--fg-muted); }
.profile .card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.profile .card-header strong,
.profile .el-card__header strong { font-weight: var(--weight-semibold); color: var(--fg); }
.profile .hint {
  color: var(--fg-muted);
  font-size: var(--text-sm);
  margin: 0 0 12px;
}
.profile .perm-tag {
  margin: 0 0 0 4px;
}
.profile pre {
  background: var(--bg-subtle);
  border: 1px solid var(--border-row);
  padding: 10px 12px;
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  overflow-x: auto;
}
.profile__dialog-actions { justify-content: flex-end; width: 100%; }

/* ApiLogView.vue (src/pages/api_log.rs). The legacy laid the filters and the chart out
   as flex siblings with the chart at `calc(100% - 400px)`; here the filters keep a fixed
   column and the chart scrolls sideways when a month is wider than the window. */
.api-log-view__card {
  flex-direction: row;
  gap: 24px;
}
.api-log-view .filters {
  width: 260px;
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.api-log-view .filter .label {
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  color: var(--fg);
  margin-bottom: 6px;
}
.api-log-view .filter .el-select,
.api-log-view .filter .el-date-editor {
  width: 100%;
}
.api-log-view__denied { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.api-log-view__denied p { margin: 0; }
.api-log-view__denied-icon { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%; background: var(--muted); color: var(--fg-muted); }
.api-log-view__denied-title { font-size: var(--text-md); font-weight: var(--weight-semibold); color: var(--fg); }
.api-log-view .chart-container {
  flex: 1;
  min-width: 0;
  overflow-x: auto;
}

/* The chart itself (src/components/api_log_chart.rs): ink and chrome in the dataviz
   reference palette's roles, the marks coloured inline from their series. */
.chart {
  min-width: 700px;
  min-height: 528px;
}
.chart-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
  font-size: 12px;
  color: #52514e;
}
.chart-legend li {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.chart-legend .swatch {
  width: 10px;
  height: 10px;
  border-radius: 2px;
  display: inline-block;
}
.chart svg {
  display: block;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
}
.chart .grid {
  stroke: #e1e0d9;
  stroke-width: 1;
}
.chart .baseline {
  stroke: #c3c2b7;
  stroke-width: 1;
}
.chart .tick,
.chart .x-label {
  fill: #898781;
  font-size: 11px;
}
.chart .tick {
  font-variant-numeric: tabular-nums;
}
.chart .cap {
  fill: #52514e;
  font-size: 10px;
  font-variant-numeric: tabular-nums;
}
.chart .hit {
  fill: transparent;
}
.chart .column:hover .hit {
  fill: rgba(11, 11, 11, 0.04);
}
.chart .column.is-clickable {
  cursor: pointer;
}
.chart .empty {
  fill: #898781;
  font-size: 13px;
}

/* GameStatLogView.vue (src/pages/game_stat.rs); its block was unscoped, and its fixed
   header row's colours are on the kit's summary row here. The time under the date was
   absolutely positioned below its 50px-tall virtual row; in a table cell it is a second
   line. */
.game-stat-view .filters .el-row { gap: 8px; align-items: center; }
.game-stat-view .filters .el-space { flex-wrap: wrap; row-gap: 8px; }
.game-stat-view .el-table__fixed-row th.el-table__cell {
  background-color: var(--muted);
  color: var(--fg);
  font-weight: var(--weight-semibold);
}
.game-stat-view .version {
  font-size: 0.8em;
  color: #909399;
}
.game-stat-view .datetime {
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 1.3;
}
.game-stat-view .time {
  font-size: var(--text-sm);
  color: var(--fg-muted);
}

/* EventsView.vue (src/pages/events.rs): the "Events" board. */
.events-view .no-levels { padding: var(--space-6) 0; }
.events-view__add .el-button { gap: 4px; }
.update-responses__list { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 16px; }
.update-responses__item { display: inline-flex; align-items: center; gap: 8px; font-size: var(--text-sm); color: var(--fg-muted); }
.update-responses__item.is-error .update-responses__outcome { color: var(--danger); }
.update-responses__item + .update-responses__item { padding-left: 16px; border-left: 1px solid var(--border); }
.events-view__list .a-card__actions .a-search { width: 220px; }
.events-view__type.el-select { width: 150px; }
.events-view__legend { flex: 1; }
.events-table td { height: 48px; }
/* The row menus open in place (src/ui/mod.rs), so the table does not clip them. */
.events-view__list .a-table-wrap { overflow: visible; }
.event-row .actions .el-dropdown__popper.el-popper,
.events-view__add .el-dropdown__popper.el-popper { left: auto; right: 0; }
.events-view__add .el-dropdown-menu { max-height: 60vh; overflow-y: auto; }
@media (max-width: 899px) {
  /* A phone scrolls the table sideways; the row menu opens inside that. */
  .events-view__list .a-table-wrap { overflow-x: auto; }
  .events-view__list .a-card__actions .a-search { flex: 1; width: auto; }
  .filter__field--stage,
  .filter__field--group { width: 100%; }
  .filter__spacer { display: none; }
  .levels-table td, .events-table td { white-space: nowrap; }
}

/* EventChart.vue (src/components/event_chart.rs): the timeline, drawn in the design's
   greys — row rules and day lines on the border colours, weekends a faint shade, today a
   red line with its date in a red pill — with the bars in the event types' own colours. */
.event-chart {
  width: 100%;
  position: relative;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--card);
}
.event-chart .left-panel {
  position: absolute;
  display: inline-block;
  background: var(--bg-subtle);
}
.event-chart .events {
  position: absolute;
  overflow-x: auto;
  top: 0;
}
.event-chart .event-types {
  position: absolute;
}
.event-chart .left-panel__caption {
  display: flex;
  align-items: flex-end;
  padding: 0 12px 10px;
  box-sizing: border-box;
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.event-chart .active-indicator {
  font-size: 5px;
  text-shadow: 0 0 3px #000;
  opacity: 0.7;
}
.event-chart .chart-kind { font-size: var(--text-base); dominant-baseline: middle; fill: var(--fg); }
.event-chart .chart-month { font-size: var(--text-xs); font-weight: var(--weight-semibold); letter-spacing: 0.06em; text-transform: uppercase; fill: var(--gray-700); }
.event-chart .chart-day { font-size: var(--text-base); fill: var(--gray-700); }
.event-chart .chart-day.is-today { fill: #ffffff; font-weight: var(--weight-semibold); }
.event-chart .chart-today-pill { fill: var(--danger); }
/* The grid is hairlines on whole-pixel coordinates: a 1px stroke centred there covers
   half of two pixels and is drawn as a faint two-pixel smear, which is what made the
   timeline look out of focus next to the Vue one. crispEdges puts each on one pixel, at
   any zoom. The today line is 2px for the same reason — 1.5 cannot land on pixels. */
.event-chart .chart-day-line,
.event-chart .chart-month-line,
.event-chart .chart-row-line,
.event-chart .chart-edge,
.event-chart .chart-today-line { shape-rendering: crispEdges; }
.event-chart .chart-today-line { stroke: var(--danger); stroke-width: 2px; }
.event-chart .chart-day-off { fill: var(--muted); }
.event-chart .chart-day-line { stroke: var(--border); stroke-width: 1px; }
.event-chart .chart-month-line { stroke: var(--gray-400); stroke-width: 1px; }
.event-chart .chart-row-line { stroke: var(--border); stroke-width: 1px; }
.event-chart .chart-edge { stroke: var(--border-strong); stroke-width: 1px; }
/* The Vue bars had a dark hairline round them; without one a bar's edge is only where
   its colour fades into the grid. */
.event-chart .chart-bar-edge { stroke: rgba(17, 24, 39, 0.35); stroke-width: 1px; }
.event-chart .chart-bar-label { font-size: var(--text-base); font-weight: var(--weight-semibold); }
.event-chart .event-bar:hover .chart-bar-edge { stroke: var(--fg); }
.timeline-legend { flex-wrap: wrap; gap: 6px 16px; }
.timeline-legend__item { display: inline-flex; align-items: center; gap: 6px; }
.timeline-legend__swatch { width: 10px; height: 10px; border-radius: 2px; background: var(--gray-500); }
.timeline-legend__swatch--ended { background: repeating-linear-gradient(45deg, #c4c7cc 0 2px, #e5e7eb 2px 4px); }
.timeline-legend__swatch--running { background: var(--gray-500); }
.timeline-legend__swatch--upcoming { background: repeating-linear-gradient(45deg, #d1d5db 0 2px, #f3f4f6 2px 4px); }
.timeline-legend__swatch--scored { background: color-mix(in srgb, var(--gray-500) 50%, white); }
.timeline-legend__hint { margin-left: auto; }

/* DateTimePicker.vue (src/components/inputs/date_time_picker.rs): the labelled wrapper
   the propagation dialog and the event dialog put their pickers in. */
.date-time-picker {
  display: inline-block;
}
.date-time-picker .label {
  display: block;
  width: 100px;
  font-size: 12px;
}

/* SimpleRewardListInput.vue (src/components/inputs/simple_reward_list_input.rs) */
.simple-reward-list-input .empty-add-row {
  min-height: 32px;
}

/* PropagationDialog.vue (src/components/event/propagation_dialog.rs): the "Propagate"
   board — the warning up top, the plan's fields, then the preview chart and table. */
.propagation-dialog .disclaimer { margin-bottom: 12px; }
.propagation-dialog__preview { margin-top: 18px; }
.propagation-dialog .date-time-picker .label { display: block; font-size: var(--text-base); font-weight: var(--weight-medium); color: var(--fg); }

/* EventRow.vue (src/components/event/row.rs): a row of the Events table. The row keeps
   the class `.row` beside `.event-row`, because the e2e suite clicks
   `.row .name.hidden-link`. An inactive or ended row is muted. */
.event-row.inactive td,
.event-row.ended td { color: var(--fg-subtle); }
.event-row .id { width: 56px; font-variant-numeric: tabular-nums; }
.event-row .type { white-space: nowrap; color: var(--fg-muted); }
.event-row .type .a-dot { margin-right: 8px; }
.event-row .date { white-space: nowrap; line-height: 17px; font-variant-numeric: tabular-nums; }
.event-row .date .date__day { display: block; }
.event-row .date .time { display: block; font-size: var(--text-sm); color: var(--fg-muted); }
.event-row .timer { white-space: nowrap; }
.event-row .flags { white-space: nowrap; }
.event-row .actions { width: 40px; text-align: right; }
.event-row .hidden-link {
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  font-weight: var(--weight-medium);
  color: var(--accent-ink);
  text-align: left;
  cursor: pointer;
}
.event-row .hidden-link:hover { color: var(--accent); text-decoration: underline; }
.event-row.inactive .hidden-link,
.event-row.ended .hidden-link { color: var(--fg-muted); font-weight: var(--weight-regular); }

/* EventDialog.vue (src/components/event/event_dialog.rs): the "Edit event" board — the
   title in the dialog's header with the type under it, a warning when only Force update
   can save, the General column beside the type's column under small uppercase captions,
   and the footer: More at the left, Cancel and the action at the right. */
.event-dialog .el-dialog__header { padding-bottom: 0; min-height: 44px; }
.event-dialog .header {
  display: flex;
  align-items: center;
  gap: 4px;
  margin: 0 0 14px;
  font-size: var(--text-base);
  color: var(--fg-muted);
}
.event-dialog__guard { margin-bottom: 14px; }
.event-dialog .content {
  align-content: start;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 0 24px;
}
.event-dialog .main-column,
.event-dialog .data-column {
  display: inline-block;
  min-width: 0;
}
.event-dialog .main-column {
  max-width: 500px;
}
.event-dialog .data-column {
  padding-left: 24px;
  border-left: 1px solid var(--border);
}
.section-caption {
  margin: 4px 0 10px;
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  line-height: 15px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-muted);
}
.event-dialog .steps .section-caption,
.event-dialog .bundles .section-caption { margin-top: 18px; }
/* The legacy wrapped these rows in an `<el-space wrap>`, whose gap is what is left of it. */
.event-dialog .dates,
.event-dialog .bundle-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.event-dialog .padded {
  padding: 3px 0;
}
.event-dialog .padded > * {
  padding-right: 5px;
}
.event-dialog .btn-icon {
  margin-top: 15px;
  color: var(--fg-muted);
}
.event-dialog .btn-icon:hover { color: var(--danger); }
.event-dialog .main-column .bundle-url {
  font-size: var(--text-base);
  padding: 0;
}
.event-dialog .propagate-link { font-weight: var(--weight-medium); }
.event-dialog .extra-actions { margin-right: auto; }
.event-dialog .more-trigger.el-button { gap: 4px; }
.el-dropdown-menu__item.extra-action-delete { color: var(--danger); }
.el-dropdown-menu__item.extra-action-delete:hover { background: var(--danger-soft); color: var(--danger); }

/* The second dialog EventDialog.vue put up: what Update Future changed. */
.future-events-dialog .a-table-wrap { margin-top: 10px; }
.future-events-dialog .a-field__help { margin: 0; }

/* The per-type parts of the event dialog (src/components/event/parts/). The legacy's
   `<style scoped>` blocks, which were three: the segments of a piggy, the products of a
   bank customization, and the widths two parts set on themselves. */
.event-dialog .segmented-piggy {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 420px;
}
.event-dialog .segment-row {
  position: relative;
  margin-bottom: 10px;
  border: 1px solid #ccc;
  padding: 5px 10px;
  border-radius: 5px;
}
.event-dialog .segment-delete {
  position: absolute;
  right: 10px;
  top: 0;
}
.event-dialog .bank-customization {
  width: 600px;
}
.event-dialog .product-row {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
}
.event-dialog .product-row .product-id {
  flex: 1;
}
/* A rating's rows are a table, as they were; the cells sit on one line. */
.event-dialog .score-rating td {
  vertical-align: bottom;
  padding-right: 4px;
}

/* The complex parts of the dialog (src/components/event/parts/): the marathons, the
   offers, the season pass and their kin. The legacy's `<style scoped>` blocks, the widths the
   parts set on their own root, and the two labels that repeat across them: the big grey
   segment number and the bold row label to the left of a pair of fields. */
.event-dialog .marathon {
  max-width: 800px;
}
.event-dialog .tournament {
  max-width: 1000px;
}
.event-dialog .competition {
  max-width: 700px;
}
.event-dialog .chance-bracelet {
  max-width: 500px;
}
.event-dialog .tapjong-bracelet {
  max-width: 700px;
}
.event-dialog .tapjong-bracelet .btn-icon {
  margin-top: 8px;
}
.event-dialog .fortune-wheel {
  max-width: 700px;
}
.event-dialog .fortune-wheel .index {
  margin-top: 18px;
  font-weight: bold;
  color: #999;
}
.event-dialog .gold-kitty {
  margin: 10px;
}
.event-dialog .double-gems {
  max-width: 450px;
}
.event-dialog .special-offer {
  max-width: 600px;
}
.event-dialog .season-pass {
  max-width: 600px;
}
.event-dialog .picture-strike {
  min-width: 400px;
  max-width: 600px;
}
/* A segment of a double-gems event, a tower piggy, and an offer of a special one: a
   rounded block per row. */
.event-dialog .double-gems .segment,
.event-dialog .tower-piggy .segment {
  border: 1px solid #ccc;
  border-radius: 10px;
  padding: 10px;
  position: relative;
  margin: 5px 0;
}
.event-dialog .special-offer .offer {
  border: 1px solid #ccc;
  border-radius: 10px;
  padding: 3px 0 0 7px;
  margin-bottom: 10px;
  position: relative;
}
.event-dialog .row-label {
  display: inline-block;
  font-weight: bold;
  font-size: 15px;
  color: #666;
  margin-top: 20px;
  margin-right: 7px;
  min-width: 78px;
}
.event-dialog .segment-number {
  display: inline-block;
  font-weight: bold;
  color: #888;
  font-size: 30px;
  padding: 8px 0 0 16px;
}
.event-dialog .tower-piggy .skin-icon {
  font-size: 32px;
}
/* The season pass's bordered groups, each with its name over the border. */
.event-dialog .season-pass .group {
  border: #bbb 1px solid;
  margin: 10px 0;
  padding: 15px 10px;
  border-radius: 10px;
  position: relative;
}
.event-dialog .season-pass .group__header {
  position: absolute;
  font-weight: bold;
  color: #444;
  background: white;
  padding: 0 1em;
  margin: -25px 0 0 15px;
}
.event-dialog .season-pass .token-try {
  max-width: 100px;
}
.event-dialog .season-pass-promo-view .time {
  margin-top: 25px;
  font-size: 16px;
  color: #888;
}
.event-dialog .season-pass-promo-view .offer {
  position: relative;
  border: 1px solid #ccc;
  border-radius: 10px;
  padding: 0 0 0 50px;
  margin-bottom: 10px;
}
.event-dialog .season-pass-promo-view .offer-index {
  position: absolute;
  top: 15px;
  left: 10px;
  color: #888;
  font-size: 27px;
}
.event-dialog .season-pass-promo-view .remove {
  position: absolute;
  top: 0;
  right: 0;
}
/* A picture strike's checkpoints. */
.event-dialog .picture-strike .checkpoints {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.event-dialog .picture-strike .checkpoint-block {
  border: 1px solid var(--el-border-color-lighter);
  border-radius: 8px;
  padding: 8px 10px 10px;
}
.event-dialog .picture-strike .checkpoint-index {
  font-size: 16px;
  font-weight: bold;
  color: #aaa;
  margin-top: 17px;
}
.event-dialog .picture-strike .empty-checkpoints {
  color: var(--el-text-color-secondary);
  font-size: 13px;
  padding: 4px 0;
}
.event-dialog .picture-strike .add-checkpoint {
  margin-top: 10px;
}
/* The segment offer's table: the widths the legacy gave its cells, and the narrow
   inputs inside them. Its own block was 1300px wide at least. */
.event-dialog .segment-offer .segment-offer-flex {
  min-width: 1300px;
}
.event-dialog .segment-offer .sale-days {
  display: flex;
  gap: 40px;
}
.event-dialog .segment-offer .condensed-checkbox {
  margin: 0;
  padding: 0;
}
.event-dialog .segment-offer table {
  border-collapse: collapse;
}
.event-dialog .segment-offer .row.head th {
  white-space: nowrap;
  font-size: 12px;
}
.event-dialog .segment-offer .segment-payer {
  width: 85px;
}
.event-dialog .segment-offer .segment-range1,
.event-dialog .segment-offer .segment-range2 {
  width: 40px;
  white-space: nowrap;
}
.event-dialog .segment-offer .segment-range2 {
  margin-right: 10px;
}
.event-dialog .segment-offer .segment-dash {
  width: 8px;
  margin: 10px 3px 0;
}
.event-dialog .segment-offer .col-product-id {
  width: 200px;
}
.event-dialog .segment-offer .col-discount-percent {
  width: 30px;
}
.event-dialog .segment-offer .col-reward {
  width: 45px;
}
.event-dialog .segment-offer .sold-out-rewards-label {
  margin-top: 10px;
  text-align: right;
}
/* The legacy set these two on the whole part, unscoped, to fit the row of numbers. */
.event-dialog .segment-offer .el-input__wrapper {
  padding-left: 1px;
  padding-right: 1px;
}
.event-dialog .segment-offer .el-input__wrapper input {
  text-align: center;
}

/* ---------- The Gem Valley balance simulator (components/balance/) ---------- */

/* MaterialEl.vue: the picture at two ems with the name beside it; `small` is the chip a
   building's gives row is made of, `icon` the picture alone at three. */
.material-el {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
}
.material-el img {
  display: inline;
  width: 2em;
  height: 2em;
}
.material-el .emoji {
  font-size: 2em;
}
.material-el.small {
  font-size: 1em;
  background-color: aliceblue;
  padding: 0.2em 0.5em;
  border-radius: 2em;
}
.material-el.small img {
  width: 1em;
  height: 1em;
}
.material-el.small .emoji {
  font-size: 1em;
}
.material-el.icon img {
  width: 3em;
  height: 3em;
}
.material-el.icon .emoji {
  font-size: 3em;
}

/* MaterialSet.vue: the checkboxes, in as many columns as fit. */
.material-set .grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 12em));
  grid-auto-rows: auto;
  gap: 20px;
  grid-auto-flow: row;
}
.material-set .actions {
  margin-top: 20px;
}

/* BuildingSet.vue and BuildingEl.vue: the cards, and Add Building at the end of them. */
/* The dashboard's frame: the page header's Back (the old back arrow), the cards. */
.balance-view__back {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  color: var(--accent-ink);
  cursor: pointer;
}
.balance-view__back:hover { text-decoration: underline; }
.balance-view__create { max-width: 440px; }
.balance-view .grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
  gap: 1em 0;
}
.balance-view .grid .el-card {
  width: 380px;
}
.balance-view .grid .header {
  font-size: 1.2em;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 1em;
}
.balance-view .grid .header .edit-button {
  margin-left: auto;
}
.balance-view .requirement {
  display: flex;
  align-items: center;
  gap: 0.5em;
}
.balance-view .amount {
  font-size: 1.2em;
}
.balance-view .add-building {
  width: 400px;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* StepSetup.vue: Remove Step in the card's top right corner. */
.balance-view .step {
  margin-bottom: 10px;
}
.balance-view .step .el-card__body {
  position: relative;
}
.balance-view .step .rem {
  position: absolute;
  top: 20px;
  right: 20px;
}

/* BalanceSetupEl.vue: what the setup is, and the two buttons across from it. */
.balance-setup .head {
  display: flex;
  justify-content: space-between;
}
.balance-setup .el-collapse-item__header h2 {
  margin: 0;
}

/* InventoryEl.vue: the bag. */
.balance-view .items {
  display: flex;
  gap: 10px;
  flex-flow: wrap;
}
.balance-view .item {
  display: flex;
  align-items: center;
  gap: 5px;
}
/* GameListEl.vue greys out a requirement a match had already met. */
.balance-view .item.complete {
  filter: grayscale(1);
}

/* SimulationOutcomeEl.vue: the two statistic cards, and the building and step headings —
   the colours the legacy painted its col-spanned rows with. */
.balance-view .outcome {
  margin-top: 20px;
}
.balance-view .cards {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
}
.balance-view .cards .card {
  display: inline-block;
  height: 100%;
  padding: 20px;
  border-radius: 4px;
  background-color: var(--el-bg-color-overlay);
  border: 0.5px solid var(--el-border-color);
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  width: fit-content;
  margin: 10px 0;
}
.balance-view .fixing .heading {
  font-size: 16px;
}
.balance-view .building-title {
  font-size: 1.2em;
  display: flex;
  gap: 20px;
  background-color: rgb(235, 229, 209);
  width: 100%;
  padding: 4px 20px;
  box-sizing: border-box;
}
.balance-view .step-title {
  display: flex;
  align-items: center;
  gap: 30px;
  font-size: 1.1em;
  background-color: rgb(208, 232, 232);
  width: 100%;
  padding: 4px 20px;
  box-sizing: border-box;
  cursor: pointer;
}
.balance-view .step-title .expand {
  transition: transform var(--el-transition-duration);
}
.balance-view .step-title .expand.is-open {
  transform: rotate(90deg);
}

/* PicturesReviewView.vue (src/pages/pictures_review.rs). The bunch list is a plain table
   rather than an `el-table`, as it was: it is one delivery per header with a rowspan down
   its left, which Element's table cannot draw. The utility classes the view asks for
   (`mt-3`) were as inert in the Vue app as they are here. */
/* The "Pictures review" board: the page header, then the stats bar as a card that
   sticks to the top of the main area while the bunches scroll under it. */
.pictures-review-view .loading-wrap {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 40px;
  color: var(--fg-muted);
}
.pictures-review-view .loading-wrap .el-icon {
  font-size: 32px;
}
.pictures-review-view .error-wrap {
  max-width: 500px;
}
.pictures-review-view .stats-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 14px;
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.pictures-review-view .stats-left {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.pictures-review-view .bulk-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}
.pictures-review-view .bunches-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.pictures-review-view .bunch-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  background: #fafafa;
  border: 1px solid #e8e8e8;
  border-radius: 4px;
  user-select: none;
  transition: background 0.15s;
}
.pictures-review-view .bunch-header:hover {
  background: #f0f0f0;
}
.pictures-review-view .bunch-checkbox {
  width: 16px;
  height: 16px;
  cursor: pointer;
  flex-shrink: 0;
  accent-color: #409eff;
}
.pictures-review-view .bunch-header-rest {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex: 1;
  cursor: pointer;
}
.pictures-review-view .bunch-left {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.pictures-review-view .expand-icon {
  transition: transform 0.2s;
  font-size: 12px;
  color: #999;
}
.pictures-review-view .expand-icon.expanded {
  transform: rotate(90deg);
}
.pictures-review-view .bunch-range {
  font-weight: 600;
  font-family: monospace;
  min-width: 90px;
}
.pictures-review-view .bunch-theme {
  color: #555;
  min-width: 140px;
}
.pictures-review-view .bunch-indicators {
  display: flex;
  gap: 4px;
  align-items: center;
}
.pictures-review-view .indicator {
  display: inline-block;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  text-align: center;
  line-height: 24px;
  font-size: 12px;
  font-weight: 600;
  color: #fff;
  flex-shrink: 0;
  box-sizing: border-box;
  vertical-align: middle;
}
.pictures-review-view .indicator.good {
  background: #b6e89e;
  color: #2d6a10;
}
.pictures-review-view .indicator.fix {
  background: #f5d5a0;
  color: #8a5d1b;
}
.pictures-review-view .indicator.replace {
  background: #f9b4b4;
  color: #a13030;
}
.pictures-review-view .bunch-comments {
  display: flex;
  gap: 4px;
  align-items: center;
  flex-wrap: wrap;
  min-width: 0;
}
.pictures-review-view .comment-preview {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 14px;
  height: 24px;
  padding: 0 9px;
  border: 1px solid #d9d9d9;
  border-radius: 12px;
  color: #606266;
  max-width: 180px;
  white-space: nowrap;
  overflow: hidden;
  background: #fff;
}
.pictures-review-view .comment-text {
  overflow: hidden;
  text-overflow: ellipsis;
}
.pictures-review-view .comment-count {
  font-size: 11px;
  font-weight: 600;
  flex-shrink: 0;
}
.pictures-review-view .bunch-to-review {
  color: #999;
  font-size: 13px;
  flex-shrink: 0;
}
.pictures-review-view .bunch-done {
  color: #999;
  font-size: 13px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}
.pictures-review-view .bunch-all-good {
  color: #8bb87a;
  font-size: 13px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}
.pictures-review-view .bunch-table-wrap {
  border: 1px solid #e8e8e8;
  border-top: none;
  border-radius: 0 0 4px 4px;
  overflow: hidden;
}
.pictures-review-view .bunch-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.pictures-review-view .bunch-table thead th {
  background: #f5f5f5;
  padding: 6px 12px;
  text-align: left;
  font-weight: 600;
  border-bottom: 1px solid #e0e0e0;
  color: #666;
}
.pictures-review-view .bunch-table tbody td {
  padding: 4px 12px;
  border-bottom: 1px solid #f0f0f0;
}
.pictures-review-view .col-check {
  width: 20px;
}
.pictures-review-view .col-numbers {
  width: 110px;
}
.pictures-review-view .col-theme {
  width: 160px;
}
.pictures-review-view .col-pic {
  width: 130px;
}
.pictures-review-view .col-decision {
  width: 180px;
}
.pictures-review-view .col-notes {
  min-width: 280px;
}
.pictures-review-view .cell-check {
  vertical-align: top;
  text-align: center;
  padding: 4px !important;
}
.pictures-review-view .cell-numbers,
.pictures-review-view .cell-theme {
  vertical-align: top;
  font-weight: 500;
  background: #fafafa;
}
.pictures-review-view .row-selected {
  background: #ecf5ff;
}
.pictures-review-view .cell-pic {
  padding: 0 !important;
}
.pictures-review-view .cell-pic-inner {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
}
.pictures-review-view .pic-checkbox {
  width: 14px;
  height: 14px;
  cursor: pointer;
  accent-color: #409eff;
  flex-shrink: 0;
}
.pictures-review-view .pic-id {
  color: #409eff;
  font-weight: 500;
  cursor: pointer;
  flex: 1;
  text-align: right;
}
.pictures-review-view .pic-id:hover {
  text-decoration: underline;
}
.pictures-review-view .pic-rev {
  color: #aaa;
  font-size: 11px;
  width: 42px;
  flex-shrink: 0;
}
.pictures-review-view .decision-good {
  background: #f0f9eb;
}
.pictures-review-view .decision-replace {
  background: #fef0f0;
}
.pictures-review-view .decision-fix {
  background: #fdf6ec;
}
.pictures-review-view .cell-notes {
  cursor: pointer;
  min-width: 280px;
  padding: 0 !important;
}
.pictures-review-view .cell-notes:hover {
  background: #f5f7fa;
}
.pictures-review-view .note-text {
  color: #555;
  white-space: pre-wrap;
  word-break: break-word;
  display: block;
  padding: 4px 12px;
  min-height: 22px;
  border-radius: 2px;
}
.pictures-review-view .note-edit {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 2px 6px;
  width: 100%;
  box-sizing: border-box;
}
.pictures-review-view .note-input {
  flex: 1;
  border: 1px solid #dcdfe6;
  border-radius: 3px;
  padding: 3px 8px;
  font-size: 12px;
  outline: none;
  min-width: 0;
  height: 24px;
  box-sizing: border-box;
}
.pictures-review-view .note-input:focus {
  border-color: #409eff;
}
.pictures-review-view .note-actions {
  display: flex;
  gap: 8px;
  flex-shrink: 0;
  align-items: center;
}
.pictures-review-view .note-action {
  cursor: pointer;
  font-size: 18px;
  padding: 3px;
  border-radius: 4px;
  transition:
    background 0.15s,
    color 0.15s;
}
.pictures-review-view .note-action.save {
  color: #67c23a;
}
.pictures-review-view .note-action.save:hover {
  background: #f0f9eb;
  color: #529b2e;
}
.pictures-review-view .note-action.cancel {
  color: #909399;
}
.pictures-review-view .note-action.cancel:hover {
  background: #f0f0f0;
  color: #606266;
}
.pictures-review-view .note-action.delete {
  color: #f56c6c;
}
.pictures-review-view .note-action.delete:hover {
  background: #fef0f0;
  color: #c45656;
}

/* The bulk comment dialog, which the view puts up; its footer was two inline flex rows. */
.comment-dialog-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.comment-color-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  color: #666;
}
.comment-color-row .color-preview {
  padding: 2px 12px;
  border-radius: 4px;
  font-size: 12px;
}
.comment-dialog-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
}
.comment-dialog-footer .comment-dialog-actions {
  display: flex;
  gap: 8px;
}

/* The view's one unscoped rule: `el-main` scrolls, and a `position: sticky` stats bar
   inside a scrolling box sticks to that box rather than to the window. */
.el-main:has(.stats-bar) {
  overflow: visible !important;
}

/* PicturePlayDialog.vue (src/components/pictures_review/picture_play_dialog.rs). The
   images are sized in script (see the file); everything here is the frame around them. */
.pic-review-dialog .el-dialog__body {
  padding: 0 10px 10px;
}
.pic-review-dialog .el-dialog__header {
  padding: 10px 16px 6px;
}
.pic-review-dialog .mt-3 {
  margin-top: 12px;
}
.pic-review-dialog .play-layout {
  display: flex;
  height: calc(92vh - 70px);
}
.pic-review-dialog .viewer-area {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
}
.pic-review-dialog .viewer-loading {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  color: #999;
}
.pic-review-dialog .viewer-loading .el-icon {
  font-size: 28px;
}
.pic-review-dialog .viewer-error {
  padding: 20px;
}
.pic-review-dialog .viewer-error .el-button {
  margin-top: 12px;
}
.pic-review-dialog .panes {
  flex: 1;
  display: flex;
  gap: 4px;
  min-height: 0;
  justify-content: center;
}
.pic-review-dialog .pane {
  flex: none;
}
.pic-review-dialog .image-wrapper {
  position: relative;
}
.pic-review-dialog .game-image {
  display: block;
  cursor: crosshair;
  user-select: none;
}
.pic-review-dialog .diff-highlight {
  position: absolute;
  border: 3px solid #67c23a;
  border-radius: 3px;
  pointer-events: none;
  box-shadow: 0 0 6px rgba(103, 194, 58, 0.4);
  display: flex;
  align-items: flex-start;
  justify-content: flex-start;
}
.pic-review-dialog .diff-highlight.fix-hovered {
  border-color: #e6a23c !important;
  box-shadow: 0 0 10px rgba(230, 162, 60, 0.6) !important;
}
.pic-review-dialog .diff-number {
  font-size: 16px;
  font-weight: 900;
  line-height: 1;
  padding: 1px 3px;
  color: #67c23a;
  text-shadow:
    0 0 3px #000,
    0 1px 2px #000,
    1px 0 2px #000,
    -1px 0 2px #000,
    0 -1px 2px #000;
}
.pic-review-dialog .diff-highlight.fix-hovered .diff-number {
  color: #e6a23c !important;
}
.pic-review-dialog .diff-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 4px;
  flex-wrap: wrap;
  border-top: 1px solid #e8e8e8;
  margin-top: 2px;
}
.pic-review-dialog .diff-cb {
  display: flex;
  align-items: center;
  gap: 2px;
  cursor: pointer;
  user-select: none;
}
.pic-review-dialog .diff-cb input[type='checkbox'] {
  width: 14px;
  height: 14px;
  cursor: pointer;
  accent-color: #67c23a;
}
.pic-review-dialog .diff-cb-num {
  font-size: 12px;
  color: #666;
}
.pic-review-dialog .decision-panel {
  width: 180px;
  flex-shrink: 0;
  border-left: 1px solid #e8e8e8;
  display: flex;
  flex-direction: column;
}
.pic-review-dialog .panel-content {
  flex: 1;
  padding: 4px 12px;
  overflow-y: auto;
}
.pic-review-dialog .decision-panel h4 {
  margin: 0 0 6px 0;
  font-size: 13px;
  font-weight: 600;
  color: #333;
}
.pic-review-dialog .decision-radios {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
}
.pic-review-dialog .decision-radios .el-radio {
  margin-right: 0;
  height: 26px;
}
.pic-review-dialog .fix-regions {
  display: flex;
  flex-direction: column;
}
.pic-review-dialog .fix-region-item {
  padding: 1px 0;
}
.pic-review-dialog .fix-region-item .el-checkbox {
  height: 22px;
}
.pic-review-dialog .save-btn {
  margin-top: 12px;
  width: 100%;
}
.pic-review-dialog .fix-warning {
  margin-top: 6px;
  font-size: 12px;
  color: #e6a23c;
  line-height: 1.3;
}
.pic-review-dialog .note-color-row {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
}
.pic-review-dialog .note-color-label {
  font-size: 12px;
  color: #666;
}
.pic-review-dialog .panel-nav {
  padding: 8px 12px;
  border-top: 1px solid #e8e8e8;
  display: flex;
  gap: 8px;
}
.pic-review-dialog .panel-nav .el-button {
  flex: 1;
}

/* ---------------------------------------------------------------------------
   Hosted flags & experiments — pages/halva.rs and the six halva_*.rs screens.

   The port brought the markup across from the Halva admin UI but not a
   stylesheet: the screens carried class names (`.page`, `.page-head`, `.stat`,
   `.table`, `.eyebrow`, `.namespace-*`) that no sheet defined, three
   components shipped a `<style>` element in their own view to make up part of
   the difference, and what rules did exist here dressed native controls in a
   1px border and stretched every field to the full column. The result did not
   look like the rest of the app.

   These screens are semantic HTML — label/input/fieldset/legend/table — rather
   than src/ui components, because their forms hold one `RwSignal<Draft>` per
   screen and the kit's inputs each want an `RwSignal<String>` of their own.
   So the rules below give that HTML Element's own metrics off Element's own
   `--el-*` variables: a 32px control with the 4px radius and the inset border
   `.el-input__wrapper` draws, the primary ring on focus, `--el-fill-color-light`
   behind a table head. A native control is left native — it keeps the
   accessibility tree the e2e suite reads — and only made to match.

   The shell, panel, field, message and table rules are under `.halva-shell` —
   the root pages/halva.rs renders — so none of them reaches another screen.
   The sections after them select on `.namespace-*`, `.variation-editor` and
   `.metric-chooser` directly: those classes are rendered by one halva page
   each and by nothing else in the app, and keeping them unnested is what lets
   the mobile-overflow rules read as being about those components. The three
   in-view `<style>` blocks are gone; those sections are where they went.
   --------------------------------------------------------------------------- */

.halva-shell {
  --halva-gap: 20px;
  /* A line of prose stops at about 80 characters; a form field at a width that
     suits its value rather than the column. Tables and the allocation bar opt
     out below — they want the room. In rem, not the `ch` this started as: `ch`
     is the width of the element's own "0", so a bold banner claimed a wider
     line than the regular-weight one under it and the stack came out ragged. */
  --halva-measure: 44rem;
  /* The shell's main area pads the page; the sections are an .a-page. */
  gap: var(--space-4);
}

/* ---------- Page head and environment strip ----------
   The section tabs this block used to draw are the sidebar's links now; the head is
   the Atlas page header (ui::atlas::PageHead) and the project context the
   environment strip (.a-env-strip, atlas.css). A detail page keeps its own heading
   under the breadcrumb. */
.halva-shell__crumb { margin-bottom: -8px; }
.halva-context__hint:empty {
  display: none;
}
.halva-shell .halva-context__env {
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: var(--text-sm);
  color: var(--fg-muted);
}
.halva-shell .halva-context__env > select.a-select {
  width: auto;
  min-width: 140px;
  height: var(--input-h);
  padding: 0 28px 0 10px;
  border: 1px solid var(--border);
  box-shadow: none;
  background-color: var(--card);
}
.halva-shell .section-intro {
  margin-top: 0;
  font-size: 13px;
  color: var(--el-text-color-secondary);
}

/* ---------- Prose ---------- */

.halva-shell p,
.halva-shell li {
  max-width: var(--halva-measure);
  line-height: 1.6;
}
.halva-shell > h3 {
  font-size: 18px;
  font-weight: 600;
  margin: var(--halva-gap) 0 8px;
  color: var(--el-text-color-primary);
}
/* A detail page's own title reads as the page title. */
.halva-shell h2 {
  font-size: var(--text-3xl);
  font-weight: var(--weight-semibold);
  line-height: 32px;
  letter-spacing: -0.01em;
  color: var(--fg);
}
.halva-shell h4 {
  font-size: 15px;
  font-weight: 600;
  margin: var(--halva-gap) 0 8px;
  color: var(--el-text-color-primary);
}
.halva-shell h5 {
  font-size: 13px;
  font-weight: 600;
  margin: 16px 0 4px;
  color: var(--el-text-color-regular);
}
.halva-flags {
  /* Was 1000px, which cut the publish and results tables off. The tables set
     their own width; prose and fields are held in by --halva-measure. */
  max-width: 1180px;
}

/* ---------- Panels: a fieldset is a card ---------- */

/* `disabled` on the fieldset is how every one of these forms greys itself out
   while a request is in flight, so the element stays — it is given the card
   the markup was asking for. */
.halva-shell fieldset,
.halva-reporting .halva-card {
  margin: var(--halva-gap) 0;
  padding: 16px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.halva-shell fieldset:disabled {
  /* 0.65 on the whole card dimmed its own border and heading too. Element
     greys the controls; the panel stays put. */
  opacity: 1;
  background: var(--el-fill-color-lighter);
}
.halva-shell legend {
  padding: 0 8px 0 0;
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  color: var(--fg);
}

/* The fields of a panel flow into as many columns as fit. A field whose value
   is a JSON document, and the nested variation editor, take the full row. */
.halva-shell fieldset {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  gap: 4px var(--halva-gap);
  align-content: start;
}
.halva-shell fieldset > :not(label),
.halva-shell fieldset > label:has(textarea),
.halva-shell fieldset > label.halva-check {
  grid-column: 1 / -1;
}

/* ---------- Fields ---------- */

.halva-shell label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 10px 0;
  min-width: 0;
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  color: var(--fg);
}
.halva-shell input:not([type="checkbox"]):not([type="radio"]),
.halva-shell textarea,
.halva-shell select {
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
  padding: 1px 10px;
  font-weight: var(--weight-regular);
  font-family: inherit;
  font-size: var(--el-font-size-base);
  color: var(--el-text-color-regular);
  background: var(--el-fill-color-blank);
  border: none;
  border-radius: var(--el-border-radius-base);
  /* Element draws its field border as an inset shadow, which is why a focused
     field can change colour without the text shifting by a pixel. */
  box-shadow: 0 0 0 1px var(--el-border-color) inset;
  transition: box-shadow 0.15s ease-out;
}
.halva-shell input:not([type="checkbox"]):not([type="radio"]),
.halva-shell select {
  height: var(--el-component-size);
  line-height: calc(var(--el-component-size) - 2px);
}
.halva-shell select {
  /* A native select keeps its own popup — the one control here the kit cannot
     replace without taking the `<select>` out of the accessibility tree — so
     it gets the field's shell and room for its own arrow. */
  padding-right: 28px;
  cursor: pointer;
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--el-text-color-placeholder) 50%),
    linear-gradient(135deg, var(--el-text-color-placeholder) 50%, transparent 50%);
  background-position: calc(100% - 15px) center, calc(100% - 10px) center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.halva-shell textarea {
  padding: 6px 10px;
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.5;
  resize: vertical;
}
.halva-shell input:not([type="checkbox"]):not([type="radio"]):hover,
.halva-shell textarea:hover,
.halva-shell select:hover {
  box-shadow: 0 0 0 1px var(--el-border-color-hover, var(--el-text-color-placeholder)) inset;
}
.halva-shell input:not([type="checkbox"]):not([type="radio"]):focus,
.halva-shell textarea:focus,
.halva-shell select:focus {
  outline: none;
  box-shadow: 0 0 0 1px var(--accent) inset, var(--ring);
}
.halva-shell input:disabled,
.halva-shell textarea:disabled,
.halva-shell select:disabled,
.halva-shell fieldset:disabled input,
.halva-shell fieldset:disabled textarea,
.halva-shell fieldset:disabled select {
  color: var(--el-disabled-text-color);
  -webkit-text-fill-color: var(--el-disabled-text-color);
  background: var(--el-disabled-bg-color);
  box-shadow: 0 0 0 1px var(--el-disabled-border-color) inset;
  cursor: not-allowed;
}
.halva-shell input[readonly] {
  color: var(--el-text-color-secondary);
  background: var(--el-fill-color-light);
}
.halva-shell input[type="checkbox"],
.halva-shell input[type="radio"] {
  width: 16px;
  height: 16px;
  flex: none;
  margin: 0;
  accent-color: var(--el-color-primary);
  cursor: pointer;
}
/* A checkbox reads as one line: the box, then its words. */
.halva-shell .halva-check,
.halva-shell label:has(> input[type="checkbox"]),
.halva-shell label:has(> input[type="radio"]) {
  flex-direction: row;
  align-items: center;
  gap: 8px;
  font-size: var(--el-font-size-base);
  color: var(--el-text-color-regular);
  cursor: pointer;
}

/* A paragraph inside a panel explains the field above it — `aria-describedby`
   points at several of them — so it is sized as help text, not as body copy. */
.halva-shell fieldset > p,
.namespace-ux fieldset > p {
  max-width: var(--halva-measure);
  margin: 2px 0 10px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--el-text-color-secondary);
}

/* The JSON example under the namespace field, and every other inline literal. */
.halva-shell code {
  justify-self: start;
  padding: 1px 6px;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--fg);
  background: var(--muted);
  border: none;
  border-radius: var(--radius-sm);
  overflow-wrap: anywhere;
}
.halva-shell pre {
  max-width: 100%;
  padding: 12px;
  font-size: 12px;
  line-height: 1.5;
  background: var(--el-fill-color-light);
  border: 1px solid var(--el-border-color-lighter);
  border-radius: var(--el-border-radius-base);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.halva-shell details > summary {
  cursor: pointer;
  color: var(--el-color-primary);
}

/* ---------- Buttons ---------- */

/* The screens that reach for a raw `<button class="el-button">` get the kit's
   button from ui.css already; all they need is the rhythm a row of them wants. */
.halva-shell .el-button {
  margin: 0 8px 8px 0;
}
.halva-shell fieldset > .el-button {
  justify-self: start;
  margin-top: 8px;
}

/* ---------- Messages ---------- */

/* `role=alert` is where every screen puts a failed request, `role=status` a
   result, `role=note` a rule that cannot be undone. Empty most of the time,
   hence `:not(:empty)` — an empty box would leave a hole in the page. */
.halva-shell [role="alert"]:not(:empty),
.halva-shell [role="note"]:not(:empty),
.halva-flags [role="status"]:not(:empty) {
  max-width: var(--halva-measure);
  margin: 12px 0;
  padding: 10px 14px;
  font-size: var(--text-sm);
  line-height: 1.5;
  border-radius: var(--radius-md);
  white-space: pre-wrap;
}
.halva-shell [role="alert"]:not(:empty) {
  color: var(--danger);
  background: var(--danger-soft);
}
.halva-shell [role="note"]:not(:empty) {
  color: color-mix(in srgb, var(--warning) 70%, black);
  background: var(--warning-soft);
}
.halva-flags [role="status"]:not(:empty) {
  color: var(--fg-muted);
  background: var(--muted);
}
/* The list's "6 of 12 flags" line is a footnote, not a message. */
.halva-flags .list-count[role="status"]:not(:empty) {
  padding: 0;
  background: none;
  font-size: var(--text-sm);
}

/* ---------- Tables ---------- */

.halva-shell table {
  width: 100%;
  margin: 12px 0 var(--halva-gap);
  font-size: var(--text-base);
  border-collapse: separate;
  border-spacing: 0;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.halva-shell caption {
  padding-bottom: 8px;
  font-size: 13px;
  color: var(--el-text-color-secondary);
  text-align: left;
}
.halva-shell th,
.halva-shell td {
  padding: 9px 12px;
  text-align: left;
  vertical-align: middle;
  border-bottom: 1px solid var(--border-row);
  overflow-wrap: anywhere;
}
.halva-shell tbody tr:last-child > td { border-bottom: none; }
.halva-shell th {
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-muted);
  background: var(--bg-subtle);
  white-space: nowrap;
  border-bottom: 1px solid var(--border);
}
.halva-shell tbody tr:hover > td {
  background: var(--bg-subtle);
}
/* The key column: the link is the row's name, the description under it a note. */
.halva-shell tbody td > a {
  font-family: var(--font-mono);
  font-weight: var(--weight-regular);
  color: var(--accent-ink);
}
.halva-shell tbody td > p {
  margin: 2px 0 0;
  font-size: 12px;
  color: var(--el-text-color-secondary);
}

/* The namespace allocation table scrolls sideways rather than wrapping its
   cells: the ranges either side of a claim only read as ranges on one line. */
.halva-shell .namespace-table {
  overflow-x: auto;
  border: 1px solid var(--el-border-color-lighter);
  border-radius: var(--el-border-radius-base);
}
.halva-shell .namespace-table table {
  /* Wider than a phone on purpose — `.namespace-table` is a focusable region
     and ArrowRight is expected to scroll it (scripts/smoke-halva-namespace-ux.cjs). */
  min-width: 520px;
  margin: 0;
}
.halva-shell .namespace-table th,
.halva-shell .namespace-table td {
  white-space: nowrap;
}
.halva-shell .namespace-table:focus-visible {
  outline: 2px solid var(--el-color-primary);
  outline-offset: 2px;
}

/* ---------- Namespaces (pages/halva_namespaces.rs) ---------- */

.namespace-ux {
  /* Capped, or the allocation bar and its table run the full width of a
     desktop window while the prose beside them stops at 78 characters. */
  max-width: min(100%, 960px);
  overflow-wrap: anywhere;
}
/* Every field here is followed by a paragraph explaining it, so the panel reads
   down one column rather than wrapping into three of a third the width. */
.namespace-ux fieldset {
  grid-template-columns: minmax(0, 520px);
}
.namespace-actions,
.namespace-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}
.namespace-legend {
  margin: 8px 0 0;
  font-size: 12px;
  color: var(--el-text-color-secondary);
}
.namespace-summary {
  font-weight: 600;
}

/* The traffic groups, one card per group; the selected one is pressed. */
.namespace-catalogue {
  list-style: none;
  padding: 0;
  display: grid;
  gap: 8px;
  max-width: var(--halva-measure);
}
.namespace-catalogue li {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 12px;
  align-items: center;
  padding: 12px 14px;
  font-size: 13px;
  color: var(--el-text-color-secondary);
  background: var(--el-bg-color);
  border: 1px solid var(--el-border-color-light);
  border-radius: var(--el-border-radius-base);
}
.namespace-catalogue li:hover {
  border-color: var(--el-color-primary-light-7);
}
/* The group's key is a toggle button, not a link — it selects the group in
   place. Element's plain-primary button is the right weight for it. */
.namespace-catalogue li > button {
  font-size: var(--el-font-size-base);
  font-weight: var(--el-font-weight-primary);
}
.namespace-catalogue li > span {
  font-size: 13px;
  color: var(--el-text-color-secondary);
}
.namespace-catalogue [aria-pressed="true"] {
  outline: 2px solid var(--el-color-primary);
  outline-offset: 2px;
}

/* The allocation bar. The track's dark border and the fill's dark blue are
   held from the version that was validated for contrast — a lighter fill on a
   light track is what the a11y pass rejected (docs/ux-namespaces-handoff.md).
   Its height and its span count are asserted, so neither is negotiable: no
   padding on a span, and nothing else nested inside. */
.halva-allocation {
  position: relative;
  height: 32px;
  margin-top: 12px;
  background: var(--el-fill-color);
  border: 1px solid var(--el-text-color-regular);
  border-radius: var(--el-border-radius-base);
  overflow: hidden;
}
.halva-allocation span {
  position: absolute;
  height: 100%;
  padding: 0;
  background: #2876c7;
  border-right: 1px solid var(--el-bg-color);
}

/* ---------- The variation editor (pages/halva_experiments.rs) ---------- */

/* Was a `<style>` element in the component's own view. `overflow-wrap` and the
   `min-width: 0` on every control are what keep `scrollWidth <= clientWidth`
   at 390px, which the experiments smoke script asserts. */
.variation-editor {
  overflow-wrap: anywhere;
}
.variation-editor > h3 {
  margin-top: 24px;
  font-size: 18px;
  font-weight: 600;
  color: var(--el-text-color-primary);
}
.variation-editor .variation-help {
  max-width: var(--halva-measure);
  margin: 4px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--el-text-color-secondary);
}
/* One card per arm, nested inside the panel's own card: a tint instead of a
   second shadow, so the nesting reads as grouping and not as depth. */
.variation-editor fieldset {
  min-width: 0;
  margin: 12px 0;
  background: var(--el-fill-color-lighter);
  box-shadow: none;
}
.variation-editor legend {
  padding: 0 8px;
  font-size: 13px;
  background: var(--el-bg-color);
  border: 1px solid var(--el-border-color-light);
  border-radius: 10px;
}
.variation-editor .variation-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  gap: 0 16px;
  grid-column: 1 / -1;
}
.variation-editor .variation-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}
.variation-editor .variation-actions .el-button {
  margin: 0;
}
/* Removing an arm is destructive and rare: it sits at the end of its own card,
   quiet until pointed at. */
.variation-editor fieldset > .el-button {
  margin-top: 12px;
  justify-self: start;
}
.variation-editor .variation-validation:not(:empty) {
  max-width: var(--halva-measure);
  margin-top: 12px;
  padding: 10px 14px;
  font-size: 13px;
  color: var(--el-color-danger-dark-2);
  background: var(--el-color-danger-light-9);
  border-left: 3px solid var(--el-color-danger);
  border-radius: var(--el-border-radius-base);
}

/* ---------- The metric chooser (pages/halva_reporting.rs) ---------- */

/* Also a `<style>` element in its own view until now. */
.metric-chooser {
  max-width: var(--halva-measure);
  margin: var(--halva-gap) 0;
  padding: 12px 16px;
  background: var(--el-bg-color);
  border: 1px solid var(--el-border-color-light);
  border-radius: var(--el-border-radius-base);
}
.metric-chooser > summary {
  font-weight: var(--el-font-weight-primary);
  color: var(--el-text-color-primary);
}
.metric-chooser[open] > summary {
  margin-bottom: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--el-border-color-lighter);
}
.metric-chooser .metric-choice {
  display: grid;
  gap: 6px;
  padding: 12px 0;
  border-bottom: 1px solid var(--el-border-color-lighter);
  overflow-wrap: anywhere;
}
.metric-chooser .metric-choice:last-child {
  border-bottom: none;
}
.metric-chooser fieldset {
  min-width: 0;
  display: block;
  box-shadow: none;
}
.metric-chooser fieldset label,
.metric-chooser .metric-choice label {
  flex-direction: row;
  align-items: center;
  gap: 8px;
  margin: 0;
}
.metric-chooser .metric-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 12px;
}
.metric-chooser .metric-help {
  max-width: var(--halva-measure);
  margin: 0;
  font-size: 12px;
  color: var(--el-text-color-secondary);
}
.metric-chooser input[type="checkbox"],
.metric-chooser input[type="radio"] {
  width: 14px;
  flex: none;
  margin: 0;
}

/* ---------- Preview, publishes, settings (pages/halva_reporting.rs) ---------- */

.halva-reporting .halva-card > h4 {
  margin-top: 0;
}
.halva-reporting .halva-card > p:empty {
  display: none;
}
.halva-reporting .stats {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin: 4px 0 var(--halva-gap);
}
/* A figure and its caption, boxed: the health numbers above the results table. */
.halva-reporting .stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 140px;
  padding: 12px 16px;
  background: var(--el-fill-color-light);
  border: 1px solid var(--el-border-color-lighter);
  border-radius: var(--el-border-radius-base);
}
.halva-reporting .stat__value {
  font-size: 22px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--el-text-color-primary);
  font-variant-numeric: tabular-nums;
}
.halva-reporting .stat__label {
  font-size: 12px;
  color: var(--el-text-color-secondary);
}
.halva-reporting .banner--error {
  font-weight: var(--el-font-weight-primary);
}
/* Not every warning is a failure: a fixed-horizon note is advice about how to
   read the numbers, and reads as advice rather than as a fourth alarm. */
.halva-reporting .banner--info[role="alert"]:not(:empty) {
  color: var(--el-color-info-dark-2);
  background: var(--el-color-info-light-9);
  border-left-color: var(--el-color-info);
}
.halva-reporting .stat__value--bad {
  color: var(--el-color-danger);
  font-weight: 700;
}
.halva-reporting .badge {
  margin-left: 8px;
  padding: 1px 8px;
  font-size: 12px;
  font-weight: var(--el-font-weight-primary);
  color: var(--el-text-color-regular);
  background: var(--el-fill-color);
  border-radius: 10px;
}
.halva-reporting .table-scroll {
  overflow-x: auto;
}
/* The share each arm actually got, against the share it was meant to get. */
.halva-reporting .bar {
  display: block;
  width: 100px;
  background: var(--el-fill-color);
  border-radius: 2px;
  overflow: hidden;
}
.halva-reporting .bar__fill {
  display: block;
  height: 10px;
  background: var(--el-color-primary);
}

/* ---------- The results screen (pages/halva_results.rs) ---------- */

/* This screen was written against a vocabulary no stylesheet ever defined —
   `.page`, `.page-head`, `.eyebrow`, `.stat`, `.table` — so until now it
   rendered as bare markup. The names are kept and given rules. */
.halva-reporting .page {
  display: block;
}
.halva-reporting .page-head {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  align-items: flex-start;
  justify-content: space-between;
  margin: var(--halva-gap) 0;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--el-border-color-light);
}
.halva-reporting .eyebrow {
  margin: 0;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--el-text-color-secondary);
}
.halva-reporting .page-head h1 {
  margin: 2px 0 0;
  font-size: 24px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--el-text-color-primary);
}
.halva-reporting .page-subtitle {
  margin: 6px 0 0;
  font-size: 13px;
  color: var(--el-text-color-secondary);
}
.halva-reporting .page-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
/* `<a class="button">`: a link that acts as one, so it is given the kit's
   default button rather than a second look of its own. */
.halva-reporting a.button {
  display: inline-flex;
  align-items: center;
  height: var(--el-component-size);
  padding: 0 15px;
  font-size: var(--el-font-size-base);
  font-weight: var(--el-font-weight-primary);
  color: var(--el-text-color-regular);
  background: var(--el-fill-color-blank);
  border: 1px solid var(--el-border-color);
  border-radius: var(--el-border-radius-base);
}
.halva-reporting a.button:hover {
  color: var(--el-color-primary);
  background: var(--el-color-primary-light-9);
  border-color: var(--el-color-primary-light-7);
}
.halva-reporting .placeholder {
  margin: 12px 0;
  padding: 24px;
  font-size: 13px;
  color: var(--el-text-color-secondary);
  text-align: center;
  background: var(--el-fill-color-lighter);
  border: 1px dashed var(--el-border-color);
  border-radius: var(--el-border-radius-base);
}
.halva-reporting .muted {
  color: var(--el-text-color-secondary);
}
/* Numbers are read down a column, so they line up and share a width. */
.halva-reporting .table td:not(:first-child),
.halva-reporting .table th:not(:first-child) {
  font-variant-numeric: tabular-nums;
}
.halva-reporting .bar-cell {
  width: 120px;
}

/* ---------- Narrow viewports ---------- */

@media (max-width: 640px) {
  .halva-shell {
    padding: 16px 12px 32px;
  }
  .halva-nav {
    gap: 0;
  }
  .halva-nav__items > a {
    padding: 0 10px;
  }
  .halva-shell fieldset,
  .halva-reporting .halva-card {
    padding: 14px;
  }
  /* A card inside a card would otherwise cost 28px of the 390px twice over. */
  .variation-editor fieldset,
  .metric-chooser {
    padding: 12px;
  }
}

/* ---------------------------------------------------------------- create dialog

   Creating an experiment is a modal asking four things, not a fieldset above the
   list. The dialog's own chrome comes from `.el-dialog`; these rules only lay the
   questions out and pin the actions to the bottom of the body, because the footer
   slot sits outside the <form> and a submit button has to be inside one. */

/* `.halva-shell fieldset` is a card in a two-column grid — right for the definition
   cards, wrong for four stacked questions, which is what put the name field at half
   width beside nothing. The card's paint has to go with its layout: dropping the border
   and padding alone left the fill and the shadow drawing a ghost panel around the
   questions, and `fieldset:disabled` repainted the whole body grey while a create was in
   flight. Element greys the controls themselves, which is all a dialog needs. */
.halva-dialog .halva-dialog-form fieldset,
.halva-dialog .halva-dialog-form fieldset:disabled {
  display: block;
  margin: 0;
  padding: 0;
  min-width: 0;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
/* A field fills the row. The checkbox is excluded by name: this selector outweighs the
   shell's `input[type="checkbox"]` sizing, and stretched the "Exempt from stale report"
   box across the whole flag dialog. */
.halva-dialog .halva-dialog-form label > input:not([type="checkbox"]):not([type="radio"]),
.halva-dialog .halva-dialog-form label > textarea {
  width: 100%;
}
/* The shell sets textareas in mono because most of them hold JSON. A hypothesis
   is a sentence. */
.halva-dialog .halva-dialog-form label > textarea {
  font-family: inherit;
  font-size: var(--el-font-size-base);
  line-height: 1.5;
}
/* One question per row. The label keeps the shell's flex column — its 6px gap is what
   holds the caption off the top of its field, and a `.halva-check` still reads as a row
   — so only the rhythm between questions is set here; `display: block` was undoing both. */
.halva-dialog .halva-dialog-form label {
  margin: 0 0 16px;
  font-weight: 500;
}
/* A note belongs to the field above it, so the gap closes above and the 16px falls
   below. It was `margin-top: -8px` under a 4px label, which came to -4px and laid the
   note over the bottom edge of the input. */
.halva-dialog .halva-dialog-form label:has(+ .halva-field-note) {
  margin-bottom: 4px;
}
.halva-dialog .halva-field-note {
  margin: 0 0 16px;
  font-size: 12.5px;
  color: var(--el-text-color-secondary, #909399);
}
.halva-dialog .halva-dialog-error:empty {
  display: none;
}
.halva-dialog .halva-dialog-error {
  margin: 0 0 12px;
  padding: 8px 12px;
  border-radius: 4px;
  background: var(--el-color-danger-light-9, #fef0f0);
  color: var(--el-color-danger, #f56c6c);
  font-size: 13px;
}
.halva-dialog .halva-dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding-top: 16px;
  border-top: 1px solid var(--el-border-color-lighter, #ebeef5);
}
/* The shell spaces its toolbar buttons with a margin of their own. Here the row's gap
   does that, and the margin only held Create 8px short of the field edge above it and
   left 8px of dead space under the row. */
.halva-dialog .halva-dialog-actions .el-button {
  margin: 0;
}
.halva-dialog .halva-dialog-aside {
  margin: 0 0 12px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--el-text-color-secondary, #909399);
}

/* ------------------------------------------------------------------ rule builder

   One card per rule, reading as a sentence — the shape GrowthBook gives the same
   information, and the reason the JSON textarea stopped being the only way to write
   a rule. The JSON view survives under `.rule-json`, as the escape hatch for the
   targeting the cards carry but do not draw.

   Two things above already have a claim on this markup and are worked with rather
   than against: `.halva-shell li` holds list items to --halva-measure, which a card
   is not; and the field rules carry enough specificity (a class plus two :not()
   attribute selectors) that fighting them for a border would need four classes. So
   the fields keep the kit's look and the cards size them with flex-basis, which
   takes precedence over `width` for a flex item without any specificity contest. */

.rule-builder {
  margin-top: 24px;
}
.rule-builder__head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--el-border-color-lighter);
}
.rule-builder__head h4 {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
}
.rule-builder__count {
  min-width: 20px;
  padding: 0 6px;
  border-radius: 10px;
  background: var(--el-fill-color);
  color: var(--el-text-color-secondary);
  font-size: 12px;
  line-height: 18px;
  text-align: center;
}
.rule-builder__note {
  flex: 1 1 320px;
  margin: 0;
  font-size: 12.5px;
  color: var(--el-text-color-secondary);
}
.rule-builder__empty {
  margin: 16px 0;
  padding: 20px;
  border: 1px dashed var(--el-border-color);
  border-radius: 6px;
  text-align: center;
  color: var(--el-text-color-secondary);
}

.rule-list {
  list-style: none;
  margin: 16px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
/* A card is not prose; --halva-measure would cut it at 44rem. */
.halva-shell .rule-list > li {
  max-width: none;
}

.rule-card {
  border: 1px solid var(--el-border-color);
  border-left: 3px solid var(--el-color-primary);
  border-radius: 6px;
  background: var(--el-bg-color);
}
.rule-card.is-dragging {
  opacity: 0.45;
}
.rule-card__head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--el-border-color-lighter);
}
.rule-card__grip {
  cursor: grab;
  color: var(--el-text-color-placeholder);
  letter-spacing: -2px;
}
.rule-card__ordinal {
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--el-fill-color);
  color: var(--el-text-color-secondary);
  font-size: 11px;
  line-height: 20px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.halva-shell .rule-card__title {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--el-text-color-primary);
}
.rule-card__experiment {
  font-weight: 500;
  color: var(--el-color-primary);
}
.rule-card__env {
  margin-left: auto;
  font-size: 12px;
  color: var(--el-text-color-secondary);
}
.rule-card__reorder {
  display: flex;
  gap: 2px;
}
.rule-card__move {
  width: 24px;
  height: 24px;
  border: 1px solid transparent;
  border-radius: 4px;
  background: none;
  color: var(--el-text-color-secondary);
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
}
.rule-card__move:hover:not(:disabled) {
  border-color: var(--el-border-color);
  color: var(--el-text-color-primary);
}
.rule-card__move--remove:hover:not(:disabled) {
  border-color: var(--el-color-danger);
  color: var(--el-color-danger);
}
.rule-card__move:disabled {
  opacity: 0.35;
  cursor: default;
}
.halva-shell .rule-card__warn {
  margin: 4px 0 0 82px;
  font-size: 12.5px;
  color: var(--el-color-warning);
}

.rule-card__body {
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* Two classes, so a `.rule-line` that happens to be a <label> is still a row:
   `.halva-shell label` would otherwise stack it and turn every flex-basis below
   into a height. */
.rule-card .rule-line {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin: 0;
  font-size: 13px;
}
.rule-card .rule-line--stacked {
  align-items: flex-start;
}
/* The keyword column: a fixed rail, so the sentences line up down the card. */
.rule-word {
  flex: none;
  width: 74px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--el-text-color-secondary);
}
.rule-inline {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  flex: 1 1 auto;
  min-width: 0;
}
.rule-condition {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
.halva-shell .rule-clause code {
  font-size: 12px;
}

/* Field widths, set as flex-basis rather than width — see the note at the top. */
.rule-card .rule-line--description input {
  flex: 1 1 240px;
  max-width: 460px;
}
/* A field with nothing more specific to say sits at a readable default rather
   than stretching to the card, which is what `width: 100%` above would do. */
.rule-card .rule-input {
  flex: 0 1 280px;
}
.rule-card .rule-input--percent {
  flex: 0 0 84px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.rule-card .rule-input--attribute,
.rule-card .rule-input--value {
  flex: 0 1 180px;
}
.rule-card .rule-input--kind {
  flex: 0 1 116px;
}
.rule-card select.rule-input {
  flex: 0 1 280px;
}
.rule-card textarea.rule-input--json {
  flex: 1 1 260px;
  max-width: 460px;
}

/* Coverage and splits drawn rather than typed — the difference the handoff called
   "drawn as coverage → split with weighted bars, not inputs". 82px is the keyword
   rail plus its gap, so a bar starts where its sentence does. */
.rule-traffic {
  display: flex;
  height: 8px;
  /* Same rail and same width as the arms list above it, so the bar reads as a
     picture of that list rather than as a separate measurement. */
  margin-left: 82px;
  max-width: 460px;
  border-radius: 4px;
  overflow: hidden;
  background: var(--el-fill-color);
}
.rule-traffic__fill {
  background: var(--el-color-primary);
}
.rule-traffic__arm--0 { background: #409eff; }
.rule-traffic__arm--1 { background: #16a34a; }
.rule-traffic__arm--2 { background: #e6a23c; }
.rule-traffic__arm--3 { background: #a855f7; }
.rule-traffic__arm--4 { background: #ef4444; }
.rule-traffic__arm--5 { background: #0891b2; }

.rule-arms {
  flex: 1 1 260px;
  max-width: 460px;
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--el-border-color-lighter);
  border-radius: 4px;
}
.halva-shell .rule-arms > li {
  max-width: none;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  font-size: 13px;
}
.rule-arms li + li {
  border-top: 1px solid var(--el-border-color-lighter);
}
.rule-arm__index {
  flex: none;
  width: 18px;
  height: 18px;
  border: 1px solid var(--el-border-color);
  border-radius: 50%;
  font-size: 10px;
  line-height: 16px;
  text-align: center;
  color: var(--el-text-color-secondary);
}
.rule-arm__key {
  flex: 1;
}
.rule-arm__weight {
  font-variant-numeric: tabular-nums;
  color: var(--el-text-color-secondary);
}

.rule-pill {
  padding: 1px 8px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 600;
  text-transform: capitalize;
  background: var(--el-fill-color);
  color: var(--el-text-color-secondary);
}
.rule-pill--running { background: #e7f6ec; color: #16a34a; }
.rule-pill--stopped { background: #fdeaea; color: #f56c6c; }

.rule-builder__add {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin: 16px 0;
}
.halva-shell .rule-builder__error:empty {
  display: none;
}
.halva-shell .rule-builder__error {
  margin: 0 0 12px;
  font-size: 13px;
  color: var(--el-color-danger);
}

.rule-json {
  margin-top: 20px;
  padding-top: 12px;
  border-top: 1px solid var(--el-border-color-lighter);
}
.halva-shell .rule-json > p {
  font-size: 12.5px;
  color: var(--el-text-color-secondary);
}
.rule-json__actions {
  display: flex;
  gap: 8px;
  margin: 8px 0;
}

@media (max-width: 640px) {
  .rule-word {
    width: 100%;
  }
  .rule-traffic,
  .halva-shell .rule-card__warn {
    margin-left: 0;
  }
}

/* ------------------------------------------------------- entity detail pages

   A record's page states what it is before it offers to change it: a title with
   its status, a strip of metadata, then cards. Editing is per-card and reveals
   the same fields as before, so nothing about what is valid moved here. */

.entity-head {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 8px;
}
.entity-head__title {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1 1 auto;
  min-width: 0;
}
.halva-shell .entity-head__title h3 {
  margin: 0;
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}
/* A flag's name is its key, so the title is a <code> — but at title size, without
   the pill the shell gives inline code elsewhere. */
.halva-shell .entity-head__title h3 code {
  font-size: inherit;
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
}
.entity-head__actions {
  display: flex;
  gap: 8px;
  flex: none;
}

/* A status is an Atlas badge, toned by the notes board's table: live and running
   success, stale warning, stopped danger, draft and archived neutral, exempt outline. */
.status-pill {
  display: inline-flex;
  align-items: center;
  flex: none;
  height: 18px;
  padding: 0 8px;
  box-sizing: border-box;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  line-height: 16px;
  background: var(--muted);
  color: var(--fg);
  white-space: nowrap;
}
.status-pill--running,
.status-pill--live {
  border-color: transparent;
  background: var(--success-soft);
  color: var(--success);
}
.status-pill--stopped {
  border-color: transparent;
  background: var(--danger-soft);
  color: var(--danger);
}
.status-pill--archived {
  background: var(--muted);
  color: var(--fg);
}

/* One row of facts, wrapping — a strip, not a definition list stacked down the
   page, which is what `<dl>` would otherwise give. */
.entity-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 28px;
  margin: 10px 0 20px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--el-border-color-lighter);
}
.entity-meta > div {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-size: 13px;
}
.entity-meta dt {
  color: var(--el-text-color-secondary);
}
.entity-meta dd {
  margin: 0;
  color: var(--el-text-color-primary);
}

.entity-card {
  margin: 0 0 16px;
  padding: 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
}
.entity-card--danger {
  border-color: var(--el-color-danger-light-7, #fab6b6);
}
.entity-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}
.halva-shell .entity-card__head h4 {
  margin: 0;
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
}
.halva-shell .entity-card__prose {
  margin: 0;
  font-size: 14px;
  line-height: 1.6;
}
.halva-shell .entity-card__prose.is-empty,
.halva-shell .entity-card__note {
  color: var(--el-text-color-secondary);
  font-size: 12.5px;
}
.halva-shell .entity-card__note {
  margin: 10px 0 0;
}
.entity-card__actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 12px;
}
/* The card supplies the panel, so the fieldset inside it should not draw a second
   one — but it keeps the two-column grid the definition forms are laid out on. */
.entity-card fieldset {
  border: 0;
  padding: 0;
  margin: 0;
  background: none;
}

/* ---------- allocation, drawn ---------- */

.alloc {
  display: flex;
  flex-direction: column;
  align-items: stretch;
}
.alloc__step {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
  padding: 12px 14px;
  border: 1px solid var(--el-border-color-lighter);
  border-radius: 6px;
  background: var(--el-fill-color-light, #f5f7fa);
  font-size: 13px;
}
.alloc__label {
  flex: none;
  width: 64px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--el-text-color-secondary);
}
.alloc__value {
  color: var(--el-text-color-primary);
}
.alloc__aside {
  margin-left: auto;
  color: var(--el-text-color-secondary);
}
.alloc__bar {
  flex: 1 0 100%;
  height: 8px;
  margin-top: 8px;
  border-radius: 4px;
  overflow: hidden;
  background: var(--el-border-color-lighter);
}
.alloc__bar-fill {
  height: 100%;
  background: var(--el-color-primary);
}
.alloc__arrow {
  align-self: center;
  padding: 6px 0;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--el-text-color-secondary);
}
.alloc__arms {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 12px;
  list-style: none;
  margin: 0;
  padding: 0;
}
/* The colour is the variation's identity across the page — the same order the
   rule cards' split bar uses. */
.halva-shell .alloc-arm {
  max-width: none;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 12px 14px;
  border: 1px solid var(--el-border-color-lighter);
  border-top: 3px solid var(--el-text-color-placeholder);
  border-radius: 4px;
}
/* Two classes, to clear the grey the base rule sets in `.halva-shell .alloc-arm`. */
.halva-shell .alloc-arm--0 { border-top-color: #409eff; }
.halva-shell .alloc-arm--1 { border-top-color: #16a34a; }
.halva-shell .alloc-arm--2 { border-top-color: #e6a23c; }
.halva-shell .alloc-arm--3 { border-top-color: #a855f7; }
.halva-shell .alloc-arm--4 { border-top-color: #ef4444; }
.halva-shell .alloc-arm--5 { border-top-color: #0891b2; }
.alloc-arm__head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.alloc-arm__index {
  flex: none;
  width: 18px;
  height: 18px;
  border: 1px solid var(--el-border-color);
  border-radius: 50%;
  font-size: 10px;
  line-height: 16px;
  text-align: center;
  color: var(--el-text-color-secondary);
}
.alloc-arm__name {
  font-weight: 600;
  font-size: 14px;
}
.halva-shell .alloc-arm__value {
  align-self: flex-start;
  max-width: 100%;
  overflow-wrap: anywhere;
}
.halva-shell .alloc-arm__split {
  margin: 0;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.halva-shell .alloc-arm__key {
  margin: 0;
  font-size: 12px;
  color: var(--el-text-color-secondary);
}
.halva-shell .alloc__note {
  margin: 12px 0 0;
  font-size: 12.5px;
  color: var(--el-text-color-secondary);
}

.linked-flags {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.halva-shell .linked-flags > li {
  max-width: none;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* --------------------------------------------------------------- list chrome

   Status tabs carrying counts, a search box, and headers that sort — the three
   things both lists were missing, and the reason a project with more than a
   handful of flags had no way to find one. */

.list-toolbar {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  flex-wrap: wrap;
  margin: 0;
}
/* A list is a card: the status tabs and search over the table, the count under it. */
.halva-shell .a-card.halva-list { gap: var(--space-3); }
.halva-shell .halva-list table { margin: 0; }
.halva-flags .halva-list .list-count:not(:empty) { margin: 0; }
.list-toolbar > .el-button {
  margin-left: auto;
}

.status-tabs {
  display: flex;
  gap: 2px;
  flex-wrap: wrap;
}
.status-tabs {
  gap: 20px;
}
.status-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 0 8px;
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  color: var(--fg-muted);
  font-size: var(--text-base);
  font-family: inherit;
  cursor: pointer;
}
.status-tab:hover {
  color: var(--fg);
}
.status-tab.is-current {
  border-bottom-color: var(--accent);
  color: var(--fg);
  font-weight: var(--weight-semibold);
}
.status-tab__count {
  min-width: 18px;
  padding: 0 5px;
  border-radius: 9px;
  background: var(--el-fill-color);
  color: var(--el-text-color-secondary);
  font-size: 11px;
  line-height: 17px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  font-weight: 400;
}
.status-tab.is-current .status-tab__count {
  background: var(--accent-soft);
  color: var(--accent-ink);
}

.list-search {
  flex: 0 1 280px;
}
.halva-shell .list-search__input {
  width: 100%;
}

/* A sortable header is a button, so it is reachable from a keyboard and announces
   its direction through the `aria-sort` on the cell around it. */
.sort-header {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
}
.sort-header:hover {
  color: var(--fg);
}
.sort-header.is-current {
  color: var(--fg);
}
.sort-header__arrow {
  font-size: 10px;
  opacity: 0.5;
}
.sort-header.is-current .sort-header__arrow {
  opacity: 1;
}

.halva-shell .list-table td.is-numeric,
.halva-shell .list-table th:has(.sort-header[data-numeric]) {
  font-variant-numeric: tabular-nums;
}
.halva-shell .list-table td .status-pill + .status-pill {
  margin-left: 4px;
}
.status-pill--stale {
  border-color: transparent;
  background: var(--warning-soft);
  color: var(--warning);
}
/* `.halva-flags [role="status"]` is a banner — right for "Rules saved", wrong for a
   row count, which still wants the role so a filter change is announced. */
.halva-flags .list-count:not(:empty) {
  margin: 10px 0 0;
  padding: 0;
  border-left: 0;
  background: none;
  font-size: 12.5px;
  color: var(--el-text-color-secondary);
}
