/* ============================================================
   LIBRARY.CSS — shared library chrome
   ------------------------------------------------------------
   Chips, favourites, bulk selection, the level ladder, cover
   fallbacks, due panels and the generic wing library. Everything
   here is used by the Coding, Notes, Snippet and wing libraries
   alike (see js/library-common.js).
   ============================================================ */

/* --- Tree rows -------------------------------------------------------------
   A leaf has no chevron, but it still reserved the full 18px slot, which added
   a second level of apparent indentation on top of its padding. Narrow it. */
.tree-item-node .tree-node-chevron.invisible { width: 9px; min-width: 9px; }
/* Programs used to sit in a shorter row than the folders above them -- 36px
   against 44px -- which made them read as secondary and gave them a smaller
   target than the thing that merely contains them. They take the same
   padding as any other row now; the smaller icon and lighter label are what
   carry the hierarchy, not a cramped row. */

/* --- Collapsible filter shell ---
   The shell is raised above the grid on purpose. The menus it opens hang below
   its own row and over the cards, and a menu that resolves its z-index inside
   some ancestor's stacking context is at the mercy of whatever creates one up
   the tree — which is exactly how the View menu ended up under the folder tiles
   (a staggered entrance was leaving a transform on the row; see fadeInUp).
   Lifting the shell itself means the menus are above the content no matter what
   happens inside it. */
.lib-filter-shell {
  margin-bottom: 1rem;
  position: relative;
  z-index: 20;
}
.lib-filter-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.lib-filter-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.65rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--text-secondary);
  font-size: 0.76rem;
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
  transition: border-color var(--transition-fast), color var(--transition-fast);
}
.lib-filter-toggle:hover { color: var(--text-primary); border-color: var(--text-tertiary); }
.lib-filter-shell.open > .lib-filter-head > .lib-filter-toggle,
.lib-filter-toggle.has-active { border-color: var(--color-primary); color: var(--color-primary); }
/* One chevron that turns over, instead of swapping the icon on every toggle. */
.lib-filter-chev { transition: transform 0.22s ease; }
.lib-filter-shell.open .lib-filter-chev { transform: rotate(180deg); }
/* The summary of what's on is only useful while the panel that sets it is shut. */
.lib-filter-shell.open > .lib-filter-head > .lib-filter-active { display: none; }
.lib-filter-count {
  min-width: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--color-primary);
  color: #fff;
  font-size: 0.62rem;
  font-weight: 800;
  text-align: center;
}
.lib-filter-active { display: flex; align-items: center; gap: 0.3rem; flex-wrap: wrap; min-width: 0; }
.lib-active-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.18rem 0.5rem;
  border: 1px solid var(--color-primary);
  border-radius: 999px;
  background: var(--color-primary-subtle);
  color: var(--color-primary);
  font-size: 0.7rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.lib-active-pill:hover { filter: brightness(1.2); }
.lib-active-clear {
  border: none;
  background: none;
  color: var(--text-tertiary);
  font-size: 0.7rem;
  font-weight: 600;
  text-decoration: underline;
  cursor: pointer;
  padding: 0.18rem 0.2rem;
}
.lib-active-clear:hover { color: var(--text-primary); }
.lib-filter-tail { display: flex; align-items: center; gap: 0.4rem; margin-left: auto; }
.lib-filter-count-label { font-size: 0.72rem; color: var(--text-tertiary); white-space: nowrap; }
.lib-sort-select {
  font-size: 0.74rem;
  padding: 0.22rem 1.5rem 0.22rem 0.5rem;
  height: auto;
  width: auto;
}
/* A grid row that goes 0fr -> 1fr collapses to exactly the content's height
   without anyone having to guess a max-height, and it animates. The clip layer
   is what actually hides the overflow while the track is short. */
.lib-filter-panel {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows 0.22s ease, opacity 0.16s ease;
}
.lib-filter-shell.open > .lib-filter-panel { grid-template-rows: 1fr; opacity: 1; }
.lib-filter-clip { overflow: hidden; min-height: 0; }

.lib-filter-body {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  margin-top: 0.6rem;
  padding: 0.7rem 0.8rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
}

@media (prefers-reduced-motion: reduce) {
  .lib-filter-panel, .lib-filter-chev { transition: none; }
}
.lib-filter-body .lib-chip-row:empty { display: none; }
.lib-filter-body .lib-active-clear { align-self: flex-start; margin-top: 0.15rem; }

/* --- View menu: the controls that change LAYOUT, not contents --- */
.lib-view { position: relative; }
.lib-view > summary {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--text-secondary);
  font-size: 0.74rem;
  font-weight: 700;
  cursor: pointer;
  list-style: none;
  white-space: nowrap;
}
.lib-view > summary::-webkit-details-marker { display: none; }
.lib-view > summary:hover { color: var(--text-primary); border-color: var(--text-tertiary); }
.lib-view[open] > summary { border-color: var(--color-primary); color: var(--color-primary); }
.lib-view[open] > summary .lib-view-chev { transform: rotate(180deg); }
.lib-view-chev { transition: transform 0.18s ease; }
.lib-view-pop {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: 30;
  min-width: 268px;
  display: flex;
  flex-direction: column;
  padding: 0.35rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.45);
  /* A short window used to cut the menu off with no way to reach the rest. */
  max-height: min(60vh, 420px);
  overflow-y: auto;
  animation: libViewOpen 0.14s ease-out;
}
@keyframes libViewOpen {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}
.lib-view-title {
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  padding: 0.3rem 0.5rem 0.35rem;
}

/* Label on the left, control on the right, the way a settings menu reads. The
   markup is <input><span>, so the row is reversed rather than reordered in
   every library that builds one. */
.lib-view-row {
  display: flex;
  flex-direction: row-reverse;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  font-size: 0.78rem;
  color: var(--text-secondary);
  cursor: pointer;
  padding: 0.45rem 0.5rem;
  border-radius: var(--radius-md);
  transition: background-color var(--transition-fast);
}
.lib-view-row:hover { background: var(--bg-surface-hover); }
.lib-view-row > span:last-child { min-width: 0; }
.lib-view-row strong { display: block; color: var(--text-primary); font-weight: 700; font-size: 0.78rem; }
.lib-view-row em { display: block; font-style: normal; font-size: 0.7rem; color: var(--text-tertiary); margin-top: 1px; line-height: 1.35; }

/* A 14px tickbox for an on/off layout choice was easy to miss and hard to hit.
   Same input, drawn as a switch — no library needed a markup change. */
.lib-view-row input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  width: 34px;
  height: 19px;
  margin: 0;
  border-radius: 999px;
  border: 1px solid var(--border-color);
  cursor: pointer;
  /* The knob is painted by the input's own background rather than an ::after.
     Chrome generates the pseudo-element on an appearance:none input but does
     not restyle it when :checked flips — measured here: the track recoloured
     and the knob stayed put. background-position is on the element itself, so
     it responds, and it animates. */
  background-color: var(--bg-surface-hover);
  background-image: radial-gradient(circle closest-side, var(--text-tertiary) 100%, transparent 100%);
  background-repeat: no-repeat;
  background-size: 13px 13px;
  background-position: 2px center;
  transition: background-color var(--transition-fast), border-color var(--transition-fast),
              background-position 0.18s ease;
}
.lib-view-row input[type="checkbox"]:checked {
  background-color: var(--color-primary);
  border-color: var(--color-primary);
  background-image: radial-gradient(circle closest-side, #fff 100%, transparent 100%);
  background-position: 17px center;
}
.lib-view-row input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* Reversing the row puts the control on the right for the <input><span> rows,
   but this one is <span><span> and was already in that order. */
.lib-view-seg { flex-direction: row; cursor: default; }
.lib-view-seg:hover { background: transparent; }
/* Segmented, so the three options read as one control with one choice made. */
.lib-view-seg .lib-kind-toggle {
  gap: 0;
  padding: 2px;
  border-radius: 999px;
  background: var(--bg-surface-hover);
  border: 1px solid var(--border-color);
}
.lib-view-seg .lib-kind-toggle .lib-chip {
  border: none;
  background: transparent;
  border-radius: 999px;
  padding: 0.2rem 0.6rem;
}
.lib-view-seg .lib-kind-toggle .lib-chip.active {
  background: var(--color-primary);
  color: #fff;
  border: none;
}

.lib-view-sep { height: 1px; margin: 0.3rem 0.5rem; background: var(--border-color); }

/* --- Filter chips --- */
.lib-chip {
  font-size: 0.74rem;
  padding: 0.22rem 0.6rem;
  border-radius: 999px;
  border: 1px solid var(--border-color);
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  font-weight: 600;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  transition: border-color var(--transition-fast), background var(--transition-fast), color var(--transition-fast);
}
.lib-chip:hover { color: var(--text-primary); border-color: var(--text-tertiary); }
.lib-chip.active {
  border-color: var(--color-primary);
  background: var(--color-primary-subtle);
  color: var(--color-primary);
}
.lib-chip b { font-weight: 800; opacity: 0.7; }
.lib-chip-row { display: flex; gap: 0.3rem; align-items: center; flex-wrap: wrap; }
.lib-chip-sep { width: 1px; height: 18px; background: var(--border-color); flex-shrink: 0; }

/* Tags on cards are a filter control now, so they have to look pressable. */
.lib-tag-badge { cursor: pointer; }
.lib-tag-badge:hover { filter: brightness(1.25); }

/* --- Favourites --- */
.lib-fav-btn {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  z-index: 3;
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-color);
  border-radius: 50%;
  background: rgba(10, 14, 22, 0.72);
  color: var(--text-tertiary);
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--transition-fast), color var(--transition-fast), background var(--transition-fast);
}
.card:hover .lib-fav-btn,
.lib-fav-btn.on,
.lib-fav-btn:focus-visible { opacity: 1; }
.lib-fav-btn:hover { color: #fbbf24; border-color: #fbbf24; }
.lib-fav-btn.on { color: #fbbf24; border-color: rgba(251, 191, 36, 0.55); }

/* --- Review / status badges on cards --- */
.lib-due-badge {
  background: rgba(99, 102, 241, 0.16);
  color: #a5b4fc;
  border: 1px solid rgba(99, 102, 241, 0.4);
}
.lib-due-badge.over {
  background: rgba(245, 158, 11, 0.16);
  color: #fbbf24;
  border-color: rgba(245, 158, 11, 0.45);
}
.lib-next-badge { opacity: 0.75; }
.lib-resume-badge {
  background: rgba(16, 185, 129, 0.16);
  color: #34d399;
  border: 1px solid rgba(16, 185, 129, 0.4);
}
.lib-last-badge {
  background: rgba(148, 163, 184, 0.14);
  color: #cbd5e1;
  border: 1px solid rgba(148, 163, 184, 0.3);
}
.lib-lang-badge {
  flex-shrink: 0;
  padding: 1px 6px;
  border-radius: 4px;
  background: rgba(6, 182, 212, 0.16);
  border: 1px solid rgba(6, 182, 212, 0.35);
  color: #67e8f9;
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.04em;
}

/* --- Cover fallback: a stable colour and monogram per title --- */
.lib-cover-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, var(--c1), var(--c2));
  color: rgba(255, 255, 255, 0.82);
}
.lib-cover-fallback svg { width: 30px; height: 30px; opacity: 0.85; }
.lib-cover-initials {
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
}

/* --- Bulk selection --- */
.lib-select-toggle.active { color: var(--color-primary); background: var(--color-primary-subtle); }
.lib-select-box {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
  z-index: 4;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--border-color);
  border-radius: 6px;
  background: rgba(10, 14, 22, 0.85);
  color: #fff;
  cursor: pointer;
}
.lib-select-box.on { background: var(--color-primary); border-color: var(--color-primary); }
.card.lib-selected { outline: 2px solid var(--color-primary); outline-offset: -2px; }

.lib-bulk-bar {
  position: sticky;
  bottom: 1rem;
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: 1.25rem;
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
}
.lib-bulk-count { font-size: 0.78rem; font-weight: 700; color: var(--text-primary); margin-right: 0.25rem; }
.lib-bulk-sep { width: 1px; height: 20px; background: var(--border-color); }

/* --- Kind toggle (programs vs practice sets) --- */
.lib-kind-toggle { display: inline-flex; gap: 0.25rem; }

/* --- Level ladder --- */
.lib-ladder { display: flex; flex-direction: column; gap: 0.5rem; }
.lib-ladder-rung {
  display: flex;
  align-items: flex-start;
  gap: 0.85rem;
  padding: 0.65rem 0.85rem;
  border: 1px solid var(--border-color);
  border-left: 3px solid var(--border-color);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
}
.lib-ladder-rung.done { border-left-color: var(--color-success); opacity: 0.75; }
.lib-ladder-rung.current {
  border-left-color: var(--color-primary);
  box-shadow: 0 0 0 1px var(--color-primary-glow), 0 6px 20px rgba(0, 0, 0, 0.25);
}
.lib-ladder-lv {
  flex-shrink: 0;
  width: 74px;
  display: flex;
  align-items: center;
  gap: 0.3rem;
  font-family: var(--font-mono);
  font-size: 0.76rem;
  font-weight: 800;
  color: var(--text-secondary);
  padding-top: 0.2rem;
}
.lib-ladder-rung.done .lib-ladder-lv { color: var(--color-success); }
.lib-ladder-rung.current .lib-ladder-lv { color: var(--color-primary); }
.lib-ladder-items { display: flex; flex-wrap: wrap; gap: 0.4rem; flex: 1; min-width: 0; }
.lib-ladder-item {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  max-width: 100%;
  padding: 0.28rem 0.6rem;
  border: 1px solid var(--border-color);
  border-radius: 999px;
  background: var(--bg-surface-hover);
  color: var(--text-secondary);
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  transition: border-color var(--transition-fast), color var(--transition-fast);
}
.lib-ladder-item span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lib-ladder-item:hover { color: var(--text-primary); border-color: var(--text-tertiary); }
.lib-ladder-item.done { border-color: rgba(16, 185, 129, 0.45); color: #34d399; }
.lib-ladder-item.attempted { border-color: rgba(245, 158, 11, 0.45); color: #fbbf24; }
.lib-ladder-note {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.85rem;
  font-size: 0.76rem;
  color: var(--text-tertiary);
}

/* --- Notebook: per-section progress and the drill callout --- */
.nb-section-row { padding: 1rem; display: flex; justify-content: space-between; align-items: center; gap: 1rem; }
.nb-section-stat { display: flex; flex-direction: column; align-items: flex-end; gap: 0.2rem; min-width: 120px; }
.nb-section-bar { width: 110px; height: 4px; border-radius: 4px; background: var(--border-color); overflow: hidden; }
.nb-section-fill { height: 100%; border-radius: 4px; }
.nb-section-fill.score-perfect { background: var(--color-success); }
.nb-section-fill.score-partial { background: var(--color-warning); }
.nb-section-fill.score-low { background: var(--color-danger); }
.nb-section-sub { font-size: 0.66rem; color: var(--text-tertiary); }
.nb-section-untried { font-size: 0.72rem; color: var(--text-tertiary); font-style: italic; }

.nb-drill-callout {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
  padding: 0.9rem 1.1rem;
  border: 1px solid rgba(245, 158, 11, 0.4);
  border-radius: var(--radius-md);
  background: rgba(245, 158, 11, 0.08);
}
.nb-drill-callout strong { display: block; color: var(--text-primary); font-size: 0.9rem; }
.nb-drill-callout span { font-size: 0.8rem; color: var(--text-secondary); }

/* --- Multi-notebook session bar in the results overlay --- */
.np-queue-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-top: 1rem;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--color-primary);
  border-radius: var(--radius-md);
  background: var(--color-primary-subtle);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-primary);
}
.np-queue-bar > span { display: inline-flex; align-items: center; gap: 0.4rem; }
.np-queue-bar > div { display: flex; gap: 0.5rem; }

/* --- Program detail: snippets pointing back at this program --- */
.prog-snippet-list { display: flex; flex-direction: column; gap: 0.4rem; }
.prog-snippet-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: 0.6rem 0.8rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--text-secondary);
  cursor: pointer;
  text-align: left;
  transition: border-color var(--transition-fast), color var(--transition-fast);
}
.prog-snippet-row:hover { border-color: var(--color-primary); color: var(--text-primary); }
.prog-snippet-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }

/* --- Library hub: due panel and cross-library search --- */
.lib-hub-search {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  max-width: 520px;
  margin: 1.25rem auto 0;
  padding: 0 0.85rem;
  border: 1px solid var(--border-color);
  border-radius: 999px;
  background: var(--bg-surface);
}
.lib-hub-search svg { width: 16px; height: 16px; color: var(--text-tertiary); flex-shrink: 0; }
.lib-hub-search .search-input { border: none; background: transparent; padding: 0.6rem 0; flex: 1; }

/* THE RING BELONGS TO THE PILL, NOT TO THE FIELD INSIDE IT. .search-input
   carries its own focus ring and keeps `border-radius: var(--radius-full)` even
   here, where the rule above strips its border and background -- so focusing
   drew a rounded rectangle around the TEXT box, which sits inset from the pill
   by the icon and the padding. The ring cut across the pill instead of tracing
   it. It moves to the wrapper, which is the shape you can actually see. */
.lib-hub-search:focus-within {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px var(--color-primary-glow);
}
.lib-hub-search .search-input:focus { box-shadow: none; border-color: transparent; }
.lib-hub-results { display: none; max-width: 520px; margin: 0.5rem auto 0; text-align: left; }
.lib-hub-results.open {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  max-height: 320px;
  overflow-y: auto;
  padding: 0.4rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
}
.lib-hub-result {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.45rem 0.6rem;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  text-align: left;
}
.lib-hub-result:hover { background: var(--bg-surface-hover); color: var(--text-primary); }
.lib-hub-result-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.85rem; }
.lib-hub-result-kind {
  flex-shrink: 0;
  font-size: 0.62rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-tertiary);
}
.lib-hub-noresult { padding: 0.75rem; font-size: 0.82rem; color: var(--text-tertiary); text-align: center; }

.lib-due-panel {
  padding: 1rem 1.15rem;
  border: 1px solid rgba(99, 102, 241, 0.35);
  border-radius: var(--radius-lg);
  background: linear-gradient(180deg, rgba(99, 102, 241, 0.09), transparent);
}
.lib-due-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 0.85rem;
}
.lib-due-head strong { color: var(--text-primary); font-size: 1rem; }
.lib-due-head span { color: var(--text-tertiary); font-size: 0.82rem; margin-left: 0.35rem; }
.lib-due-list { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.lib-due-row {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  max-width: 100%;
  padding: 0.35rem 0.7rem;
  border: 1px solid var(--border-color);
  border-radius: 999px;
  background: var(--bg-surface);
  color: var(--text-secondary);
  font-size: 0.8rem;
  cursor: pointer;
  transition: border-color var(--transition-fast), color var(--transition-fast);
}
.lib-due-row:hover { border-color: var(--color-primary); color: var(--text-primary); }
.lib-due-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px; }
.lib-due-when { font-size: 0.68rem; color: var(--text-tertiary); font-weight: 700; }

/* Three bands of lateness. Everything overdue used to be one amber, so twenty
   days looked the same as one and the row order was the only clue. */
.lib-due-when.over { color: var(--color-warning); }
.lib-due-when.over-mid { color: #fb923c; }
.lib-due-when.over-high { color: var(--color-danger); }

/* The worst ones carry it on the pill too, so the eye finds them without
   reading a single label. Kept to the top band only -- tinting all of them
   just makes a wall of orange and loses the distinction again. */
.lib-due-row:has(.over-high) {
  border-color: color-mix(in srgb, var(--color-danger) 45%, var(--border-color));
  background: color-mix(in srgb, var(--color-danger) 7%, var(--bg-surface));
}

/* A count, not a row. It sat in the same flex line as twelve pills at a similar
   size and read as a thirteenth you could press. */
.lib-due-more {
  align-self: center;
  font-size: 0.72rem;
  color: var(--text-tertiary);
  padding-left: 0.35rem;
  font-style: italic;
}
.lib-stat-chip.due { border-color: rgba(99, 102, 241, 0.5); color: #a5b4fc; }

/* Wings are real libraries now, so they stop looking disabled. They lost their
   icon styling when .lib-card-soon went away — each gets its own accent instead,
   so the row reads as eight collections rather than eight greyed-out stubs. */
/* The accent now arrives inline from the wing's own definition. It used to
   come from :nth-child on the "other wings" grid, which made the colour a
   property of the ROW — so moving the wings into Your collections would have
   quietly turned all seven purple. The fallback below is for anything that
   renders a wing card without passing one. */
.lib-card-wing { cursor: pointer; --lib-accent: #8b5cf6; }
.lib-card-wing .lib-card-icon { background: color-mix(in srgb, var(--lib-accent) 16%, transparent); color: var(--lib-accent); }
.lib-card-wing .lib-card-desc { min-height: 2.4em; }

/* The one card left under "Other wings": a marker for what comes next, so it
   should read as an empty slot rather than as something you failed to open.
   Dashed, dimmed, and it does not lift on hover the way a real card does. */
.lib-card-placeholder {
  --lib-accent: #64748b;
  border-style: dashed;
  cursor: default;
  opacity: 0.72;
}
.lib-card-placeholder .lib-card-icon {
  background: color-mix(in srgb, var(--lib-accent) 14%, transparent);
  color: var(--lib-accent);
}
.lib-card-placeholder .lib-card-desc { min-height: 2.4em; }
.lib-card-placeholder:hover { transform: none; opacity: 0.85; }
.lib-card-placeholder .lib-card-arrow { color: var(--lib-accent); opacity: 0.8; }
/* Browsers without color-mix still get a readable tile. */
@supports not (background: color-mix(in srgb, red 16%, transparent)) {
  .lib-card-wing .lib-card-icon { background: var(--bg-surface-hover); }
}

/* --- Generic wing library --- */
.wing-body {
  font-size: 0.95rem;
  line-height: 1.75;
  color: var(--text-secondary);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.tree-node-count {
  margin-left: auto;
  font-size: 0.68rem;
  font-weight: 700;
  color: var(--text-tertiary);
  padding-left: 0.4rem;
}

/* ============================================================
   CODING ADMIN — list pane
   ------------------------------------------------------------
   The list used to show two programs and hide the rest behind a
   "Show N more…" disclosure, with folder navigation living in a
   dropdown. It now shows everything, grouped by folder, and the
   secondary panels collapse so Programs owns the pane.
   ============================================================ */
/* Programs is now the same <details> panel as Practice Sets, Categories and
   Skill Tree Locks — it was the only section in the pane that could not be
   collapsed. It stays open by default because it is what you come here for,
   and it reads a size larger than the panels below it. */
.admin-panel-programs > summary { font-size: 0.95rem; }
.admin-panel-programs > summary .admin-panel-name { font-weight: 700; }
/* These were underlined text links floating in the panel header — they read as
   stray words rather than controls, and "Select all" sat on a line of its own
   below the header. Both are now the same small pill button with an icon,
   grouped at the right end of the header. */
.admin-panel-tools {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  flex-shrink: 0;
}
.admin-tool-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.22rem 0.5rem;
  font-family: inherit;
  font-size: 0.7rem;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  color: var(--text-tertiary);
  background: var(--bg-surface-hover);
  border: 1px solid var(--border-color);
  border-radius: 999px;
  cursor: pointer;
  transition: color 0.14s ease, border-color 0.14s ease, background 0.14s ease;
}
.admin-tool-btn:hover {
  color: var(--text-primary);
  border-color: var(--color-primary);
  background: var(--color-primary-subtle);
}
.admin-tool-btn:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}
.admin-tool-btn.is-on {
  color: var(--color-primary);
  border-color: var(--color-primary);
  background: var(--color-primary-subtle);
}
.admin-tool-ic { width: 12px; height: 12px; flex-shrink: 0; }

/* Narrow panes: keep the icons, drop the words rather than wrapping. */
@media (max-width: 520px) {
  .admin-tool-btn span { display: none; }
  .admin-tool-btn { padding: 0.3rem; }
}

/* Still used by the tree panes' own collapse links. */
.admin-groups-btn {
  margin-left: auto;
  border: none;
  background: none;
  color: var(--text-tertiary);
  font-size: 0.72rem;
  font-weight: 600;
  cursor: pointer;
  padding: 0.2rem;
  white-space: nowrap;
}
.admin-groups-btn:hover { color: var(--text-primary); }

.admin-group { border-bottom: 1px solid var(--border-color); }
.admin-group:last-child { border-bottom: none; }
.admin-group > summary {
  /* Not sticky. Pinned to the top of a 400px pane it covered the row above it
     and left the list looking like it had lost a line. With three groups on
     screen there was nothing to keep oriented about anyway. */
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.45rem 0.3rem;
  background: var(--bg-surface);
  color: var(--text-secondary);
  font-size: 0.76rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.admin-group > summary::-webkit-details-marker { display: none; }
.admin-group > summary:hover { color: var(--text-primary); }
.admin-group-chev {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  transition: transform 0.18s ease;
}
.admin-group[open] > summary .admin-group-chev { transform: rotate(90deg); }
.admin-group-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.admin-group-count {
  flex-shrink: 0;
  min-width: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--bg-surface-hover);
  color: var(--text-tertiary);
  font-size: 0.65rem;
  font-weight: 800;
  text-align: center;
}
.admin-group-body {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.15rem 0 0.5rem;
}

/* --- Collapsed secondary panels (Practice Sets / Categories / Locks) --- */
.admin-panel {
  margin-bottom: 0.6rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  overflow: hidden;
}
.admin-panel > summary {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.7rem 0.85rem;
  cursor: pointer;
  list-style: none;
  font-weight: 700;
  font-size: 0.9rem;
  color: var(--text-primary);
  user-select: none;
}
.admin-panel > summary::-webkit-details-marker { display: none; }
.admin-panel > summary:hover { background: var(--bg-surface-hover); }
.admin-panel-chev {
  width: 14px; height: 14px; color: var(--text-tertiary); flex-shrink: 0;
  transition: transform 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}
.admin-panel:not(.collapsed) > summary .admin-panel-chev { transform: rotate(90deg); }
.admin-panel-name { flex: 1; min-width: 0; }
.admin-panel-count {
  flex-shrink: 0;
  min-width: 20px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--bg-surface-hover);
  color: var(--text-tertiary);
  font-size: 0.68rem;
  font-weight: 800;
  text-align: center;
}
/* The body sits in its own single-track grid, exactly like the tree's children.
   Putting the grid on the <details> itself with `auto 0fr` did not work: with a
   fixed `auto` track alongside it, the flexible track kept resolving to its
   content height and the panel never closed — forcing `auto 0fr` inline still
   measured 154px. One track on a plain wrapper collapses properly. */
.admin-panel-slot {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}
.admin-panel.collapsed > .admin-panel-slot { grid-template-rows: 0fr; }

.admin-panel-body {
  padding: 0.85rem 0.85rem 0.95rem;
  border-top: 1px solid var(--border-color);
  overflow: hidden;
  min-height: 0;
  transition: padding 0.45s cubic-bezier(0.4, 0, 0.2, 1),
              border-top-width 0.45s cubic-bezier(0.4, 0, 0.2, 1),
              opacity 0.35s ease 0.05s;
}
.admin-panel.collapsed .admin-panel-body {
  padding-top: 0;
  padding-bottom: 0;
  border-top-width: 0;
  opacity: 0;
  transition: padding 0.45s cubic-bezier(0.4, 0, 0.2, 1),
              border-top-width 0.45s cubic-bezier(0.4, 0, 0.2, 1),
              opacity 0.25s ease;
}

@media (prefers-reduced-motion: reduce) {
  .admin-panel-slot, .admin-panel-body, .admin-panel-chev { transition: none !important; }
}

/* ============================================================
   PROGRAM DETAIL — structured stats, tags, paged attempts
   ------------------------------------------------------------
   Versions / attempts / best / last used to be badges in the same
   wrap as the tags, so a fact about your progress looked exactly
   like the word "Math" and a few tags turned the header into a
   wall. Facts are a labelled strip now; tags are their own row.
   ============================================================ */
.prog-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.4rem;
  margin-top: 0.65rem;
}
.prog-stat {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--text-tertiary);
  min-width: 0;
}
.prog-stat-body { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.prog-stat em {
  font-style: normal;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}
.prog-stat strong {
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.prog-stat.score-perfect strong { color: var(--color-success); }
.prog-stat.score-partial strong { color: var(--color-warning); }
.prog-stat.score-low strong { color: var(--color-danger); }
.prog-stat.score-perfect, .prog-stat.score-partial, .prog-stat.score-low { color: inherit; }
.prog-stat-live strong { color: var(--color-primary); }
.prog-stat-live { color: var(--color-primary); }

.prog-tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  margin-top: 0.7rem;
  padding-top: 0.7rem;
  border-top: 1px solid var(--border-color);
}
.prog-tags > svg { color: var(--text-tertiary); flex-shrink: 0; }
.prog-tag-more {
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--color-primary);
  background: none;
  border: none;
  cursor: pointer;
  padding: 0 0.2rem;
}
.prog-tag-more:hover { text-decoration: underline; }

.prog-attempt-no {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: var(--text-tertiary);
  flex-shrink: 0;
  min-width: 26px;
}
.prog-attempt-pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: 0.6rem;
}
.prog-page-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.3rem 0.7rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-color);
  background: var(--bg-surface);
  color: var(--text-secondary);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.prog-page-btn:hover:not(:disabled) { border-color: var(--color-primary); color: var(--color-primary); }
.prog-page-btn:disabled { opacity: 0.35; cursor: default; }
.prog-page-info {
  font-size: 0.72rem;
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}

/* --- Card: stats on one row, tags on the next, both capped --- */
.card-stat-row,
.card-tag-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem;
}
.card-stat-row { margin-bottom: 0.4rem; }
.card-tag-row { margin-bottom: 0.75rem; }
.card-stat {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  font-size: 0.7rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text-tertiary);
  padding: 0.1rem 0.4rem;
  border-radius: var(--radius-sm);
  background: var(--bg-surface-hover);
}
.card-stat.score-perfect { color: var(--color-success); }
.card-stat.score-partial { color: var(--color-warning); }
.card-stat.score-low { color: var(--color-danger); }
.card-tag-more {
  font-size: 0.68rem;
  font-weight: 700;
  color: var(--text-tertiary);
  padding: 0.1rem 0.35rem;
  border-radius: var(--radius-sm);
  border: 1px dashed var(--border-color);
  cursor: default;
}

/* --- Filters: typed level, folded chip rows --- */
.lib-level-input {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  border: 1px solid var(--border-color);
  border-radius: 999px;
  padding: 0.1rem 0.5rem 0.1rem 0.55rem;
  background: transparent;
}
.lib-level-input:focus-within { border-color: var(--color-primary); }
.lib-level-prefix {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  color: var(--text-tertiary);
}
.lib-level-input input {
  width: 44px;
  border: none;
  background: transparent;
  color: var(--color-primary);
  font-family: var(--font-mono);
  font-size: 0.74rem;
  font-weight: 700;
  padding: 0.12rem 0;
  outline: none;
  -moz-appearance: textfield;
}
.lib-level-input input::-webkit-outer-spin-button,
.lib-level-input input::-webkit-inner-spin-button { opacity: 0.35; height: 18px; }
.lib-level-range {
  font-size: 0.66rem;
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.lib-chip-row-wrap { flex-wrap: wrap; }
.lib-chip-more {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--color-primary);
  background: transparent;
  border: 1px dashed var(--color-primary);
  border-radius: 999px;
  padding: 0.18rem 0.6rem;
  cursor: pointer;
  white-space: nowrap;
}
.lib-chip-more:hover { background: var(--color-primary-subtle); }

/* ============================================================
   TREE DRAG & DROP (tree-dnd.js)
   ------------------------------------------------------------
   Three distinct answers, because the old bar gave one: an
   insertion line for "beside", an outline for "into", and a
   struck-through row for "you cannot drop that here". Before,
   every target looked droppable and most of them silently were
   not.
   ============================================================ */
.tree-node-row.dragging { opacity: 0.4; }

/* Reorder: a line where the row will land */
.tree-node-row.drag-over-top,
.tree-node-row.drag-over-bottom { position: relative; }
.tree-node-row.drag-over-top::after,
.tree-node-row.drag-over-bottom::after {
  content: '';
  position: absolute;
  left: 0.35rem;
  right: 0.35rem;
  height: 2px;
  background: var(--color-primary);
  border-radius: 2px;
  box-shadow: 0 0 6px var(--color-primary-glow);
  pointer-events: none;
}
.tree-node-row.drag-over-top::after { top: -1px; }
.tree-node-row.drag-over-bottom::after { bottom: -1px; }

/* Into: the folder itself is the target, so outline the whole row */
.tree-node-row.drag-into {
  outline: 2px solid var(--color-primary);
  outline-offset: -2px;
  border-radius: var(--radius-sm);
  background: var(--color-primary-subtle);
}

/* Refused — the cursor already says "no", this says why it looks wrong */
.tree-node-row.drag-deny {
  outline: 2px dashed var(--color-danger);
  outline-offset: -2px;
  border-radius: var(--radius-sm);
  opacity: 0.6;
}
.tree-node-row.drag-deny .tree-node-label { text-decoration: line-through; }

/* The top level, as a real target rather than dead space */
.tree-root-drop {
  display: none;
  align-items: center;
  gap: 0.4rem;
  margin: 0.4rem 0.5rem 0.75rem;
  padding: 0.55rem 0.7rem;
  border: 1px dashed var(--border-color);
  border-radius: var(--radius-md);
  font-size: 0.72rem;
  color: var(--text-tertiary);
}
body.tree-dragging .tree-root-drop { display: flex; }
.tree-root-drop.drag-into {
  border-color: var(--color-primary);
  border-style: solid;
  color: var(--color-primary);
  background: var(--color-primary-subtle);
}

/* A 24px target without a 24px glyph — the chevron sits inside a row whose
   click does something else, so it was easy to miss at 18px. */
.tree-node-chevron {
  padding: 4px;
  margin: -4px;
  box-sizing: content-box;
}

/* --- Right-click menu ---
   Sized for reading rather than for saving pixels. At 186px wide with 0.78rem
   labels and 14px icons the rows were a 26px target -- under the ~32px a
   pointer hits reliably, and well under a fingertip.

   The height is the part that was actually broken. The old code clamped top to
   innerHeight - height - 8, so a menu taller than the viewport got a NEGATIVE
   top and its first rows sat above the screen with no way to reach them
   (measured: top -177px, six rows unreachable). The list scrolls inside a
   max-height now, and the menu flips above the cursor when there is more room
   up there.

   No scrollbar. On a 240px popup a track down the edge costs more than it
   tells you, and it fights the rounded corners. The fades at top and bottom
   say the same thing, and unlike a scrollbar they show up only in the
   direction there is actually more to see. */
.tree-ctx {
  position: fixed;
  z-index: 10050;
  min-width: 232px;
  max-width: 320px;
  padding: 0.35rem;
  display: flex;
  flex-direction: column;
  overflow: hidden;                  /* keeps the fades inside the corners */
  background: var(--bg-elevated);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.55);
  animation: treeCtxIn 0.12s ease-out;
}
@keyframes treeCtxIn { from { opacity: 0; transform: scale(0.97); } to { opacity: 1; transform: scale(1); } }

.tree-ctx-scroll {
  overflow-y: auto;
  min-height: 0;                     /* so it can shrink under the max-height */
  overscroll-behavior: contain;      /* the page behind must not scroll with it */
  scrollbar-width: none;             /* Firefox */
  -ms-overflow-style: none;          /* legacy Edge */
}
.tree-ctx-scroll::-webkit-scrollbar { width: 0; height: 0; display: none; }

/* Each fade is shown only while there is more in that direction. */
.tree-ctx::before,
.tree-ctx::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 28px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease;
  z-index: 1;
}
.tree-ctx::before { top: 0;    background: linear-gradient(to bottom, var(--bg-elevated) 30%, transparent); }
.tree-ctx::after  { bottom: 0; background: linear-gradient(to top,    var(--bg-elevated) 30%, transparent); }
.tree-ctx.can-up::before,
.tree-ctx.can-down::after { opacity: 1; }

.tree-ctx-item {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: 0.5rem 0.7rem;
  border: none;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-secondary);
  font-size: 0.85rem;
  line-height: 1.35;
  text-align: left;
  cursor: pointer;
}
.tree-ctx-item i,
.tree-ctx-item svg { width: 16px; height: 16px; flex-shrink: 0; }
/* A label long enough to blow the menu open gets clipped, not wrapped; the
   full text is on the button title. */
.tree-ctx-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tree-ctx-item:hover { background: var(--bg-surface-hover); color: var(--text-primary); }
.tree-ctx-item:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }
.tree-ctx-item.danger { color: var(--color-danger); }
.tree-ctx-item.danger:hover { background: rgba(239, 68, 68, 0.12); }
.tree-ctx-sep { height: 1px; margin: 0.3rem 0.3rem; background: var(--border-color); }

/* --- Move to… --- */
.tree-move-list {
  max-height: 320px;
  overflow-y: auto;
  margin: 0.5rem 0;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
}
.tree-move-opt {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  width: 100%;
  padding: 0.4rem 0.6rem;
  border: none;
  background: none;
  color: var(--text-secondary);
  font-size: 0.8rem;
  text-align: left;
  cursor: pointer;
}
.tree-move-opt:hover { background: var(--bg-surface-hover); color: var(--text-primary); }
.tree-move-opt:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }

/* Nothing on the page should try to interpret a drag as a text selection */
body.tree-dragging { user-select: none; }
body.tree-dragging .tree-node-row { cursor: grabbing; }

/* Undo affordance on a toast */
.mx-toast-action {
  flex-shrink: 0;
  align-self: center;
  padding: 0.25rem 0.6rem;
  margin-right: 0.2rem;
  border: 1px solid var(--color-primary);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--color-primary);
  font-size: 0.74rem;
  font-weight: 700;
  cursor: pointer;
}
.mx-toast-action:hover { background: var(--color-primary); color: #fff; }


/* A drag must not make the pane's scrollbar appear. The drop strip and the
   hover markers add a few pixels of height, which was enough to trip the
   scrollbar into view mid-gesture and shift every row sideways under the
   pointer. The pane still scrolls (auto-scroll at the edges relies on it) -
   only the chrome is hidden. */
body.tree-dragging [data-tree-ns] {
  scrollbar-width: none;
  -ms-overflow-style: none;
  overflow-x: hidden;
}
body.tree-dragging [data-tree-ns]::-webkit-scrollbar { width: 0; height: 0; }

/* ============================================================
   ADMIN CARD BROWSER
   ------------------------------------------------------------
   The admin's second pane can now show a collection as cards, the way the
   libraries do, instead of only ever holding the edit form behind a "select
   something on the left" placeholder.
   ============================================================ */
.admin-card-browser {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1.25rem;
  height: 100%;
  overflow-y: auto;
}
.admin-card-browser.hidden { display: none; }

/* Back and the breadcrumbs stay reachable however far down the grid you are.
   .admin-card-browser is the scroller, so `top: 0` pins this to it; the
   negative margins let the background span the browser's 1.25rem padding. */
.admin-cards-head {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin: -1.25rem -1.25rem 0;
  padding: 1rem 1.25rem 1.1rem;
  /* No rule under it — a hard line across the pane just added clutter. The
     header goes opaque at the top and fades out at its lower edge, the same
     way the form footer fades into the content above it, so what scrolls
     underneath dissolves rather than being cut off. */
  background: linear-gradient(to top, transparent, var(--bg-surface) 30%, var(--bg-surface) 100%);
}
.admin-cards-back { flex-shrink: 0; padding: 0.3rem 0.6rem; font-size: 0.75rem; }
.admin-cards-titles { flex: 1; min-width: 140px; }
.admin-cards-titles h2 {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text-primary);
}
.admin-cards-titles p { margin: 0.1rem 0 0; font-size: 0.75rem; color: var(--text-tertiary); }
.admin-cards-search { max-width: 220px; flex: 1 1 160px; }

.admin-cards-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 0.75rem;
  align-items: stretch;
}

.admin-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.3rem;
  padding: 0.85rem;
  min-height: 128px;
  text-align: left;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
  cursor: pointer;
  transition: border-color var(--transition-fast), transform var(--transition-fast),
              box-shadow var(--transition-fast);
}
.admin-card:hover {
  border-color: var(--color-primary);
  transform: translateY(-2px);
  box-shadow: var(--shadow-md, 0 8px 20px rgba(0,0,0,0.25));
}
.admin-card:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }

.admin-card-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-md);
  margin-bottom: 0.2rem;
}
.admin-card-chip svg { width: 17px; height: 17px; }

.admin-card-title {
  font-weight: 700;
  font-size: 0.85rem;
  color: var(--text-primary);
  line-height: 1.3;
  /* Two lines, then an ellipsis: a long title must not stretch one card
     taller than the rest of the row. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}
.admin-card-sub {
  font-size: 0.72rem;
  color: var(--text-tertiary);
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}
.admin-card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: auto;
  padding-top: 0.4rem;
  font-size: 0.68rem;
  color: var(--text-tertiary);
}
.admin-card-meta span { display: inline-flex; align-items: center; gap: 0.2rem; }
.admin-card-meta svg { width: 11px; height: 11px; }

/* The dashed card is the one that makes a new thing. */
.admin-card-add {
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  border: 2px dashed var(--border-color);
  background: transparent;
  color: var(--text-tertiary);
  font-weight: 600;
  font-size: 0.78rem;
  text-align: center;
}
.admin-card-add:hover {
  border-color: var(--color-primary);
  color: var(--color-primary);
  background: var(--color-primary-subtle);
}
.admin-card-add svg { width: 22px; height: 22px; }

.admin-cards-none { font-size: 0.78rem; color: var(--text-tertiary); margin: 0; }

/* The section whose cards are on screen, marked in pane 1. */
.admin-panel.is-showing > summary {
  background: var(--color-primary-subtle);
  box-shadow: inset 3px 0 0 var(--color-primary);
}

/* Back sits beside the editor's close button. */
.af-back-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.3rem 0.55rem;
  flex-shrink: 0;
}

@media (max-width: 640px) {
  .admin-cards-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
  .admin-cards-search { max-width: none; }
}

/* ============================================================
   GENERAL — the admin's navigable folder browser
   ============================================================ */

/* Breadcrumbs sit on their own row with Back at the far left, the way every
   library header is laid out. */
.admin-nav-bar { gap: 0.6rem; }
.admin-nav-crumbs {
  display: flex;
  align-items: center;
  gap: 0.2rem;
  flex-wrap: wrap;
  min-width: 0;
}
.admin-crumb {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.2rem 0.45rem;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-tertiary);
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  max-width: 190px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.admin-crumb:hover { background: var(--bg-surface-hover); color: var(--text-primary); }
.admin-crumb.current { color: var(--text-primary); cursor: default; }
.admin-crumb svg { width: 14px; height: 14px; }
.admin-crumb-sep { color: var(--text-tertiary); font-size: 0.75rem; opacity: 0.6; }

.admin-nav-title {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.admin-nav-title h2 { margin: 0; font-size: 1.4rem; font-weight: 800; color: var(--text-primary); }
.admin-nav-title p { margin: 0.15rem 0 0; font-size: 0.78rem; color: var(--text-tertiary); }
.admin-nav-tools { display: flex; gap: 0.4rem; flex-wrap: wrap; }

.admin-nav-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 1rem;
  align-items: stretch;
}
/* min-height:100% resolves against an auto-height grid row, i.e. to nothing —
   it overrode the base min-height and left the dashed card 74px tall beside
   285px cards. A real length holds it up when it lands on a row of its own;
   sharing a row with real cards still stretches it. */
.admin-nav-grid .admin-card-add { min-height: 220px; }

.admin-nav-card {
  display: flex;
  flex-direction: column;
  position: relative;
  padding: 0;
  overflow: hidden;
}
.admin-nav-card .nb-card-cover { margin: 0 0 0.75rem; }
.admin-nav-card > *:not(.nb-card-cover):not(.admin-nav-number):not(.admin-nav-lock) {
  margin-left: 0.9rem;
  margin-right: 0.9rem;
}
.admin-nav-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.35rem;
}
.admin-nav-head h3 {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  color: var(--text-primary);
  line-height: 1.3;
  word-break: break-word;
}
.admin-nav-sub {
  margin: 0 0 0.5rem;
  font-size: 0.78rem;
  color: var(--text-secondary);
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.admin-nav-card .card-stat-row,
.admin-nav-card .card-tag-row { margin-bottom: 0.5rem; }

.admin-nav-actions {
  display: flex;
  gap: 0.35rem;
  margin-top: auto;
  padding-bottom: 0.9rem;
}
.admin-nav-actions .btn { padding: 0.35rem 0.6rem; font-size: 0.75rem; }
.admin-nav-actions .btn:first-child { flex: 1; }
.admin-nav-actions svg { width: 14px; height: 14px; }
.admin-nav-danger:hover { color: var(--color-danger); }

/* ── Arranging by hand ──────────────────────────────────────
   The number only exists while an order is being set. Outside that the grid
   is already in order, so stamping every card with one was noise. */
.admin-nav-number {
  position: absolute;
  top: 0.6rem;
  left: 0.6rem;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: linear-gradient(145deg, var(--color-primary), var(--color-accent));
  color: #fff;
  font-size: 0.85rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.28);
  animation: adminNumberPop 0.28s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Not yet placed: an empty socket showing where a number will land. */
.admin-nav-number.unplaced {
  background: rgba(15, 23, 42, 0.55);
  border: 2px dashed var(--border-color);
  box-shadow: none;
  animation: none;
}
@keyframes adminNumberPop {
  from { transform: scale(0.4); opacity: 0; }
  to   { transform: scale(1); opacity: 1; }
}

.admin-nav-ordering { cursor: pointer; }
.admin-nav-ordering:hover { border-color: var(--color-primary); }
.admin-nav-ordering.placed {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 1px var(--color-primary) inset;
}
/* A placed card steps back so the ones still to pick stand out. */
.admin-nav-ordering.placed .nb-card-cover { opacity: 0.45; }

.admin-order-bar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--color-primary);
  border-radius: var(--radius-md);
  background: var(--color-primary-subtle);
  color: var(--text-primary);
  font-size: 0.8rem;
}
.admin-order-bar > svg { width: 16px; height: 16px; color: var(--color-primary); flex-shrink: 0; }
.admin-order-msg { flex: 1; min-width: 0; }
.admin-order-msg strong { display: block; font-weight: 700; }
.admin-order-bar .btn { flex-shrink: 0; }

/* The Order menu reuses the View dropdown's shell. */
.admin-order-opt {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
  width: 100%;
  padding: 0.45rem 0.5rem;
  border: none;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-secondary);
  font-size: 0.78rem;
  text-align: left;
  cursor: pointer;
}
.admin-order-opt:hover { background: var(--bg-surface-hover); color: var(--text-primary); }
.admin-order-opt svg { width: 15px; height: 15px; flex-shrink: 0; margin-top: 1px; }
.admin-order-opt strong { display: block; color: var(--text-primary); font-weight: 700; }
.admin-order-opt em {
  display: block; font-style: normal; font-size: 0.7rem;
  color: var(--text-tertiary); margin-top: 1px; line-height: 1.35;
}
.admin-order-menu > summary { font-size: 0.75rem; }

.admin-nav-lock {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: rgba(15, 23, 42, 0.75);
  color: var(--color-warning);
  backdrop-filter: blur(4px);
}
.admin-nav-lock svg { width: 13px; height: 13px; }

/* The category tree in the General panel in pane 1.
   Built from plain rows rather than <details>, because a <details> hides its
   content outright and cannot be transitioned — this animates open with the
   same 0fr -> 1fr grid row, timing and left-hand guide as the library's tree,
   so the two read as the same control. */
.admin-gen-list { display: flex; flex-direction: column; }

.admin-gen-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.45rem 0.3rem;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color var(--transition-fast), color var(--transition-fast);
}
.admin-gen-row:hover { background: var(--bg-surface-hover); color: var(--text-primary); }
.admin-gen-row.current {
  background: var(--color-primary-subtle);
  color: var(--color-primary);
  font-weight: 700;
}
.admin-gen-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.admin-gen-icon { width: 14px; height: 14px; flex-shrink: 0; }

/* The chevron is its own button: it expands, while the rest of the row opens
   the folder in the second pane. */
.admin-gen-chev {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
}
.admin-gen-chev:hover { background: var(--bg-surface-hover); color: var(--text-primary); }
.admin-gen-chev svg { width: 13px; height: 13px; transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); }
.admin-gen-chev.open svg { transform: rotate(90deg); }
.admin-gen-chev:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 1px; }
/* A leaf keeps the same indent as a branch, so the names line up. */
.admin-gen-chev-spacer { width: 18px; flex-shrink: 0; }

.admin-gen-children {
  display: grid;
  grid-template-rows: 1fr;
  /* Matching the library tree: smooth ease-out so the rows below are pushed
     down gradually rather than snapping. */
  transition: grid-template-rows 0.7s cubic-bezier(0.4, 0, 0.2, 1);
  overflow: hidden;
  border-left: 2px solid var(--border-color);
  margin-left: 0.85rem;
}
.admin-gen-children.collapsed { grid-template-rows: 0fr; }

.admin-gen-children-inner {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  opacity: 1;
  /* Space opens slightly before the content fades in. */
  transition: opacity 0.7s ease 0.08s;
}
.admin-gen-children.collapsed .admin-gen-children-inner {
  opacity: 0;
  transition: opacity 0.7s ease;
}

.admin-gen-empty {
  margin: 0;
  padding: 0.3rem 0.4rem 0.3rem 1.4rem;
  font-size: 0.72rem;
  color: var(--text-tertiary);
  font-style: italic;
}

@media (prefers-reduced-motion: reduce) {
  .admin-gen-children,
  .admin-gen-children-inner,
  .admin-gen-chev svg { transition: none !important; }
}

/* The section whose cards are on screen, marked in pane 1. */


/* Back sits beside the editor's close button. */
.af-back-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.3rem 0.55rem;
  flex-shrink: 0;
}

@media (max-width: 640px) {
  .admin-cards-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
  .admin-cards-search { max-width: none; }
}

/* ============================================================
   GENERAL — the admin's navigable folder browser
   ============================================================ */

/* Breadcrumbs sit on their own row with Back at the far left, the way every
   library header is laid out. */
.admin-nav-bar { gap: 0.6rem; }
.admin-nav-crumbs {
  display: flex;
  align-items: center;
  gap: 0.2rem;
  flex-wrap: wrap;
  min-width: 0;
}
.admin-crumb {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.2rem 0.45rem;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-tertiary);
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  max-width: 190px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.admin-crumb:hover { background: var(--bg-surface-hover); color: var(--text-primary); }
.admin-crumb.current { color: var(--text-primary); cursor: default; }
.admin-crumb svg { width: 14px; height: 14px; }
.admin-crumb-sep { color: var(--text-tertiary); font-size: 0.75rem; opacity: 0.6; }

.admin-nav-title {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.admin-nav-title h2 { margin: 0; font-size: 1.4rem; font-weight: 800; color: var(--text-primary); }
.admin-nav-title p { margin: 0.15rem 0 0; font-size: 0.78rem; color: var(--text-tertiary); }
.admin-nav-tools { display: flex; gap: 0.4rem; flex-wrap: wrap; }

.admin-nav-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 1rem;
  align-items: stretch;
}
/* min-height:100% resolves against an auto-height grid row, i.e. to nothing —
   it overrode the base min-height and left the dashed card 74px tall beside
   285px cards. A real length holds it up when it lands on a row of its own;
   sharing a row with real cards still stretches it. */
.admin-nav-grid .admin-card-add { min-height: 220px; }

.admin-nav-card {
  display: flex;
  flex-direction: column;
  position: relative;
  padding: 0;
  overflow: hidden;
}
.admin-nav-card .nb-card-cover { margin: 0 0 0.75rem; }
.admin-nav-card > *:not(.nb-card-cover):not(.admin-nav-number):not(.admin-nav-lock) {
  margin-left: 0.9rem;
  margin-right: 0.9rem;
}
.admin-nav-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.35rem;
}
.admin-nav-head h3 {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  color: var(--text-primary);
  line-height: 1.3;
  word-break: break-word;
}
.admin-nav-sub {
  margin: 0 0 0.5rem;
  font-size: 0.78rem;
  color: var(--text-secondary);
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.admin-nav-card .card-stat-row,
.admin-nav-card .card-tag-row { margin-bottom: 0.5rem; }

.admin-nav-actions {
  display: flex;
  gap: 0.35rem;
  margin-top: auto;
  padding-bottom: 0.9rem;
}
.admin-nav-actions .btn { padding: 0.35rem 0.6rem; font-size: 0.75rem; }
.admin-nav-actions .btn:first-child { flex: 1; }
.admin-nav-actions svg { width: 14px; height: 14px; }
.admin-nav-danger:hover { color: var(--color-danger); }

/* The position badge doubles as the control that changes it. */
.admin-nav-number {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
  z-index: 2;
  min-width: 30px;
  padding: 0.15rem 0.4rem;
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 999px;
  background: rgba(15, 23, 42, 0.75);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 800;
  cursor: pointer;
  backdrop-filter: blur(4px);
}
.admin-nav-number:hover { background: var(--color-primary); border-color: var(--color-primary); }

.admin-nav-lock {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: rgba(15, 23, 42, 0.75);
  color: var(--color-warning);
  backdrop-filter: blur(4px);
}
.admin-nav-lock svg { width: 13px; height: 13px; }

/* The tree inside the General panel in pane 1. It reuses .admin-group, so a
   branch reads exactly like the Programs panel's folder headers, but nests:
   a folder's body holds its subfolders and then its own items. */
.admin-gen-list { display: flex; flex-direction: column; }
.admin-gen-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Nested branches drop the divider, which at three levels deep read as a
   stack of unrelated sections rather than one tree. */
.admin-gen-group .admin-gen-group { border-bottom: none; }
.admin-gen-group > .admin-group-body { display: block; }
.admin-gen-group.current > summary {
  background: var(--color-primary-subtle);
  color: var(--color-primary);
  font-weight: 700;
}

.admin-gen-item {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  width: 100%;
  padding: 0.32rem 0.4rem;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-secondary);
  font-size: 0.78rem;
  text-align: left;
  cursor: pointer;
}
.admin-gen-item:hover { background: var(--bg-surface-hover); color: var(--text-primary); }
.admin-gen-item.current {
  background: var(--color-primary-subtle);
  color: var(--color-primary);
  font-weight: 700;
}
.admin-gen-item.current svg { opacity: 1; }
.admin-gen-item svg { width: 13px; height: 13px; flex-shrink: 0; opacity: 0.75; }

.admin-gen-empty {
  margin: 0;
  padding: 0.3rem 0.4rem 0.3rem 1.4rem;
  font-size: 0.72rem;
  color: var(--text-tertiary);
  font-style: italic;
}

@media (max-width: 640px) {
  .admin-nav-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
}


/* ============================================================
   PANE 2 — cards loading in
   ------------------------------------------------------------
   Every time the second pane changes what it is showing, the cards arrive in
   reading order rather than appearing all at once, so it reads as the pane
   filling up. The delay is set per card from JS and capped, because a folder
   with sixty items should not take three seconds to finish arriving.
   ============================================================ */
@keyframes adminCardIn {
  from { opacity: 0; transform: translate3d(-10px, 8px, 0) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

/* Gated on a class the renderer only adds when the pane starts showing
   something else. Without that, every redraw — placing one card while
   arranging, for instance — replayed the whole load-in as a flash. */
.admin-cards-animate .admin-cards-grid > *,
.admin-cards-animate .admin-nav-grid > * {
  animation: adminCardIn 0.34s cubic-bezier(0.22, 0.61, 0.36, 1) backwards;
  animation-delay: var(--card-in-delay, 0ms);
}

/* The header above them leads, so the pane does not start mid-list. */
.admin-cards-animate .admin-cards-head,
.admin-cards-animate .admin-nav-bar,
.admin-cards-animate .admin-nav-title {
  animation: adminCardIn 0.28s cubic-bezier(0.22, 0.61, 0.36, 1) backwards;
}

@media (prefers-reduced-motion: reduce) {
  .admin-cards-animate .admin-cards-grid > *,
  .admin-cards-animate .admin-nav-grid > *,
  .admin-cards-animate .admin-cards-head,
  .admin-cards-animate .admin-nav-bar,
  .admin-cards-animate .admin-nav-title { animation: none !important; }
}

/* ── The sequence number ───────────────────────────────────────
   A course is an order, and the order was invisible: it lived in an array, so
   nothing said which program came first and one click of the descending
   control could run the whole curriculum backwards with no sign of it.

   Small, ahead of the title, on every card. Its job is to make the sequence
   readable at a glance — which is also what makes a wrong one obvious. */
.prog-seq {
  position: absolute;
  top: 0.625rem;
  left: 0.625rem;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.55rem;
  height: 1.55rem;
  padding: 0 0.35rem;
  border-radius: 8px;
  /* It sits on the cover art, which is a different colour on every card, so it
     carries its own ground and a hairline rather than trusting the contrast. */
  background: rgba(13, 17, 23, 0.72);
  border: 1px solid rgba(255, 255, 255, 0.22);
  backdrop-filter: blur(8px) saturate(140%);
  -webkit-backdrop-filter: blur(8px) saturate(140%);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
  color: #fff;
  font-size: 0.74rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
/* A position it happens to occupy, rather than one it was given. Same shape so
   the column still lines up, quieter so the difference is visible. */
.prog-seq.is-implied {
  color: rgba(255, 255, 255, 0.62);
  border-color: rgba(255, 255, 255, 0.12);
}
/* The compact card has no cover behind it, so the badge takes the page's own
   surface instead of a scrim over artwork. */
.card-compact .prog-seq {
  background: var(--color-primary-subtle, rgba(99, 102, 241, 0.16));
  border-color: transparent;
  color: var(--color-primary);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: none;
}
.card-compact .prog-seq.is-implied { color: var(--text-tertiary); }
/* Clear of the number. */
.card-compact .card-compact-head { padding-left: 1.9rem; }

/* Only while reordering. Two buttons on every card is a lot of furniture for
   the rarer job, so they arrive with the mode and leave with it. */
.prog-move { display: inline-flex; flex-direction: column; gap: 2px; flex-shrink: 0; }
.prog-move-btn {
  width: 20px;
  height: 15px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--border-color);
  border-radius: 4px;
  background: var(--bg-surface);
  color: var(--text-secondary);
  cursor: pointer;
}
.prog-move-btn:hover { border-color: var(--color-primary); color: var(--color-primary); }
.prog-move-btn svg { width: 11px; height: 11px; }
