/* ═══════════════════════════════════════════════════════════════════════
   V5 — text-forward minimal design layer
   ─────────────────────────────────────────────────────────────────────
   Philosophy: no buttons, no checkboxes, no pills. Every control is a
   word. State is shown by color and a left accent bar. The kc-app grid
   (board left + right pane) is reused; v5 overrides typography,
   spacing, and the control primitives.

   Naming: .v5-* classes only — so a page can opt into v5 look while
   keeping the shared board/sound/settings infrastructure.
   ═══════════════════════════════════════════════════════════════════ */

/* ── Row-indent variables ────────────────────────────────────────
   Every v5 "row" primitive (.v5-toggle, .v5-row, .v5-field,
   .v5-section-label, .v5-action-bar, .v5-list-box) uses these
   variables for its horizontal padding or margin. Desktop values
   mirror the historical 14px/0 inset (row text starts 14px from
   the container's inner edge, right side flush with the .v5-scroll
   right gutter).

   On mobile the .v5-scroll is flattened with display:contents, so
   there is no container gutter any more. The mobile override below
   bumps both sides to 20px so rows stay inset from the viewport
   edge by exactly the same amount as .kc-left's 20px board padding.

   The point of using variables (and NOT scattering
   `.kc-app.v5-puzzle-mobile .v5-scroll > * { padding: 0 20px }` style
   overrides on every descendant) is that the alignment follows the
   primitive, not the container. A .v5-toggle nested inside a BV
   panel lines up with a .v5-toggle that's a direct scroll child —
   on both breakpoints — without any per-selector mobile overrides. */
:root {
  --v5-row-pad-l: 0;
  --v5-row-pad-r: 0;
}
@media(max-width:780px) {
  .kc-app.v5-puzzle-mobile {
    --v5-row-pad-l: 20px;
    --v5-row-pad-r: 20px;
  }
}

/* ── Typography baseline ─────────────────────────────────────────── */
/* .v5 fills the right pane as a flex column so the topbar stays put
   while .v5-scroll (flex:1) owns the vertical overflow. Without this
   the .v5-scroll height collapsed to its content and nothing scrolled. */
.v5 {
  font-family: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  font-feature-settings: 'ss01', 'ss02';
  color: var(--text);
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  height: 100%;
  position: relative;
  isolation: isolate;
}

/* ── Breadcrumb — replaces titles + back links on every v5 page ─── */
.v5-crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--dim);
  line-height: 1.4;
  /* Sticky under the topbar. The scroll container provides no top
     padding (so the breadcrumb can sit flush at the top edge); the
     breadcrumb owns its own top padding instead. Horizontal negative
     margins + matching padding stretch the breadcrumb edge-to-edge so
     its background fully covers the gap while text stays aligned with
     the row-text column (14 px accent gutter). */
  position: sticky;
  top: 0;
  background: var(--bg-r);
  margin: 0 -24px 20px;
  padding: 20px 24px 14px;
  z-index: 5;
}
.v5-crumbs a,
.v5-crumbs span {
  color: var(--dim);
  text-decoration: none;
  transition: color .12s;
}
.v5-crumbs a:hover { color: var(--text); }
.v5-crumbs .v5-crumb-sep {
  color: var(--dim);
  opacity: .4;
  user-select: none;
}
.v5-crumbs .v5-crumb-current {
  color: var(--text);
  font-weight: 600;
}

/* "BACK / PAGE" breadcrumb variant — mirrors the v5-toggle on/off look
   so the back link and current page title read as one binary control.
   Adds a visible separator slash between them. */
.v5-crumbs.v5-crumbs-back {
  justify-content: flex-start;
  text-transform: uppercase;
  letter-spacing: .1em;
}
.v5-crumbs .v5-crumb-back {
  color: var(--dim);
  transition: color .12s;
  cursor: pointer;
}
.v5-crumbs .v5-crumb-back:hover { color: var(--accent); }
.v5-crumbs .v5-crumb-slash {
  color: var(--dim);
  opacity: .4;
  user-select: none;
  margin: 0 2px;
}

/* ── Swatch grids (board colors + piece sets) ────────────────────────
   Picked up from the shared settings partial so the board + piece choices
   stay familiar. The outer grid is left-indented to align with the
   row-text column, the swatch itself keeps the original 4-quadrant
   board tile and 64 px piece tile so nothing looks miniaturised. */
.v5-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 6px 0 18px;
}
.v5-swatch {
  width: 44px;
  height: 44px;
  border-radius: 4px;
  overflow: hidden;
  border: 2px solid var(--bd);
  cursor: pointer;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  transition: border-color .12s, transform .12s;
}
.v5-swatch span { display: block; }
.v5-swatch:hover { border-color: var(--dim); }
.v5-swatch.is-on { border-color: var(--accent); }

.v5-piece-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 6px 0 18px;
}
.v5-piece-swatch {
  width: 60px;
  height: 60px;
  padding: 6px 4px;
  border: none;
  background: none;
  color: var(--dim);
  font-size: 10px;
  font-family: inherit;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  box-sizing: border-box;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  position: relative;
  transition: color .12s;
}
.v5-piece-swatch img { width: 28px; height: 28px; object-fit: contain; display: block; flex-shrink: 0; }
.v5-piece-swatch:hover { color: var(--text); }
.v5-piece-swatch.is-on { color: var(--accent); }
/* Selected piece pack gets a small underline dot below the label
   instead of a box border, to match the borderless piece tiles. */
.v5-piece-swatch.is-on::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 2px;
  transform: translateX(-50%);
  width: 18px;
  height: 2px;
  background: var(--accent);
  border-radius: 1px;
}

/* Custom-board swatch — four editable slots next to the preset board
   swatches. The small ... button in the top-right opens the colour
   picker modal. Mirrors the legacy .kc-custom-swatch markup so the shared
   _customBoardClick / _openCustomBoardModal helpers work unchanged. */
.v5-swatch.v5-custom-swatch { position: relative; }
.v5-swatch.v5-custom-swatch .v5-custom-edit {
  position: absolute;
  top: 2px;
  right: 3px;
  width: 14px;
  height: 14px;
  background: rgba(0, 0, 0, .45);
  color: #fff;
  border-radius: 50%;
  font-size: 10px;
  line-height: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  user-select: none;
}
.v5-swatch.v5-custom-swatch .v5-custom-edit:hover { background: rgba(0, 0, 0, .75); }

/* ── Row edit-mode controls ──────────────────────────────────────────
   When the list is in "edit" mode, a drag handle and selection
   checkbox slide into the 28 px left gutter that the scroll container
   already owns — so the title text never shifts right. The accent
   bar and hover highlight are suppressed in edit mode so the rows
   read as a manageable list rather than a clickable menu. */
.v5-row-handle,
.v5-row-cb {
  display: none;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}
.v5-row-handle {
  /* Sits in the scroll container's left padding (28 px), the same
     rail the row marker uses (`.v5-row::before { left: -8px }`) and
     the section chevron uses (`.v5-sec-arrow { left: -22px }`).
     One shows in view mode (chevron, sections only), the other in
     edit mode (handle, both rows) — so drag controls line up
     vertically across section + chapter rows. */
  left: -22px;
  cursor: grab;
  color: var(--dim);
  font-size: 13px;
  user-select: none;
  line-height: 1;
  padding: 0;
  width: 10px;
  text-align: center;
}
.v5-row-handle:active { cursor: grabbing; }
.v5-row-cb {
  right: 4px;
  width: 14px;
  height: 14px;
  cursor: pointer;
  margin: 0;
  accent-color: var(--accent);
}
.v5-rows-editing .v5-row-handle { display: inline-block; }
.v5-rows-editing .v5-row-cb      { display: inline-block; }
.v5-rows-editing .v5-ch-row {
  cursor: default;
}
.v5-rows-editing .v5-ch-row::before { background: transparent !important; }
.v5-rows-editing .v5-ch-row:hover { color: var(--text); }
/* Hide the right-side stats hint and the section LEARN shortcut in
   edit mode so the far-right checkbox has a clean landing strip. */
.v5-rows-editing .v5-row-hint,
.v5-rows-editing .v5-sec-learn { display: none; }
.v5-row.is-dragging { opacity: .4; }
.v5-row.is-drop-before { box-shadow: 0 2px 0 0 var(--accent) inset; }
.v5-row.is-drop-after  { box-shadow: 0 -2px 0 0 var(--accent) inset; }

/* Inline rename input sits exactly where the chapter name was, so
   toggling into rename doesn't jump the layout. */
.v5-row-rename {
  background: none;
  border: none;
  border-bottom: 1px solid var(--accent);
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  outline: none;
  padding: 0;
  margin: 0;
  flex: 1;
  min-width: 0;
}

/* Bulk-action bar that appears when rows are selected in edit mode. */
.v5-bulk-bar {
  display: flex;
  gap: 12px;
  align-items: center;
  padding: 8px 0 8px 14px;
  font-size: 12px;
  color: var(--dim);
  letter-spacing: .04em;
  text-transform: uppercase;
  border-top: 1px dashed var(--bd);
  border-bottom: 1px dashed var(--bd);
  margin: 10px 0;
}
.v5-bulk-bar .v5-bulk-action {
  background: none;
  border: none;
  color: var(--err);
  font-family: inherit;
  font-size: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
  padding: 2px 4px;
}
.v5-bulk-bar .v5-bulk-action:hover { color: var(--err); text-decoration: underline; }
.v5-bulk-bar .v5-bulk-spacer { margin-left: auto; }

/* Pen (rename) button — appears inline next to the name row in edit
   mode. Same typographic weight as the row text so it reads as a
   siblings pair, not a chrome element. Hidden outside edit mode. */
.v5-row-pen {
  display: none;
  background: none;
  border: none;
  color: var(--dim);
  cursor: pointer;
  font-family: inherit;
  font-size: 12px;
  line-height: 1;
  padding: 0 6px;
  margin-left: 4px;
  transition: color .12s;
}
.v5-row-pen:hover { color: var(--accent); }
.v5-rows-editing .v5-row-pen { display: inline-block; }
/* Pause toggle: always visible (not gated on editing mode) and remains
   operable even on muted rows where the parent .v5-row sets
   pointer-events:none. Reuses the .v5-row-pen base for sizing/colour. */
.v5-row-pause {
  display: none;
  pointer-events: auto;
  opacity: 1;
  cursor: pointer;
}
/* Pause / unpause is an editing-only control — visible only when the
   user has flipped the chapter list into edit mode. In view mode, the
   chapter row carries an inline "paused" tag instead. The button is
   absolutely positioned on the right edge of the row, immediately to
   the LEFT of the checkbox (which sits at right:4px with width:14px),
   so they read as a paired "select + state-toggle" cluster. */
.v5-rows-editing .v5-row-pause {
  display: inline-block;
  position: absolute;
  right: 28px;
  top: 50%;
  transform: translateY(-50%);
  margin: 0;
  padding: 0;
  font-size: 14px;
  line-height: 1;
}
/* When in edit mode, the inline `paused` / `rare` tags are redundant
   (the pause button + filter dropdown surface that state) — hide them
   so the row stays compact. */
.v5-rows-editing .v5-tag-paused,
.v5-rows-editing .v5-tag-rare { display: none; }
.v5-row.is-disabled .v5-row-pause {
  /* override the parent .v5-row.is-disabled mute so the toggle stays
     clickable + readable when the chapter is paused. */
  opacity: 1;
  pointer-events: auto;
  cursor: pointer;
  color: var(--dim);
}
.v5-row-pause:hover { color: var(--accent); }
/* Section header pause button always sticks to the right edge, even
   when the section has no EXPLORE/LEARN buttons (every chapter is
   filtered or the section is empty). The auto-margin is a no-op when
   another sibling already has margin-left:auto pushing first. */
.v5-sec-row .v5-row-pause { margin-left: auto; }
/* The ch-action ~ row-pause rule below is the canonical sibling spacing
   for sec-row pause; the older sec-learn-anchored rule was dropped to
   keep section + chapter row icon spacing identical. */

/* Section row — same controls as a chapter row so drag handle and
   checkbox line up vertically across the list. Position:relative is
   needed for absolute children; the LEARN anchor stays at the right
   edge, and the rename pen sits inline after the name. */
.v5-sec-row {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  user-select: none;
  position: relative;
}
/* Section-head text sizing matches a deselected `.v5-tabs .v5-tab`
   (12px, .08em tracking, uppercase, dim, weight 400). Higher
   specificity than the generic `.v5-section-label` above so the
   section rows in the chapter list read as tabs rather than tiny
   subtitles. */
.v5-section-label.v5-sec-row {
  font-size: 12px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--dim);
  font-weight: 400;
}
.v5-sec-row .v5-ch-name { color: var(--dim); }
.v5-sec-row .v5-sec-arrow { color: var(--dim); }
.v5-sec-row .v5-sec-arrow {
  /* Sits in the scroll container's left padding (28 px), the same
     rail the row marker and drag handle use. Hidden in edit mode so
     the drag handle owns the rail. */
  position: absolute;
  left: -22px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 9px;
  width: 10px;
  text-align: center;
  flex-shrink: 0;
  z-index: 1;
}
.v5-rows-editing .v5-sec-arrow { display: none; }
.v5-sec-row .v5-sec-learn {
  font-size: 10px;
  color: var(--dim);
  letter-spacing: .08em;
  text-decoration: none;
  transition: color .12s;
}
/* First .v5-sec-learn in the row gets pushed to the right edge.
   Subsequent siblings (multiple action buttons) stack next to it
   with a small gap. */
/* sec-learn alignment lives in the .v5-ch-action rules further down
   so section + chapter rows share one source of truth. */
.v5-sec-row .v5-sec-learn:hover { color: var(--accent); }
/* Icon-style section action buttons (book/magnifier emoji) need a
   bigger glyph than the original text labels (font-size:10px) so they
   stay legible. */
.v5-sec-row .v5-sec-learn.v5-sec-icon {
  font-size: 14px;
  letter-spacing: 0;
}
/* Per-chapter action buttons (learn/explore icons) — always visible,
   hidden only when the bulk-edit mode is on (matches v5-sec-learn). */
.v5-row-pen.v5-ch-action { display: inline-block; }
.v5-rows-editing .v5-row-pen.v5-ch-action { display: none; }
/* Keep these clickable on disabled/paused rows so users can launch
   single-chapter learn/explore even when the chapter is excluded from
   bulk learn-section / learn-opening runs. */
.v5-row.is-disabled .v5-ch-action,
.v5-row.is-paused   .v5-ch-action {
  pointer-events: auto;
  cursor: pointer;
  opacity: 1;
  color: var(--dim);
}
/* Trailing icon group on chapter rows + section header — pinned to
   the right edge with even, tight spacing so book/search/pause read as
   one compact action cluster. Each button has the same horizontal
   padding so siblings sit symmetrically; the first action carries
   margin-left:auto so the cluster floats to the right. Icon size is
   the same on section and chapter rows so they line up vertically. */
.v5-row .v5-ch-action,
.v5-row .v5-ch-action ~ .v5-row-pause,
.v5-sec-row .v5-ch-action,
.v5-sec-row .v5-ch-action ~ .v5-row-pause {
  padding: 0;
  margin: 0 0 0 4px;
  font-size: 14px;
}
/* Chapter rows inherit `.v5-row { gap: 14px }` which made trailing
   icon spacing visibly larger on chapter rows than on section rows
   (14+4=18px vs 8+4=12px). Match the section row's 8px so both
   clusters render with identical inter-button gaps. */
.v5-row.v5-ch-row { gap: 8px; }
.v5-row .v5-ch-action:first-of-type,
.v5-sec-row .v5-ch-action:first-of-type {
  margin-left: auto;
}
/* The hint span (e.g. "♟ 5 · 50%" inside .v5-row-hint, or
   "📖 X/Y · Z%" in the openings list) renders its book/pawn icon plus
   numbers with even gaps via inline-flex so the icon doesn't visually
   cling to one neighbour. */
.v5-row-hint {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* Each icon-number stat pair (e.g. book + X/Y, pawn + Z) sits as a
   tight inline-flex unit so the gap between glyph and number is the
   same regardless of which Tabler icon's intrinsic padding. */
.v5-stat {
  display: inline-flex;
  align-items: center;
  gap: 3px;
}
.v5-sec-row.is-dragging { opacity: .4; }
.v5-sec-row.is-drop-before { box-shadow: 0 2px 0 0 var(--accent) inset; }
.v5-sec-row.is-drop-after  { box-shadow: 0 -2px 0 0 var(--accent) inset; }

/* ── Tab strip (Chapters / Settings) ─────────────────────────────────
   Two words separated by a dim slash, the active one in accent and
   weight 700. Mirrors the look of a v5-toggle so the page reads
   consistently across its primitives. */
.v5-tabs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
  font-size: 12px;
  letter-spacing: .08em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--bd);
  margin-bottom: 14px;
}
.v5-tabs .v5-tab {
  background: none;
  border: none;
  color: var(--dim);
  font-family: inherit;
  font-size: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
  padding: 4px 0;
  transition: color .12s;
}
.v5-tabs .v5-tab:hover { color: var(--text); }
.v5-tabs .v5-tab.is-on { color: var(--accent); font-weight: 700; }
.v5-tabs .v5-tab-sep { color: var(--dim); opacity: .4; user-select: none; }
.v5-tab-pane { display: none; }
.v5-tab-pane.is-on { display: block; }

/* Danger zone — visual separator for destructive actions at the
   bottom of the Settings tab. */
.v5-danger-zone {
  margin-top: 28px;
  padding-top: 14px;
  border-top: 1px dashed var(--bd);
}

/* Modal text alignment — title, body, status lines sit flush with
   field labels and row-button text. Both row/field padding and modal
   text are now at 0 (the previous 14 px gutter held space for the
   row marker, which has since moved into the scroll container's
   left padding). */

/* Puzzle-page action bar — equal-width text buttons on a single row.
   Each button gets its own hover underline (accent colour) so the
   blue line moves with the cursor rather than spanning the whole
   bar. Hidden buttons collapse to 0 space thanks to flex:1. */
.v5-action-bar {
  display: flex;
  align-items: stretch;
  gap: 0;
  /* Use the same left/right indent variables as rows so buttons sit
     in the row-text column on desktop AND mobile. */
  padding: 10px var(--v5-row-pad-l);
  margin: 4px 0 14px;
}
.v5-action-btn {
  flex: 1;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .02em;
  cursor: pointer;
  /* No horizontal padding — text-align:center handles spacing inside
     the equal-width bar buttons, and standalone uses (scouting
     "Fetch games", account fetch buttons) get text flush at the
     column edge. */
  padding: 6px 0;
  text-align: center;
  text-decoration: none;
  transition: color .12s, border-color .12s;
}

/* V5 override for rating / time-control filter pills in settings.
   Strip the legacy pill outline; active state is the accent colour with
   a 2px underline, hover just changes the text colour. */
#ed-rating-btns      .ed-filter-btn,
#ed-tc-btns          .ed-filter-btn,
#sc-tc-btns          .ed-filter-btn,
#sc-platform-btns    .ed-filter-btn,
#sc-section-toggles  .ed-filter-btn {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  padding: 4px 6px;
  color: var(--dim);
  font-weight: 500;
  transition: color .12s, border-color .12s;
}
#ed-rating-btns      .ed-filter-btn:hover,
#ed-tc-btns          .ed-filter-btn:hover,
#sc-tc-btns          .ed-filter-btn:hover,
#sc-platform-btns    .ed-filter-btn:hover,
#sc-section-toggles  .ed-filter-btn:hover {
  color: var(--text);
  border-bottom-color: transparent;
}
#ed-rating-btns      .ed-filter-btn.active,
#ed-tc-btns          .ed-filter-btn.active,
#sc-tc-btns          .ed-filter-btn.active,
#sc-platform-btns    .ed-filter-btn.active,
#sc-section-toggles  .ed-filter-btn.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
  font-weight: 600;
}

/* PGN-bar active move — drop the pill outline, use a bottom accent
   line matching the rest of the v5 design language. */
.pgn-bar-active {
  background: none !important;
  color: var(--accent) !important;
  border: none !important;
  border-radius: 0 !important;
  padding: 1px 4px !important;
}

.v5-action-btn:hover {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
.v5-action-btn.is-accent { color: var(--accent); font-weight: 700; }
.v5-action-btn.is-danger { color: var(--err); }

/* Next is kept visible at all times per v5 UX. puzzle.js's jQuery
   .hide() sets inline display:none; the !important override wins so
   the button always takes its flex slot. */
.v5-action-btn#next-btn { display: inline-block !important; }
.v5-action-btn:disabled { color: var(--dim); opacity: 0.45; cursor: default; pointer-events: none; }

/* V5 puzzle page — vertical stack on mobile only. Desktop keeps the
   standard kc-app two-column shell (board left, right pane right). The
   bootstrap in puzzle.html tags the .kc-app wrapper with this class; the
   actual reshaping rules only fire under the mobile media query so
   wider viewports stay as-is. */
@media(max-width:780px) {
  .kc-app.v5-puzzle-mobile {
    display: flex;
    flex-direction: column;
    height: auto;
    overflow: auto;
  }
  /* Flatten .kc-right, .v5 and .v5-scroll so every descendant
     (topbar, breadcrumb, action bar, stats, ...) becomes a direct
     flex child of .kc-app and can be individually ordered into the
     desired vertical stack: topbar → breadcrumb → board → the rest. */
  .kc-app.v5-puzzle-mobile .kc-right  { display: contents; }
  .kc-app.v5-puzzle-mobile .v5        { display: contents; }
  .kc-app.v5-puzzle-mobile .v5-scroll { display: contents; }

  .kc-app.v5-puzzle-mobile .v5-topbar {
    order: 1;
    border-bottom: 1px solid var(--bd);
    background: var(--bg-r);
    padding-top: max(14px, env(safe-area-inset-top, 14px));
  }
  /* .kc-left has overflow:hidden on desktop (grid cell clipping);
     on mobile it clips the PGN nav row that sits below the board.
     Allow overflow + leave room above the board for .board-above
     (absolute-positioned status line that sits at bottom:100% of
     the board-wrap). Tight bottom padding keeps the gap between
     the nav row and the puzzle action bar small. */
  .kc-app.v5-puzzle-mobile .kc-left {
    padding: 56px 20px 4px;
    order: 3;
    overflow: visible;
  }
  /* Pull the action bar up right under the board-nav row. */
  .kc-app.v5-puzzle-mobile .v5-action-bar {
    margin-top: 0;
    padding-top: 4px;
    padding-bottom: 4px;
  }
  /* Direct children of .v5-scroll are stacked AFTER the board via
     flex ordering. Horizontal padding is owned by each primitive's
     own rule (which reads --v5-row-pad-l / --v5-row-pad-r), so we
     only set order here — no per-element mobile padding overrides. */
  .kc-app.v5-puzzle-mobile .v5-scroll > * { order: 4; }
  /* Opening trainer: show the chapter title (v5-title-row) between the
     topbar and the board on mobile — topbar (1) → title (2) → board
     (3) → rest (4). Puzzle pages don't have a .v5-title-row, so this
     rule is a no-op there. */
  .kc-app.v5-puzzle-mobile .v5-scroll > .v5-title-row {
    order: 2;
    padding: 10px 20px 0;
    margin: 0;
  }

  .kc-app.v5-puzzle-mobile .kc-board-std {
    max-width: min(calc(100vw - 40px), 520px);
  }
}

/* ── Text-as-button row ──────────────────────────────────────────── */
/*  Replaces <button class="kc-btn-line">. Row of plain text with a
    hairline underscore on hover and a left accent bar when active.
    Tap target is the whole row (min 36px tall). */
.v5-row-list {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
}
.v5-row {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 36px;
  padding: 8px var(--v5-row-pad-r) 8px var(--v5-row-pad-l);
  color: var(--text);
  text-decoration: none;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: .01em;
  cursor: pointer;
  position: relative;
  transition: color .12s, background-color .12s;
  background: none;
  border: none;
  border-radius: 0;
  text-align: left;
  font-family: inherit;
  width: 100%;
}
.v5-row::before {
  content: '';
  position: absolute;
  /* Sits in the scroll container's left padding (28 px) so the menu
     text itself can be flush-left with titles / breadcrumbs / etc.
     The marker remains 2 px wide and the same height as the row. */
  left: -8px; top: 8px; bottom: 8px;
  width: 2px;
  background: transparent;
  transition: background-color .12s;
}
.v5-row:hover {
  color: var(--accent);
}
.v5-row:hover::before {
  background: var(--accent);
}
.v5-row.is-active {
  color: var(--accent);
}
.v5-row.is-active::before {
  background: var(--accent);
}
.v5-row[disabled], .v5-row.is-disabled, .v5-row.is-paused {
  color: var(--dim);
  opacity: .5;
}
.v5-row.is-paused .v5-row-pause,
.v5-row.is-disabled .v5-row-pause {
  pointer-events: auto;
  cursor: pointer;
}
.v5-sec-row.is-paused .v5-ch-name { opacity: .5; color: var(--dim); }
/* Inline state tags after the chapter title. Small uppercase pill,
   colour-coded so freq-filtered ("rare") and user-paused ("paused")
   don't get visually conflated. */
.v5-tag {
  display: inline-block;
  font-size: 9px;
  letter-spacing: .12em;
  text-transform: uppercase;
  padding: 1px 6px;
  margin-left: 8px;
  border-radius: 999px;
  font-weight: 600;
  vertical-align: middle;
  flex-shrink: 0;
}
.v5-tag-rare   { background: var(--bg2, #2a2a2a); color: var(--dim); }
.v5-tag-paused { background: var(--accent); color: var(--bg, #111); }
.v5-row[disabled]:hover::before, .v5-row.is-disabled:hover::before {
  background: transparent;
}
.v5-row .v5-row-hint {
  margin-left: auto;
  font-size: 11px;
  color: var(--dim);
  font-weight: 400;
  letter-spacing: .04em;
}
.v5-row.is-danger { color: var(--err); }
.v5-row.is-danger:hover { color: var(--err); }
.v5-row.is-danger:hover::before { background: var(--err); }

/* First-moves mode: reserve room below the board for the
   `#fm-instruction` strip so the instruction stays in the viewport.
   Without this, app.css's `calc(100vh - 152px)` assumes only board-
   above + nav row sit below the board; fm-instruction (≈70px + margin)
   pushes the strip off-screen on short viewports.

   Safari/iOS: `100vh` counts the area behind the URL bar so the
   calc resolves too large and the board bleeds past the actual
   visible viewport. Declare both `vh` (Chrome/FF fallback) and `svh`
   (small viewport height = visible on Safari) so the browser picks
   whichever it understands. */
.kc-app.v5-fm-mode .kc-board-wrap {
  /* Two layers: `100vh` for browsers that don't know `svh`, then
     `100svh` (small viewport height — visible area on Safari/iOS,
     excluding URL bar) overrides it. Bump the subtrahend to 280px
     to cover status line + nav row + fm-instruction (≈70px) + body
     padding with some buffer. */
  max-width:  min(800px, calc(100vh  - 280px), 100%);
  max-width:  min(800px, calc(100svh - 280px), 100%);
  max-height: calc(100vh  - 280px);
  max-height: calc(100svh - 280px);
}

/* Preset row under a puzzle-mode row — indented, dim, with edit/
   delete actions tucked on the far right. The name span is a
   sibling <a> (not a wrapper) so the action buttons aren't nested
   inside the navigation link; that lets them behave as normal
   buttons without stopPropagation gymnastics. */
.v5-preset-row {
  color: var(--dim);
  font-size: 13px;
  font-weight: 400;
}
.v5-preset-row:hover { color: var(--text); }
.v5-preset-row .v5-preset-launch {
  flex: 1;
  color: inherit;
  text-decoration: none;
}
.v5-preset-row .v5-preset-launch:hover { color: var(--text); }
.v5-preset-row .v5-preset-actions {
  margin-left: auto;
  display: inline-flex;
  gap: 6px;
  opacity: .55;
  transition: opacity .12s;
}
.v5-preset-row:hover .v5-preset-actions { opacity: 1; }
.v5-preset-btn {
  background: none;
  border: none;
  color: inherit;
  font: inherit;
  font-size: 13px;
  line-height: 1;
  padding: 2px 6px;
  cursor: pointer;
  border-radius: 3px;
  transition: color .12s, background-color .12s;
}
.v5-preset-btn:hover { color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
.v5-preset-btn.v5-preset-del:hover { color: var(--err); background: color-mix(in srgb, var(--err) 12%, transparent); }

/* Group header above a row list — small caps label. */
.v5-section-label {
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--dim);
  margin: 22px 0 6px;
  padding-left: var(--v5-row-pad-l);
  font-weight: 500;
}
.v5-section-label:first-child { margin-top: 0; }

/* The first row inside the chapters list sits directly below the
   "Explore all" row above it. Zero its top gap so the separation
   matches the chapter-to-chapter rhythm (just the 8/8 row padding)
   rather than the wider 22px section-label margin. */
#v5-chapters-container > *:first-child,
#v5-chapters-container > *:first-child.v5-section-label { margin-top: 0; }

/* When a section label is used as the inline label of a v5-toggle row
   (theme picker section headers), the row's own left padding already
   aligns it with sibling rows — drop the section label's padding/margin
   so it sits on the same vertical and horizontal grid as per-theme
   labels. Vertical separation between sections is provided by margin
   on the wrapping row instead. */
.v5-toggle > .v5-section-label {
  padding-left: 0;
  margin: 0;
}
.v5-theme-group-row,
.v5-theme-combo-row {
  margin-top: 16px;
}
.v5-scroll > .v5-theme-group-row:first-child,
.v5-scroll > .v5-theme-combo-row:first-child {
  margin-top: 0;
}


/* Horizontal divider between row groups (optional, very thin). */
.v5-rule {
  border: none;
  border-top: 1px solid var(--bd);
  margin: 16px 0;
  opacity: .5;
}

/* ── Toggle — "Label  on / off" ──────────────────────────────────── */
/*  Renders a row where the right side holds two words: ON / OFF.
    Active one is accent-coloured and bold; inactive is dim. Click
    either word to set the state. */
.v5-toggle {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 8px var(--v5-row-pad-r) 8px var(--v5-row-pad-l);
  min-height: 36px;
  font-size: 14px;
  position: relative;
}
/* No left accent bar on toggles — state is signalled by the
   highlighted on/off word on the right. */
.v5-toggle-label {
  flex: 1;
  color: var(--text);
  font-weight: 500;
  cursor: default;
  user-select: none;
}
/* Only show the click affordance when the row's options actually have
   clickable buttons (toggles / switches). Stats rows whose options are
   read-only spans (Session time, Misses, Score, Chapter X / Y, etc.) get
   the default cursor. */
.v5-toggle:has(.v5-toggle-options button) .v5-toggle-label {
  cursor: pointer;
}
/* Timer value spans (#session-timer / #puzzle-timer / #stat-timer) —
   tabular-nums keeps every digit the same width, so the ⏱ button to
   the left doesn't jiggle when the elapsed time changes (e.g. 0:09 →
   0:10 in a proportional font would shift the row by ~2px). */
#session-timer, #puzzle-timer, #stat-timer {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
}

/* ⏱ button next to a timer label (Session Time / Puzzle Time). Small,
   ghost-styled icon button that opens the set-timer modal. */
.v5-timer-set-btn {
  background: transparent;
  border: none;
  padding: 0 2px;
  margin-left: 6px;
  color: var(--dim);
  cursor: pointer;
  font-size: 14px;
  vertical-align: middle;
  line-height: 1;
}
.v5-timer-set-btn:hover {
  color: var(--text);
}
.v5-toggle-options {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12px;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.v5-toggle-options button,
.v5-toggle-options span {
  background: none;
  border: none;
  padding: 4px 0;
  font-family: inherit;
  font-size: inherit;
  letter-spacing: inherit;
  color: var(--dim);
  cursor: pointer;
  transition: color .12s;
}
.v5-toggle-options button:hover { color: var(--text); }
.v5-toggle-options button.is-on {
  color: var(--accent);
  font-weight: 700;
}
.v5-toggle-options .v5-toggle-sep {
  color: var(--dim);
  opacity: .35;
  cursor: default;
}

/* Bordered content container used by mode panels (BV selections /
   Positional picks / …). Margin-based horizontal inset so its
   border lines up with .v5-toggle's left content column on desktop
   and with the board edge on mobile. Internal padding stays 6px so
   the pills inside don't hug the border. */
.v5-list-box {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  min-height: 32px;
  padding: 6px;
  border: 1px solid var(--bd);
  border-radius: 3px;
  margin-left: var(--v5-row-pad-l);
  margin-right: var(--v5-row-pad-r);
}

/* Free-form text block inside a mode panel (instructions, task list,
   etc.). Same left/right indent as rows. */
.v5-mode-text {
  padding-left: var(--v5-row-pad-l);
  padding-right: var(--v5-row-pad-r);
  font-size: 12px;
  color: var(--dim);
  line-height: 1.5;
  margin-bottom: 8px;
}

/* Display-only stat rows (Session Time / Session Rating / Puzzle
   Rating / Blunder / Best move / etc.) share the .v5-toggle shell
   with on/off switches, so they line up in the same right-column
   gutter. But semantically they're name ⇆ value pairs, not
   switchable controls, and the label should read as a field caption
   (small, uppercase, dim) while the value should be the dominant
   text, not dimmed. The structural selector below targets rows
   where the options slot contains a single `.is-on` span (no
   buttons, no separators) and only restyles those — toggle rows
   keep their existing look. */
/* Stat rows match if the options slot is either:
   1. just an `.is-on` value span (Session Rating, Puzzle Rating, etc.), OR
   2. a `.v5-timer-set-btn` followed by an `.is-on` value (Session Time,
      Puzzle Time, opening Session time) — the ⏱ button is ancillary and
      shouldn't disqualify the row from the stat-row caption/value styling. */
.v5-toggle:has(> .v5-toggle-options > .is-on:only-child) > .v5-toggle-label,
.v5-toggle:has(> .v5-toggle-options > .v5-timer-set-btn:first-child + .is-on:last-child) > .v5-toggle-label {
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--dim);
  font-weight: 500;
}
.v5-toggle:has(> .v5-toggle-options > .is-on:only-child) > .v5-toggle-options,
.v5-toggle:has(> .v5-toggle-options > .v5-timer-set-btn:first-child + .is-on:last-child) > .v5-toggle-options {
  font-size: 14px;
  letter-spacing: 0;
  text-transform: none;
}
.v5-toggle:has(> .v5-toggle-options > .is-on:only-child) .is-on,
.v5-toggle:has(> .v5-toggle-options > .v5-timer-set-btn:first-child + .is-on:last-child) .is-on {
  color: var(--text);
  font-weight: 500;
}

/* ── Input / select — full-width, underline only ─────────────────── */
/*  Tiny uppercase label floats above. No border box, just a hairline
    under the field. Focus deepens the underline to accent.
    Left padding matches the row/breadcrumb column so labels line up
    vertically with menu text. */
.v5-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 14px 0;
  padding-left: var(--v5-row-pad-l);
  padding-right: var(--v5-row-pad-r);
}
.v5-field-label {
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--dim);
  font-weight: 500;
}
.v5-input, .v5-select, .v5-textarea {
  width: 100%;
  background: none;
  border: none;
  border-bottom: 1px solid var(--bd);
  padding: 8px 0;
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
  outline: none;
  transition: border-color .12s;
  border-radius: 0;
}
.v5-input:focus, .v5-select:focus, .v5-textarea:focus {
  border-bottom-color: var(--accent);
}
.v5-select {
  appearance: none;
  cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, var(--dim) 50%),
                    linear-gradient(135deg, var(--dim) 50%, transparent 50%);
  background-position: calc(100% - 10px) 50%, calc(100% - 6px) 50%;
  background-size: 4px 4px, 4px 4px;
  background-repeat: no-repeat;
  padding-right: 22px;
}
.v5-textarea { resize: vertical; min-height: 80px; }

/* ── Heading (used sparingly — page body introductions) ──────────── */
.v5-heading {
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--text);
  margin: 0 0 6px;
}

/* Page title — sits right under the topbar, displays the
   current page / opening / puzzle type that was removed from the
   breadcrumb per the 2-item rule. Flush-left with the row-text
   column (both at 0). */
.v5-title {
  font-size: 20px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -.01em;
  margin: 0 0 18px;
}

/* Compact prev/next buttons next to chapter titles. Overrides kc-nb
   padding + gap so the arrows sit close together and vertically
   centred against the chapter name (itself 20px / line-height ~1.2).
*/
.v5-ch-nav { gap: 0; }
.v5-ch-nav .kc-nb {
  padding: 4px 4px;
  line-height: 1;
  min-height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* When the title sits on a flex row with prev/next / pen buttons,
   drop its bottom margin so the flex items share the same vertical
   centre (margin moves the .v5-title's box center up relative to
   its unmargined siblings under `align-items: center`). */
.v5-title-row .v5-title { margin-bottom: 0; }
.v5-title-row { margin-bottom: 18px; }
.v5-sub {
  font-size: 13px;
  color: var(--dim);
  line-height: 1.6;
  margin: 0 0 20px;
  max-width: 420px;
}

/* ── Topbar slim / no chrome ─────────────────────────────────────── */
/* Content is right-aligned: username acts as the Settings shortcut,
   and the theme toggle lives to its right. No logout button here —
   log out lives inside the Settings panel. */
/* Two-column grid: [breadcrumb fills remaining space on the LEFT]
   [theme/user/PRO cluster on the RIGHT]. Grid columns guarantee
   layout regardless of DOM order, flex margin tricks, or any other
   rule that tried to push items around. */
.v5-topbar {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 14px;
  padding: 14px 24px;
  background: var(--bg-r);
  border-bottom: 1px solid var(--bd);
  font-size: 12px;
  color: var(--dim);
  flex-shrink: 0;
}
.v5-topbar-right {
  grid-column: 2;
  justify-self: end;
  display: flex;
  align-items: center;
  gap: 14px;
}
/* Topbar breadcrumb — visible on every viewport, pinned to the
   first grid column (left edge). The scroll-area breadcrumb is
   always hidden when a topbar one is present so the breadcrumb
   never renders twice. */
.v5-topbar .v5-crumbs-topbar {
  grid-column: 1;
  justify-self: start;
  /* Force single-line layout. The parent grid's 1fr column gives us
     the available width; block + nowrap + hidden + ellipsis collapses
     the whole breadcrumb onto one line with "…" if it'd overflow into
     the theme-toggle / user column on the right. */
  display: block;
  min-width: 0;
  max-width: 100%;
  margin: 0;
  padding: 0;
  position: static;
  background: transparent;
  z-index: auto;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .08em;
  line-height: 1.4;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
/* Children become inline so text-overflow ellipsis clips the whole
   run naturally. Replaces the flex `gap` with explicit horizontal
   margins on the separator. */
.v5-topbar .v5-crumbs-topbar > * { display: inline; }
.v5-topbar .v5-crumbs-topbar .v5-crumb-sep { margin: 0 6px; }
.v5:has(.v5-crumbs-topbar) .v5-scroll .v5-crumbs { display: none; }
.v5-topbar a { color: var(--dim); text-decoration: none; transition: color .12s; }
.v5-topbar a:hover { color: var(--text); }
.v5-topbar .v5-topbar-user {
  letter-spacing: .04em;
  cursor: pointer;
}
/* Gear icon sitting just before the username in the topbar.
   Opens the Settings overlay. Inherits topbar colour transitions so
   it dims/lights with the surrounding text. */
.v5-topbar-gear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  line-height: 1;
  padding: 2px 4px;
  text-decoration: none;
  cursor: pointer;
}
/* Trigger of the currently-visible overlay shows in active state. */
.v5-topbar a.v5-topbar-gear.is-active,
.v5-topbar a.v5-topbar-user.is-active { color: var(--text); }

/* Daily-activity streak indicator: small flame + count. Only visible
   when the user has an active streak (≥1 day). Hidden by default; the
   client fetches /api/user/streak on page load and shows it when
   `days > 0`. Uses the topbar's regular dim colour so it reads as one
   muted set with the gear / username. tabular-nums keeps the count
   from shifting width as the streak ticks up. */
.v5-topbar-streak {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 12px;
  line-height: 1;
  color: var(--dim);
  padding: 2px 4px;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
  user-select: none;
}
.v5-topbar-streak .ti { font-size: 14px; }
.v5-theme-btn {
  background: none;
  border: none;
  color: var(--dim);
  font-size: 14px;
  padding: 2px 4px;
  cursor: pointer;
  line-height: 1;
  font-family: inherit;
  transition: color .12s;
}
.v5-theme-btn:hover { color: var(--text); }

/* Themes-picker aside head (setup.html) — visually mirrors .v5-topbar /
   .v5-theme-btn but is semantically a panel-close header, not a page
   topbar. Kept distinct so the topbar-uniqueness audit (see spec at
   docs/superpowers/specs/2026-04-27-v5-topbar.md) stays clean. */
.v5-themes-aside-head {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 14px;
  padding: 14px 24px;
  background: var(--bg-r);
  border-bottom: 1px solid var(--bd);
  font-size: 12px;
  color: var(--dim);
  flex-shrink: 0;
}
.v5-themes-aside-close {
  background: none;
  border: none;
  color: var(--dim);
  font-size: 14px;
  padding: 2px 4px;
  cursor: pointer;
  line-height: 1;
  font-family: inherit;
  transition: color .12s;
}
.v5-themes-aside-close:hover { color: var(--text); }
.v5-pro-badge {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: .08em;
  color: var(--dim);
  border: 1px solid var(--bd);
  border-radius: 10px;
  padding: 2px 6px;
  line-height: 1.2;
}

/* ── Scroll content area inside right pane ───────────────────────── */
/* No top padding — the sticky breadcrumb owns the vertical breathing
   room at the top. Horizontal padding keeps row content indented from
   the right-pane edges. */
.v5-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  /* Horizontal padding matches `.v5-topbar { padding: 14px 24px }`
     so titles, tabs, fields, comboboxes — every direct child — sits
     in the same vertical column as the topbar breadcrumb. */
  padding: 20px 24px 48px;
  background: var(--bg-r);
}

/* ── Panel switcher ─────────────────────────────────────────────── */
.v5-panel { display: none; }
.v5-panel.active { display: block; }

/* ── Bottom-anchored avatar image ────────────────────────────────
   Decorative image pinned to the bottom of the right pane (.v5),
   always in the bottom half of the visible right pane regardless of
   scroll position. The image is absolutely positioned relative to
   .v5 (which is position:relative). It lives inside its panel /
   scroll container so display:none on inactive panels naturally
   hides their avatar.

   To keep the avatar visible behind the scrolling content, the
   right-pane background colour is hoisted from .v5-scroll up to .v5
   on pages that contain an avatar — and .v5-scroll becomes
   transparent. Pages without an avatar are untouched. */
.v5-avatar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: 50%;
  object-fit: contain;
  object-position: bottom center;
  pointer-events: none;
  user-select: none;
  z-index: -1;
}
.v5:has(.v5-avatar) { background: var(--bg-r); }
.v5:has(.v5-avatar) .v5-scroll { background: transparent; }

/* Tiny footer row under the main menu — About / Contact pair. */
.v5-footer {
  display: flex;
  gap: 12px;
  align-items: center;
  margin-top: 28px;
  font-size: 11px;
  color: var(--dim);
  letter-spacing: .04em;
}
.v5-footer a { color: var(--dim); text-decoration: none; transition: color .12s; }
.v5-footer a:hover { color: var(--text); }
.v5-footer .v5-footer-sep { opacity: .4; }

/* Strip the red outline + radius off the per-row delete (×) button in
   the analysis moves table. board.js renders an inline-styled button
   here and CLAUDE.md forbids touching that file, so override at the
   CSS layer with !important to beat the inline style. The hover
   colour stays as is — the user still sees that it's an actionable
   destructive control. */
#ed-moves-tbody button[title="Delete move"] {
  border: none !important;
  border-radius: 0 !important;
  padding: 0 4px !important;
  background: none !important;
}
