/* ============================================================
   EN PASSANT — "Study Hall" skin, applied app-wide.
   New furniture (rounded cards, chips, pill buttons, serif
   headings, callouts) in the app's ORIGINAL color scheme.
   Started as the Learn-tab trial; now covers all four tabs.
   The chess board, pieces, arrows, eval bar, page background,
   header and bottom nav are untouched.
   Revert: remove this file and its <link> in index.html.
   ============================================================ */

:root {
  /* Original app palette, mapped onto the new components */
  --ln-card: #d8cc9e;            /* --surface */
  --ln-card2: #c8b880;           /* --surface2 */
  --ln-border: #a08848;          /* --border */
  --ln-border-soft: rgba(160, 136, 72, 0.45);
  --ln-ink: #1e1008;             /* --text */
  --ln-dim: #7a5830;             /* --text-dim */
  --ln-faint: #9a7d4e;
  --ln-green: #3e7818;           /* --accent */
  --ln-green-deep: #2c5a10;
  --ln-green-tint: rgba(62, 120, 24, 0.12);
  --ln-green-tint2: rgba(62, 120, 24, 0.20);
  --ln-amber-ink: #8a5c0c;
  --ln-amber-tint: rgba(184, 121, 27, 0.14);
  /* Book "At a glance" style-meter spectrum colors. */
  --meter-solid: rgba(30, 90, 160, 0.32);
  --meter-attack: rgba(180, 50, 20, 0.32);
  --meter-dynamic: rgba(160, 100, 10, 0.40);
  --meter-access: rgba(20, 130, 70, 0.32);
  --meter-theory: rgba(110, 40, 160, 0.32);
  --ln-serif: 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, 'Times New Roman', serif;
  --ln-ui: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --ln-shadow-sm: 0 1px 3px rgba(60, 42, 12, 0.10);
  --ln-shadow-md: 0 8px 22px rgba(60, 42, 12, 0.16);
  --ln-search-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237a5830' stroke-width='2.4' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-4.2-4.2'/%3E%3C/svg%3E");
}

/* ============================================================
   GLOBAL COMPONENTS
   ============================================================ */

/* ---------- Buttons: pills, flat green primary ---------- */
.btn {
  border-radius: 999px;
  font-family: var(--ln-ui);
  font-weight: 600;
  padding: 8px 18px;
  transition: transform 0.14s ease, box-shadow 0.16s ease,
    background 0.16s ease, border-color 0.16s ease, color 0.16s ease;
}
.btn-primary {
  background: linear-gradient(180deg, var(--bg-rp-prep-segready), var(--ln-green));
  border: 1px solid var(--ln-green-deep);
  color: var(--ink-btn-primary);
  box-shadow: 0 1px 3px var(--shadow-btn-primary), inset 0 1px 0 var(--shadow-btn-primary-2);
}
.btn-primary:hover:not(:disabled) {
  background: linear-gradient(180deg, var(--ln-green), var(--ln-green-deep));
  border-color: var(--ln-green-deep);
  transform: translateY(-1px);
  box-shadow: 0 6px 16px var(--shadow-disabled-2), inset 0 1px 0 var(--shadow-disabled);
}
.btn-secondary {
  background: linear-gradient(180deg, var(--bg-btn-secondary-3), var(--ln-card2));
  border: 1px solid var(--ln-border);
  color: var(--ln-ink);
  box-shadow: var(--ln-shadow-sm), inset 0 1px 0 var(--shadow-btn-secondary);
}
.btn-secondary:hover:not(:disabled) {
  background: linear-gradient(180deg, var(--bg-disabled-3), var(--bg-hover-3));
  transform: translateY(-1px);
  box-shadow: 0 5px 14px var(--shadow-disabled-4), inset 0 1px 0 var(--shadow-disabled-3);
}
.btn-ghost {
  background: linear-gradient(180deg, var(--bg-btn-ghost), var(--bg-btn-ghost-2));
  border: 1px solid var(--line-btn-ghost);
  color: var(--ln-dim);
  box-shadow: 0 1px 2px var(--shadow-btn-ghost), inset 0 1px 0 var(--shadow-disabled-3);
}
/* Analyze magnifying-glass toggle: lit while a scratch board is active. */
#book-keep-btn.active {
  background: linear-gradient(180deg, var(--bg-active-move), var(--bg-rpc-movegreen));
  border-color: var(--ln-green);
  color: var(--ln-green-deep);
  box-shadow: inset 0 1px 3px var(--shadow-book-keep-btnactive);
}
.btn-ghost:hover:not(:disabled) {
  background: linear-gradient(180deg, var(--bg-hover-2), var(--bg-disabled));
  border-color: var(--ln-green);
  color: var(--ln-green-deep);
  transform: translateY(-1px);
  box-shadow: 0 5px 14px var(--shadow-disabled-5), inset 0 1px 0 var(--shadow-disabled-6);
}
.btn:active:not(:disabled) {
  transform: translateY(0);
  box-shadow: 0 1px 2px var(--shadow-disabled-7);
}
.btn:disabled { opacity: 0.38; }

/* ---------- Train: the end of a line ---------- */
/* Two equal tiles, so neither reads as an afterthought. What to do about THIS
   line goes here; leaving the line or ending the session goes in the footer
   under the hairline, which is bordered so it still reads as pressable. */
.ms-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.ms-tile {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; padding: 11px 8px; border-radius: 14px; cursor: pointer;
  font-family: var(--ln-ui); font-size: 0.83rem; font-weight: 700;
  line-height: 1.15; text-align: center;
  background: linear-gradient(180deg, var(--bg-ms-tile), var(--bg-ms-tile-2));
  border: 1px solid var(--line-ms-tile); color: var(--ln-dim);
  box-shadow: 0 2px 5px var(--shadow-ms-tile-2), inset 0 1px 0 var(--shadow-ms-tile);
  transition: transform 0.14s ease, box-shadow 0.16s ease, background 0.16s ease;
}
.ms-tile .ms-ico { font-size: 1.12rem; line-height: 1; }
.ms-tile.go {
  background: linear-gradient(180deg, var(--bg-rp-prep-segready), var(--ln-green));
  border-color: var(--ln-green-deep); color: var(--ink-btn-primary);
  box-shadow: 0 2px 8px var(--shadow-disabled-2), inset 0 1px 0 var(--shadow-disabled);
}
.ms-tile:hover { transform: translateY(-1px); box-shadow: 0 6px 15px var(--shadow-hover-5); }
.ms-tile:active { transform: translateY(0); }

.ms-foot {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 7px;
  margin-top: 11px; padding-top: 11px;
  border-top: 1px solid var(--line-ms-foot);
}
/* The same chips mid-line (Restart line · Skip · Finish). No hairline of its own
   up there — the progress bar already sits directly above it. */
#master-nav.ms-navrow {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 7px;
  width: 100%; max-width: var(--ms-bw); margin-top: 9px;
}
/* The Repertoire tab's line ends borrow this whole treatment — the two tiles
   and the chip footer — so a line end looks the same wherever you meet one.
   Written once, never copied: .ms-tiles / .ms-tile / .ms-foot are the same
   classes in both tabs. */
#rep2-walk-nav:empty { display: none; }

/* "✓ LINE COVERED" IS THE CARD'S OWN HEADER (Ed, 2026-09-17, option 3 of
   docs/line-covered-mock.html, then C of docs/line-covered-back-left-mock.html).
   A green bar welded to the top of the panel, running edge to edge, so the
   marker travels with the card it heads instead of floating above the board.
   THE MARKER IS ALL IT HOLDS (Ed, 2026-09-18, option 1 of
   docs/line-covered-back-move-mock.html). A back button sat at its left edge,
   which is where a way OUT sits everywhere else, so it kept being tapped to
   leave. It is the .rp-alt-back bar under the chips now — see rpLineDoneBackHtml.
   The panel gives up its own padding for this, so .rp-lbody takes it instead.
   Written by rpLineDoneHtml, and shared by all three line ends, which have to
   match. */
.rp-builder-panel.has-lhdr { padding: 0; }
.rp-lhdr {
  display: flex; align-items: center; justify-content: center;
  min-height: 52px; padding: 11px 14px;
  background: linear-gradient(180deg, var(--bg-rp-linedone), var(--bg-disabled));
  border-bottom: 1px solid var(--line-rp-linedone);
  color: var(--ln-green-deep);
  font-family: var(--ln-serif); font-weight: 700; font-size: 1rem; line-height: 1.2;
}
.rp-lhdr-mark { display: inline-flex; align-items: center; gap: 9px; }
/* The panel's own padding, one level in. Both steps live HERE rather than beside
   the panel's rules in styles.css: this file loads second, so a phone rule over
   there loses to this one at the same specificity. Measured 16px on a 430px
   screen before the phone step was moved up. */
.rp-lbody { padding: 16px; }
@media (max-width: 760px) { .rp-lbody { padding: 14px 10px; } }
.rp-linedone-tick {
  flex: none; width: 23px; height: 23px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(180deg, var(--bg-rp-prep-segready), var(--ln-green));
  color: var(--ink-btn-primary); font-family: var(--ln-ui); font-size: 0.76rem; font-weight: 800;
  box-shadow: 0 1px 3px var(--shadow-btn-primary);
}

/* A Repertoire line end can carry four chips where Train's carries three, so
   its footer wraps to a second row instead of squeezing them to nothing. */
/* One tile and no second job: let it take the whole row rather than sit
   half-width with a hole beside it. Used by the Explore board's practise card
   in the one state that has nothing to offer on the right — every line here is
   covered, and none of them is drillable under your current answers. */
.ms-tiles.is-one { grid-template-columns: 1fr; }

.ms-foot.ms-foot-wrap { display: flex; flex-wrap: wrap; }
.ms-foot.ms-foot-wrap button { flex: 1 1 116px; }

/* Shorter buttons here than in Train (Ed, 2026-08-07). Train's line end has the
   screen to itself; this one sits under a board, a marker row, a trail and an
   eval bar, and the whole card has to fit without scrolling. Scoped to the
   card, so Train's own tiles and chips are untouched. */
.rp-walk-lineend .ms-tile { padding: 7px 8px; gap: 2px; }
.rp-walk-lineend .ms-tile .ms-ico { font-size: 0.95rem; }
/* The first-line beat: once per repertoire, above the tiles, in its own tinted
   block so it reads as a moment rather than another line of the card. */
.rp-firstline {
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 12px 13px 13px; margin-bottom: 10px;
  background: var(--ln-green-tint); border: 1px solid var(--line-rp-chipis-done);
  border-radius: 14px;
}
.rp-firstline-tick {
  width: 48px; height: 48px; border-radius: 50%; flex: none;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.45rem; color: var(--ink-rp-danger-btn); background: var(--ln-green);
  box-shadow: 0 6px 18px var(--shadow-header);
}
.rp-firstline-title { font-family: var(--ln-serif); font-size: 1.05rem; font-weight: 700; text-align: center; }
.rp-firstline-sub { font-size: 0.8rem; line-height: 1.5; color: var(--ln-dim); text-align: center; }
.rp-walk-lineend .ms-foot { margin-top: 9px; padding-top: 9px; }
.rp-walk-lineend .ms-foot button { padding: 7px 6px; font-size: 0.76rem; line-height: 1.25; }
.ms-foot button, #master-nav.ms-navrow button {
  padding: 10px 6px; border-radius: 10px; cursor: pointer;
  font-family: var(--ln-ui); font-size: 0.78rem; font-weight: 700;
  background: var(--bg-button-3);
  border: 1px solid var(--line-button); color: var(--ln-dim);
}
.ms-foot button:hover, #master-nav.ms-navrow button:hover {
  background: var(--bg-hover-15); color: var(--ln-ink);
}
.ms-foot:empty { display: none; }

/* ---------- Train: the Finish report ---------- */
/* Over a faded screen, closable, with the session's score and a list of the
   lines you got wrong that re-trains one on tap. */
.ms-scrim {
  position: fixed; inset: 0; z-index: 900;
  background: var(--bg-ms-scrim);
  display: flex; align-items: center; justify-content: center; padding: 16px;
  animation: msScrimIn 0.16s ease-out;
}
@keyframes msScrimIn { from { opacity: 0; } to { opacity: 1; } }
.ms-report {
  position: relative; width: 100%; max-width: 360px;
  max-height: calc(100vh - 40px); overflow-y: auto;
  background: linear-gradient(180deg, var(--bg-ms-report), var(--bg-ms-report-2));
  border: 1px solid var(--line-ms-report); border-radius: 20px;
  padding: 20px 16px 16px; text-align: center;
  box-shadow: 0 18px 44px var(--shadow-ms-report);
  animation: msReportIn 0.2s cubic-bezier(0.2, 0.9, 0.3, 1);
}
@keyframes msReportIn { from { opacity: 0; transform: translateY(10px) scale(0.97); } }
@media (prefers-reduced-motion: reduce) {
  .ms-scrim, .ms-report { animation: none; }
}
.ms-rx {
  position: absolute; top: 10px; right: 10px; width: 30px; height: 30px;
  border-radius: 50%; cursor: pointer;
  border: 1px solid var(--line-ms-rx); background: var(--bg-btn-ghost);
  color: var(--ln-dim); font-size: 1rem; font-weight: 700; line-height: 1;
  display: flex; align-items: center; justify-content: center;
}
.ms-rx:hover { background: var(--bg-hover-4); color: var(--ln-ink); }
.ms-report h3 {
  font-family: var(--ln-serif); font-size: 1.2rem; font-weight: 700;
  margin: 0 26px 2px; color: var(--ln-ink); text-wrap: balance;
}
.ms-rsub { font-size: 0.78rem; color: var(--ln-dim); margin-bottom: 15px; }
.ms-rgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 7px; margin-bottom: 16px; }
.ms-rstat {
  padding: 11px 4px; border-radius: 12px;
  background: var(--bg-ms-rstat); border: 1px solid var(--line-ms-rstat);
}
.ms-rstat b {
  display: block; font-family: var(--ln-serif); font-size: 1.5rem;
  line-height: 1.1; font-variant-numeric: tabular-nums;
}
.ms-rstat span {
  font-size: 0.66rem; font-weight: 800; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--ln-dim);
}
.ms-rstat.ok b { color: var(--ln-green-deep); }
.ms-rstat.sh b { color: var(--ln-amber-ink); }
.ms-rstat.fx b { color: var(--ln-dim); }

.ms-rlabel {
  font-size: 0.68rem; font-weight: 800; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--ln-dim); text-align: left; margin-bottom: 7px;
}
/* Scrolls inside itself, so a long session doesn't stretch the card. */
.ms-rlist {
  max-height: 168px; overflow-y: auto; -webkit-overflow-scrolling: touch;
  display: flex; flex-direction: column; gap: 5px;
  margin-bottom: 15px; padding-right: 2px; text-align: left;
}
.ms-rrow {
  display: flex; align-items: center; gap: 8px; width: 100%; cursor: pointer;
  padding: 9px 11px; border-radius: 10px; text-align: left;
  font-family: var(--ln-ui); font-size: 0.82rem; color: var(--ln-ink);
  background: var(--bg-ms-rrow); border: 1px solid var(--ln-border-soft);
}
.ms-rrow:hover { background: var(--bg-hover-4); }
.ms-rnm { flex: 1; min-width: 0; }
.ms-rnm em { font-style: normal; color: var(--ln-amber-ink); font-weight: 700; }
.ms-rgo { flex: none; font-size: 0.95rem; color: var(--ln-dim); }
.ms-rrow.done { background: var(--bg-ms-rrowdone); border-color: var(--line-ms-rrowdone); }
.ms-rrow.done .ms-rnm em { color: var(--ln-green-deep); }
.ms-rtag {
  flex: none; font-size: 0.64rem; font-weight: 800; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--ln-green-deep);
}

.ms-rbtns { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
.ms-rbtns.one { grid-template-columns: 1fr; }
.ms-rbtns button {
  padding: 12px 10px; border-radius: 999px; cursor: pointer;
  font-family: var(--ln-ui); font-size: 0.86rem; font-weight: 700;
  background: linear-gradient(180deg, var(--bg-button), var(--bg-button-2));
  border: 1px solid var(--line-rpc-leader); color: var(--ln-dim);
}
.ms-rbtns button.go {
  background: linear-gradient(180deg, var(--bg-rp-prep-segready), var(--ln-green));
  border-color: var(--ln-green-deep); color: var(--ink-btn-primary);
}

/* Danger pill: same tactile gradient/shadow/lift as the primary, in red. Shared by
   the page "Reset progress" button, the inline delete-confirm, and the dialog OK. */
.btn-danger, .rp-danger-btn, .ep-modal-danger {
  background: linear-gradient(180deg, var(--bg-ep-modal-danger), var(--bg-ep-modal-danger-2));
  border: 1px solid var(--line-ep-modal-danger);
  color: var(--ink-ep-modal-danger);
  box-shadow: 0 1px 3px var(--shadow-ep-modal-danger), inset 0 1px 0 var(--shadow-btn-primary-2);
}
.btn-danger:hover:not(:disabled), .rp-danger-btn:hover:not(:disabled), .ep-modal-danger:hover:not(:disabled) {
  background: linear-gradient(180deg, var(--bg-disabled-4), var(--bg-disabled-5));
  border-color: var(--line-ep-modal-danger);
  transform: translateY(-1px);
  box-shadow: 0 6px 16px var(--shadow-disabled-8), inset 0 1px 0 var(--shadow-disabled);
}

/* ---------- Button layout primitives (redesign) ---------- */
/* Center label + icon, so mixed emoji/text buttons line up cleanly. */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; line-height: 1; }

/* Compact icon-only button (steppers, tools). Square, no label. */
.btn-icon { flex: 0 0 auto; width: 44px; min-width: 44px; padding: 0; font-size: 1.15rem; }

/* Book study nav: all buttons a uniform 44px tall (like the app's other nav
   bars) so the icon buttons don't look shrunk next to Prev/Next. */
.book-board-col .study-nav .btn,
#fable-player .study-nav .btn { min-height: 44px; }

/* Walk & analysis nav: ONE short row so the notes box below stays visible.
   Icon buttons keep it small; the green primary is the only labelled one. */
#rep2-walk-nav { align-items: center; }
.rp-nav-bar { display: flex; flex-wrap: nowrap; justify-content: center; align-items: stretch; gap: 6px; width: 100%; }
.rp-nav-bar > .btn { min-height: 44px; }
.rp-nav-next { flex: 0 1 auto; padding: 8px 18px; white-space: nowrap; }
.rp-nav-finish { flex: 0 1 auto; padding: 8px 14px; white-space: nowrap; }

/* Inline key-cap: shows a nav button's glyph inside instruction copy. */
.rp-key { display: inline-block; padding: 0 0.36em; border: 1px solid var(--ln-border);
  border-radius: 6px; color: var(--ln-dim); font-weight: 700; font-family: var(--ln-ui);
  line-height: 1.35; }
@media (max-width: 480px) {
  .rp-nav-bar { gap: 5px; }
  .rp-nav-bar > .btn-icon { width: 40px; min-width: 40px; font-size: 1.05rem; }
  .rp-nav-next { padding: 8px 14px; }
}

/* ---------- In-app dialogs (custom alert / confirm / prompt) ---------- */
/* Replaces the browser's native alert()/confirm()/prompt() so every popup wears
   the app's own skin instead of the grey iOS/system sheet. Driven by epConfirm /
   epAlert / epPrompt in app.js. */
#ep-modal-root { position: fixed; inset: 0; z-index: 100000; pointer-events: none; }
.ep-modal-overlay {
  position: fixed; inset: 0; display: flex; align-items: center; justify-content: center;
  padding: 24px; z-index: 100000; pointer-events: auto;
  background: var(--bg-ep-modal-overlay); -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  opacity: 0; transition: opacity 0.15s ease;
}
.ep-modal-overlay.ep-open { opacity: 1; }
.ep-modal-overlay.ep-closing { opacity: 0; }
.ep-modal {
  width: 100%; max-width: 340px; text-align: left;
  background: var(--surface); color: var(--text);
  border: 2px solid var(--accent2); border-radius: 16px; padding: 20px 20px 16px;
  box-shadow: 0 18px 50px var(--shadow-ep-modal);
  transform: translateY(10px) scale(0.98); transition: transform 0.16s ease;
}
.ep-modal-overlay.ep-open .ep-modal { transform: none; }
.ep-modal-title { font-family: Georgia, serif; font-weight: 700; font-size: 1.15rem; color: var(--accent2); margin-bottom: 8px; }
.ep-modal-msg { font-size: 0.9rem; line-height: 1.5; color: var(--text); }
.ep-modal-input {
  width: 100%; box-sizing: border-box; margin-top: 14px; padding: 10px 12px;
  border: 1px solid var(--accent2); border-radius: 8px; background: var(--bg);
  color: var(--text); font-size: 1rem; font-family: inherit;
}
.ep-modal-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px var(--shadow-focus); }
.ep-modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; }
.ep-modal-btn { min-width: 84px; padding: 9px 18px; cursor: pointer; }

/* ---------- Shared top banner (Book line, Repertoire, Master, Learn) ---------- */
.rep-info-banner {
  background: var(--ln-card);
  border: 1px solid var(--ln-border-soft);
  border-radius: 18px;
  box-shadow: var(--ln-shadow-sm);
}
/* No banner at all: the skeleton walk drops the card, the title and the subtitle
   so the board sits at the top of the page, leaving just the back pill above it.
   Two classes deep so it beats every plain .rep-info-banner rule whatever its
   place in the cascade (styles.css sets the panel, and a mobile media query in
   this file re-sets the radius). Opted into per screen — see rpBuilderBannerBare. */
.rep-info-banner.rep-info-banner-bare {
  background: none;
  border: none;
  box-shadow: none;
  min-height: 0;
  padding: 0;
}
.rep-info-banner.rep-info-banner-bare .rep-banner-center { display: none; }
.rep-practice-back {
  background: transparent;
  border: 1px solid var(--ln-border);
  color: var(--ln-dim);
  border-radius: 999px;
  padding: 7px 15px;
  font-weight: 600;
  font-size: 0.8rem;
  transition: border-color 0.15s, color 0.15s;
}
.rep-practice-back:hover {
  background: transparent;
  border-color: var(--ln-green);
  color: var(--ln-green-deep);
}
.rep-practice-back.stacked {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  line-height: 1.05;
  padding: 4px 14px;
}
.rep-practice-back.stacked .rp-back-word { font-size: 0.68rem; }
.rep-practice-back.stacked .rp-back-arrow { font-size: 0.95rem; }
.rep-practice-bar-title {
  color: var(--ln-ink);
  font-family: var(--ln-serif);
  font-size: 1rem;
}
.rep-practice-line-name { color: var(--ln-dim); }

/* ---------- Move history panel ---------- */
.rep-move-history-header { border-radius: 12px 12px 0 0; }
.rep-move-history { border-radius: 0 0 12px 12px; }

/* ============================================================
   LEARN TAB
   ============================================================ */
.fable-search-wrap { position: relative; max-width: 560px; margin-bottom: 34px; }
.fable-search-wrap::before {
  content: '';
  position: absolute; left: 17px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; pointer-events: none;
  background: var(--ln-search-icon) no-repeat center / contain;
  opacity: 0.75;
}
.fable-search {
  border-radius: 999px;
  padding: 12px 20px 12px 43px;
  background: var(--ln-card);
  border: 1px solid var(--ln-border);
  box-shadow: var(--ln-shadow-sm);
  color: var(--ln-ink);
  transition: border-color 0.15s, box-shadow 0.15s;
}
.fable-search::placeholder { color: var(--ln-faint); }
.fable-search:focus {
  border-color: var(--ln-green);
  box-shadow: 0 0 0 3px var(--ln-green-tint);
}

.fable-section { margin-bottom: 38px; }
.fable-section-title {
  font-family: var(--ln-serif);
  font-size: 1.3rem;
  color: var(--ln-ink);
  border-bottom: none;
  padding-bottom: 0;
}
.fable-section-blurb {
  font-size: 0.85rem;
  color: var(--ln-dim);
  margin: 3px 0 14px;
}
.fable-grid {
  gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
.fable-card {
  background: var(--ln-card);
  border: 1px solid var(--ln-border-soft);
  border-radius: 16px;
  padding: 0;
  overflow: hidden;
  box-shadow: var(--ln-shadow-sm);
  /* Animate only transform + border (both composited/cheap). box-shadow is left
     out of the transition so it snaps on hover instead of forcing a repaint
     every frame over the image-heavy card. */
  transition: transform 0.15s ease, border-color 0.15s ease;
}
.fable-card-body {
  display: flex; flex-direction: column; gap: 5px; flex: 1;
  padding: 12px 16px 14px;
}
.fable-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--ln-shadow-md);
  border-color: var(--ln-green);
  background: var(--ln-card);
}
.fable-card-name {
  font-family: var(--ln-serif);
  font-size: 1.1rem;
  color: var(--ln-ink);
}
.fable-card-sub { color: var(--ln-dim); font-size: 0.8rem; line-height: 1.45; }
.fable-card-meta {
  opacity: 1;
  margin-top: 6px;
  display: flex; flex-wrap: wrap; gap: 6px;
}
.fable-chip {
  font-family: var(--ln-ui);
  font-size: 0.67rem;
  font-weight: 600;
  color: var(--ln-dim);
  background: var(--bg-rpc-move);
  border: 1px solid var(--ln-border-soft);
  border-radius: 999px;
  padding: 2px 9px;
  white-space: nowrap;
}
.fable-chip-story {
  color: var(--ln-green-deep);
  background: var(--ln-green-tint);
  border-color: transparent;
}

.fable-ch-step { color: var(--ln-dim); border-radius: 999px; }
.fable-ch-step:hover:not(:disabled) {
  background: var(--ln-green-tint);
  color: var(--ln-green-deep);
}
.fable-ch-current { color: var(--ln-ink); border-radius: 10px; }
.fable-ch-current:hover { background: var(--bg-hover-16); }
.fable-ch-eyebrow { color: var(--ln-faint); opacity: 1; letter-spacing: 1.3px; }
.fb-head .fable-chapter-name {
  font-family: var(--ln-serif);
  color: var(--ln-ink);
}
.fb-hair { background: var(--bg-fable-ch-progress-2); }
.fb-hair span { background: var(--ln-green); }

.fable-ch-menu {
  background: var(--ln-card);
  border: 1px solid var(--ln-border);
  border-radius: 16px;
  box-shadow: 0 18px 44px var(--shadow-fable-ch-menu-2);
  padding: 8px;
}
.fable-ch-item { border-radius: 10px; color: var(--ln-ink); }
.fable-ch-item:hover { background: var(--ln-card2); }
.fable-ch-item.current {
  background: var(--ln-green-tint2);
  color: var(--ln-green-deep);
}
.fable-ch-item.current .fable-ch-num { opacity: 0.7; }
.fable-ch-title { font-family: var(--ln-serif); }

/* On a laptop the chapter list is a rail inside the lesson card, not a floating
   dropdown, so it drops the card treatment it wears on a phone. Here and not in
   styles.css because this file loads second and would otherwise win. */
@media (min-width: 761px) {
  .fable-ch-menu {
    background: transparent; border: 0; border-right: 1px solid var(--ln-border-soft);
    border-radius: 0; box-shadow: none; padding: 16px 8px;
  }
  /* On the whole middle column, not just the board slot: the board no longer
     fills the column, so a border on the slot alone would stop short. */
  .fb-mid { border-right: 1px solid var(--ln-border-soft); }
}

.fb-page {
  background: var(--ln-card);
  border: 1px solid var(--ln-border-soft);
  border-radius: 18px;
  box-shadow: var(--ln-shadow-sm);
}
.fb-foot { border-top: 1px solid var(--ln-border-soft); }
.fable-story-body { padding: 18px 20px 16px; }
.fable-step-text {
  font-family: var(--ln-serif);
  font-size: 1.02rem;
  line-height: 1.7;
  color: var(--ln-ink);
}
.fable-analysis-note { color: var(--ln-dim); }
.fable-challenge-prompt {
  background: var(--ln-green-tint);
  color: var(--ln-green-deep);
  border-radius: 12px;
  padding: 11px 14px;
  font-family: var(--ln-ui);
  font-size: 0.88rem;
}
.fable-challenge-result.ok {
  background: var(--ln-green-tint);
  color: var(--ln-green-deep);
  border-radius: 12px;
  padding: 11px 14px;
}
.fable-challenge-result.shown { color: var(--ln-dim); }
.fable-hint {
  background: var(--ln-amber-tint);
  color: var(--ln-amber-ink);
  border-radius: 12px;
  padding: 11px 14px;
}
.fable-arrow-list { border-top: 1px solid var(--ln-border-soft); }
.fable-arrow-item { color: var(--ln-ink); }

#fable-analysis-btn.active {
  background: var(--ln-green);
  border-color: var(--ln-green);
  color: var(--ink-btn-primary);
}
.fable-movelist {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.fable-mv-num { color: var(--ln-faint); }
.fable-mv.current { background: var(--ln-green); border-radius: 4px; }

/* ============================================================
   BOOK TAB
   ============================================================ */
.book-welcome h2 { font-family: var(--ln-serif); color: var(--ln-ink); }
.book-opening-title { font-family: var(--ln-serif); color: var(--ln-ink); }
/* The lesson is the tab's headline offer, so it sits above the intro prose and
   reads as a card you enter, not a button you press. Whole thing is the target. */
.book-lesson-card {
  display: flex; align-items: center; gap: 14px;
  width: 100%; margin: 16px 0 20px; padding: 14px 16px;
  text-align: left; cursor: pointer;
  background: linear-gradient(135deg, var(--bg-rpc-movegreen) 0%, var(--bg-rp-deep-primary) 100%);
  border: 1px solid var(--line-rp-linedone);
  border-left: 4px solid var(--ln-green);
  border-radius: 14px;
  box-shadow: var(--ln-shadow-sm);
  transition: box-shadow 0.14s ease, transform 0.14s ease, border-color 0.14s ease;
}
.book-lesson-card:hover {
  transform: translateY(-1px);
  box-shadow: var(--ln-shadow-md);
  border-color: var(--ln-green);
}
.book-lesson-card:active { transform: translateY(0); box-shadow: var(--ln-shadow-sm); }
.blc-badge {
  flex: none; display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 12px;
  background: var(--ln-green-tint2); font-size: 1.35rem; line-height: 1;
}
.blc-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.blc-eyebrow {
  font-family: var(--ln-ui); font-size: 0.64rem; font-weight: 800;
  letter-spacing: 0.09em; text-transform: uppercase; color: var(--ln-green-deep);
}
.blc-title {
  font-family: var(--ln-serif); font-size: 1.06rem; font-weight: 700;
  color: var(--ln-ink); line-height: 1.25;
}
.blc-sub { font-size: 0.84rem; color: var(--ln-dim); line-height: 1.4; }
.blc-meta {
  font-family: var(--ln-ui); font-size: 0.72rem; font-weight: 600;
  color: var(--ln-faint); margin-top: 3px;
}
.blc-go { flex: none; font-size: 1.3rem; color: var(--ln-green); font-weight: 700; }
.book-intro-text {
  background: var(--ln-card);
  border: 1px solid var(--ln-border-soft);
  border-left: 3px solid var(--ln-green);
  border-radius: 12px;
  box-shadow: var(--ln-shadow-sm);
}
.book-section-divider { font-family: var(--ln-serif); font-size: 1.05rem; }
.book-tag { border-radius: 999px; }

/* At-a-glance style meters on the opening page */
.book-meters {
  display: flex; flex-direction: column; gap: 11px;
  max-width: 430px; margin: 2px 0 22px;
}
.book-meters-title {
  font-family: var(--ln-serif); font-size: 0.8rem; color: var(--ln-dim);
  text-transform: uppercase; letter-spacing: 0.6px; margin-bottom: 2px;
}
.book-meter {
  display: grid; grid-template-columns: 76px 1fr 76px;
  align-items: center; gap: 10px;
}
.book-meter-l, .book-meter-r { font-size: 0.75rem; font-weight: 600; color: var(--ln-dim); }
.book-meter-l { text-align: right; }
.book-meter-track {
  position: relative; height: 6px; border-radius: 999px;
  border: 1px solid var(--ln-border-soft);
}
.book-meter-track.m-solid-attack { background: linear-gradient(90deg, var(--meter-solid), var(--meter-attack)); }
.book-meter-track.m-solid-dynamic { background: linear-gradient(90deg, var(--meter-solid), var(--meter-dynamic)); }
.book-meter-track.m-access-theory { background: linear-gradient(90deg, var(--meter-access), var(--meter-theory)); }
.book-meter-dot {
  position: absolute; top: 50%; width: 14px; height: 14px; border-radius: 50%;
  background: var(--ln-ink); border: 2px solid var(--ln-card);
  box-shadow: 0 1px 3px var(--shadow-master-feedback); transform: translate(-50%, -50%);
}

/* Search results (openings / variations / lines) on the Book home */
.book-results { max-width: 720px; margin: 0 auto; }
.book-results-group { margin-bottom: 22px; }
.book-results-label {
  font-family: var(--ln-serif); font-size: 0.95rem; color: var(--ln-ink);
  opacity: 0.75; margin: 0 4px 8px;
}
.book-result {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  width: 100%; text-align: left; font: inherit; color: inherit; cursor: pointer;
  background: var(--ln-card); border: 1px solid var(--ln-border-soft);
  border-radius: 12px; box-shadow: var(--ln-shadow-sm);
  padding: 10px 14px; margin-bottom: 8px;
  transition: transform 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
}
.book-result:hover { transform: translateY(-1px); border-color: var(--ln-green); box-shadow: var(--ln-shadow-md); }
.book-result-name { font-family: var(--ln-serif); font-size: 0.98rem; color: var(--ln-ink); }
.book-result-path { font-size: 0.8rem; color: var(--ln-dim); }
.book-hl { background: var(--bg-book-hl); color: inherit; border-radius: 3px; padding: 0 1px; }
.book-results-more { font-size: 0.8rem; color: var(--ln-dim); margin: 2px 6px 0; }

/* Browse-by-style filter chips, shared by the Book and Learn homes
   (colours come from btag-*) */
.book-filter-bar {
  display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
  align-items: center; max-width: 760px; margin: 0 auto 24px;
}
.book-filter-chip {
  display: inline-block; padding: 5px 13px; border-radius: 999px;
  font-size: 0.74rem; font-weight: 700; letter-spacing: 0.2px;
  cursor: pointer; opacity: 0.68;
  transition: opacity 0.12s ease, box-shadow 0.12s ease, transform 0.12s ease;
}
.book-filter-chip:hover { opacity: 1; transform: translateY(-1px); }
.book-filter-chip.active { opacity: 1; box-shadow: 0 0 0 2px var(--ln-green); }
/* Extra tags are hidden until More is tapped — but an active one always stays
   visible so an applied filter is never hidden behind the collapse. */
.book-filter-extra { display: none; }
.book-filter-bar.expanded .book-filter-extra,
.book-filter-chip.book-filter-extra.active { display: inline-block; }
.book-filter-more,
.book-filter-clear {
  padding: 5px 13px; border-radius: 999px; font-size: 0.74rem; font-weight: 600;
  background: none; border: 1px solid var(--ln-border-soft);
  cursor: pointer; transition: color 0.12s ease, border-color 0.12s ease;
}
.book-filter-more { color: var(--ln-green); border-style: dashed; }
.book-filter-more:hover { border-color: var(--ln-green); }
.book-filter-clear { color: var(--ln-dim); }
.book-filter-clear:hover { color: var(--ln-ink); border-color: var(--ln-dim); }
.bfm-caret { font-size: 0.6rem; }

/* Phone: one row of chips, not three (Ed, 2026-08-07). The 11 everyday tags wrap
   onto 3 rows and cost 94px above the openings. Keep the first four and let the
   SAME More toggle that reveals the extra tags reveal these too, so there is one
   control rather than two. An active chip always stays visible, matching the
   rule for the extras above, or an applied filter could hide behind the collapse. */
@media (max-width: 760px) {
  .book-filter-bar:not(.expanded) .book-filter-chip:nth-child(n+5):not(.active) { display: none; }
}
.book-var-item {
  border: 1px solid var(--ln-border-soft);
  border-radius: 14px;
  box-shadow: var(--ln-shadow-sm);
}
.book-var-lines { border-top-color: var(--ln-border-soft); }
.book-var-card-name { font-family: var(--ln-serif); }
.book-var-count { border-radius: 999px; }
.book-line-card {
  border: 1px solid var(--ln-border-soft);
  border-radius: 14px;
  box-shadow: var(--ln-shadow-sm);
}
.book-line-card-click { transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease; }
.book-line-card-click:hover {
  transform: translateY(-2px);
  border-color: var(--ln-green);
  box-shadow: var(--ln-shadow-md);
}
.book-line-card-name { font-family: var(--ln-serif); font-size: 0.92rem; }
.book-movelist { border-radius: 12px; border-color: var(--ln-border-soft); box-shadow: var(--ln-shadow-sm); }
.book-back-btn { border-radius: 999px; padding: 6px 14px; }
.bk-arrow-label { color: var(--ln-dim); }

/* First-class Study Hall header for the line study view (replaces the borrowed
   Repertoire practice banner). */
.book-study-header {
  width: 100%; box-sizing: border-box;
  display: flex; align-items: center; gap: 12px;
  padding: 11px 16px; min-height: 70px;
  background: var(--ln-card); border: 1px solid var(--ln-border-soft);
  border-radius: 16px; box-shadow: var(--ln-shadow-sm);
}
#book-back-btn { flex: 0 0 auto; white-space: nowrap; }
.book-study-titles { flex: 1; min-width: 0; text-align: center; }
.book-study-title { font-family: var(--ln-serif); font-size: 1.12rem; font-weight: 700; color: var(--ln-ink); line-height: 1.25; }
.book-study-sub { font-size: 0.82rem; color: var(--ln-dim); margin-top: 2px; }
/* The line study's trail sits above the header card, left-aligned, the way the
   lesson player's does (Ed, 2026-08-04). It used to be centred inside the card
   under the title. */
.book-line-crumbs { margin: 2px 0 10px; }
.book-study-spacer { flex-shrink: 0; width: 64px; }

/* Breadcrumb trail (Opening › Variation › Line) */
.book-crumbs {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 4px 6px; margin-bottom: 16px;
  font-size: 0.82rem; line-height: 1.25;
}
.book-crumbs-center { justify-content: center; margin: 3px 0 0; }
/* Inside the lesson player the trail sits above the banner, so it needs a
   little breathing room but less bottom margin than a page-top crumb. */
.fable-crumbs { margin: 2px 0 10px; }
.book-crumb-link {
  background: none; border: none; padding: 0; cursor: pointer;
  font: inherit; color: var(--ln-green); border-radius: 4px;
}
.book-crumb-link:hover { text-decoration: underline; }
.book-crumb-current { color: var(--ln-dim); font-weight: 600; }
.book-crumb-sep { color: var(--ln-dim); opacity: 0.55; }

/* ============================================================
   REPERTOIRE TAB
   ============================================================ */
/* The continue card: one door at the top of the openings home. Same width as
   the grid below it (and the same lift at 1100px, see the laptop block), so the
   top of the page reads as one column. The whole card is the tap target. */
.rp-contcard {
  display: flex; align-items: center; gap: 12px; width: 100%;
  max-width: 760px; margin: 0 auto 18px; padding: 11px 13px;
  font: inherit; text-align: left; cursor: pointer;
  background: var(--ln-card); border: 1px solid var(--ln-green);
  border-radius: 18px; box-shadow: 0 0 0 1px var(--ln-green), var(--ln-shadow-sm);
}
.rp-contcard-board { flex: 0 0 92px; width: 92px; border-radius: 8px; overflow: hidden; }
.rp-contcard-board img, .rp-contcard-board svg { display: block; width: 100%; height: auto; }
.rp-contcard-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 10px; }
.rp-contcard-name {
  font-family: var(--ln-serif); font-size: 1.08rem; font-weight: 700;
  color: var(--ln-ink); line-height: 1.25; text-align: center;
}
.rp-contcard-go {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  font-size: 0.85rem; font-weight: 700; color: var(--ink-btn-primary);
  background: linear-gradient(180deg, var(--bg-rp-prep-segready), var(--ln-green));
  border: 1px solid var(--ln-green-deep); border-radius: 999px; padding: 9px 16px;
  box-shadow: 0 1px 3px var(--shadow-btn-primary), inset 0 1px 0 var(--shadow-btn-primary-2);
}
.rp-contcard:hover .rp-contcard-go {
  background: linear-gradient(180deg, var(--ln-green), var(--ln-green-deep));
}

.rp-open-search, .rp-create-input {
  border-radius: 999px;
  border: 1px solid var(--ln-border);
  background: var(--ln-card);
  box-shadow: var(--ln-shadow-sm);
}
.rp-open-search {
  padding: 11px 18px 11px 43px;
  background: var(--ln-card) var(--ln-search-icon) no-repeat 17px center / 16px;
}
.rp-open-search:focus, .rp-create-input:focus {
  outline: none;
  border-color: var(--ln-green);
  box-shadow: 0 0 0 3px var(--ln-green-tint);
}
.rp-current-rep, .rp-opening-name, .rp-chapter-name, .rp-cov-head,
.rp-open-tile-name, .rp-walk-lineend-title {
  font-family: var(--ln-serif);
}
.rp-color-pick-card, .rp-chapter-card, .rp-prep, .rp-opening-card, .rp-cov {
  border: 1px solid var(--ln-border-soft);
  border-radius: 16px;
  box-shadow: var(--ln-shadow-sm);
  background: var(--ln-card);
}
.rp-color-pick-card:hover { border-color: var(--ln-green); box-shadow: var(--ln-shadow-md); }

/* ---------- "Which side?" color pick — split board panels ---------- */
.rp-color-hero { margin-bottom: 20px; }
.rp-side-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  max-width: 760px;
  margin: 0 auto;
}
.rp-side-panel {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 4px;
  padding: 30px 22px 26px;
  border-radius: 22px;
  cursor: pointer;
  font-family: var(--ln-ui);
  border: 1px solid var(--ln-border-soft);
  box-shadow: var(--ln-shadow-sm);
  transition: transform 0.18s ease, box-shadow 0.2s ease, border-color 0.2s ease;
  -webkit-tap-highlight-color: transparent;
}
.rp-side-panel:hover, .rp-side-panel:focus-visible {
  transform: translateY(-4px);
  box-shadow: var(--ln-shadow-md);
  outline: none;
}
.rp-side-panel:active { transform: translateY(-1px); }
/* Soft top sheen + a faint inner frame, so each panel reads like a real board
   edge. Both are pseudo-elements (independent of the hover box-shadow) and sit
   behind the pawn, so the piece and title stay crisp. Tuned per side below. */
.rp-side-panel::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 42%;
  background: linear-gradient(to bottom, var(--rp-sheen, rgba(255,255,255,0.18)), transparent);
  pointer-events: none; z-index: 0;
}
.rp-side-panel::after {
  content: ""; position: absolute; inset: 5px; border-radius: 17px;
  border: 1px solid var(--rp-frame, rgba(0,0,0,0.08));
  pointer-events: none; z-index: 2;
}

/* Checkerboard plinth glowing up from the bottom of each panel */
.rp-side-plinth {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 46%;
  background-image: repeating-conic-gradient(var(--rp-check) 0 25%, transparent 0 50%);
  background-size: 34px 34px;
  background-position: center bottom;
  -webkit-mask-image: linear-gradient(to top, #000 0%, rgba(0,0,0,0.35) 55%, transparent 100%);
          mask-image: linear-gradient(to top, #000 0%, rgba(0,0,0,0.35) 55%, transparent 100%);
  pointer-events: none;
}
/* The pawn, presented as if held out in an open hand */
.rp-side-pawn {
  position: relative;
  width: 104px; height: 104px;
  display: flex; align-items: center; justify-content: center;
  transition: transform 0.2s ease;
}
.rp-side-pawn::before {
  content: "";
  position: absolute; top: 50%; left: 50%;
  width: 118px; height: 118px;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle, var(--rp-glow) 0%, transparent 68%);
  pointer-events: none;
}
.rp-side-pawn::after {
  content: "";
  position: absolute; bottom: 6px; left: 50%;
  width: 56px; height: 12px;
  transform: translateX(-50%);
  background: radial-gradient(ellipse, var(--bg-after-6), transparent 70%);
  border-radius: 50%;
}
.rp-side-pawn img {
  width: 88px; height: 88px;
  position: relative; z-index: 1;
  filter: drop-shadow(0 5px 7px var(--shadow-bottom-nav));
}
.rp-side-panel:hover .rp-side-pawn,
.rp-side-panel:focus-visible .rp-side-pawn { transform: translateY(-4px) scale(1.05); }
.rp-side-title {
  position: relative;
  font-family: var(--ln-serif);
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: 0.4px;
}

/* White side: light board */
.rp-side-white {
  --rp-check: rgba(106, 64, 24, 0.13);
  --rp-glow: rgba(255, 252, 240, 0.85);
  --rp-sheen: rgba(255, 255, 255, 0.42);
  --rp-frame: rgba(106, 64, 24, 0.16);
  background: linear-gradient(165deg, var(--bg-rp-side-white) 0%, var(--bg-rp-side-white-2) 100%);
  color: var(--ink-rp-side-white);
  border-color: var(--line-btn-ghost);
}
.rp-side-white:hover, .rp-side-white:focus-visible { border-color: var(--ln-green); }

/* Black side: the site's own header brown, so the pawn stays readable */
.rp-side-black {
  --rp-check: rgba(255, 248, 232, 0.10);
  --rp-glow: rgba(255, 236, 200, 0.30);
  --rp-sheen: rgba(255, 244, 224, 0.13);
  --rp-frame: rgba(255, 248, 232, 0.14);
  background: linear-gradient(165deg, var(--bg-header) 0%, var(--bg-rp-side-black) 100%);
  color: var(--ink-rp-side-black);
  border-color: var(--line-rp-side-black);
}
.rp-side-black:hover, .rp-side-black:focus-visible { border-color: var(--line-focus-visible); }
/* Give the black pawn a thin, smooth light rim so it reads like the white pawn's
   refined dark outline (1px offsets with a touch of blur keep it crisp but not blocky) */
.rp-side-black .rp-side-pawn img {
  filter:
    drop-shadow(1px 0 0.4px var(--shadow-img))  drop-shadow(-1px 0 0.4px var(--shadow-img))
    drop-shadow(0 1px 0.4px var(--shadow-img))  drop-shadow(0 -1px 0.4px var(--shadow-img))
    drop-shadow(0.7px 0.7px 0.4px var(--shadow-img)) drop-shadow(-0.7px -0.7px 0.4px var(--shadow-img))
    drop-shadow(0.7px -0.7px 0.4px var(--shadow-img)) drop-shadow(-0.7px 0.7px 0.4px var(--shadow-img))
    drop-shadow(0 5px 7px var(--shadow-bottom-nav));
}

@media (max-width: 760px) {
  .rp-side-grid { grid-template-columns: 1fr; gap: 14px; max-width: 420px; }
  .rp-side-panel { flex-direction: row; align-items: center; text-align: left; padding: 18px 22px; gap: 18px; }
  .rp-side-plinth { width: 46%; height: 100%; left: 0; right: auto; top: 0;
    -webkit-mask-image: linear-gradient(to right, #000 0%, rgba(0,0,0,0.3) 55%, transparent 100%);
            mask-image: linear-gradient(to right, #000 0%, rgba(0,0,0,0.3) 55%, transparent 100%); }
  .rp-side-pawn { width: 66px; height: 66px; flex: 0 0 auto; }
  .rp-side-pawn img { width: 58px; height: 58px; }
  .rp-side-pawn::before { width: 78px; height: 78px; }
  .rp-side-pawn::after { display: none; }
  .rp-side-title { font-size: 1.3rem; }
}

/* ── Big SQUARE board-tiles for the White / Black choice. Shared by the
   Repertoire two-panel picker and the Train picker (whose third "Both" panel
   spans full width beneath, styled separately below). Higher specificity than
   the shared base + its mobile row layout, so squares hold on the phone too. ── */
.rp-side-grid .rp-side-white,
.rp-side-grid .rp-side-black {
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  aspect-ratio: 1 / 1;
  gap: clamp(6px, 1.8vw, 12px);
  padding: clamp(14px, 4vw, 30px);
}
.rp-side-grid .rp-side-white .rp-side-plinth,
.rp-side-grid .rp-side-black .rp-side-plinth {
  left: 0; right: 0; top: auto; bottom: 0;
  width: auto; height: 46%;
  -webkit-mask-image: linear-gradient(to top, #000 0%, rgba(0,0,0,0.35) 55%, transparent 100%);
          mask-image: linear-gradient(to top, #000 0%, rgba(0,0,0,0.35) 55%, transparent 100%);
}
.rp-side-grid .rp-side-white .rp-side-pawn,
.rp-side-grid .rp-side-black .rp-side-pawn {
  width: clamp(84px, 27vw, 132px); height: auto; aspect-ratio: 1 / 1;
}
.rp-side-grid .rp-side-white .rp-side-pawn img,
.rp-side-grid .rp-side-black .rp-side-pawn img { width: 84%; height: 84%; }
.rp-side-grid .rp-side-white .rp-side-pawn::before,
.rp-side-grid .rp-side-black .rp-side-pawn::before { width: 116%; height: 116%; }
.rp-side-grid .rp-side-white .rp-side-pawn::after,
.rp-side-grid .rp-side-black .rp-side-pawn::after { display: block; width: 52%; height: 11px; }
.rp-side-grid .rp-side-white .rp-side-title,
.rp-side-grid .rp-side-black .rp-side-title { font-size: clamp(1.15rem, 4.6vw, 1.55rem); }

/* Repertoire picker: exactly the two squares, side by side. */
.rp-side-grid:not(.ms-side-grid) {
  grid-template-columns: 1fr 1fr;
  gap: clamp(12px, 3.2vw, 22px);
  max-width: 640px;
}

/* Train "which side?" — the two squares (White / Black) on top, then a full-width
   "Both" panel beneath. */
.ms-side-grid {
  grid-template-columns: 1fr 1fr;
  gap: clamp(12px, 3.2vw, 22px);
  max-width: 640px;
}
/* The panels sit in the middle of the screen, not the top third (Ed,
   2026-08-11). The wrapper is given the height of the screen area and centres
   the grid inside it.
   NOT done by making #master-setup a flex column: master.js sets
   `style.display = 'block'` on that pane inline in three places (the arena /
   setup swap), and an inline style beats any stylesheet, so the rule matched and
   did nothing. Measured that way: :has() reported true while the computed
   display stayed `block`.
   The subtraction is the chrome around the wrapper, and it differs by width,
   which is why there are two: the header is 50px on a laptop and 37px on a
   phone, the nav is 54px, the pane's own padding is 28+60 on a laptop and 16+60
   on a phone, and the trail above takes ~25. The bottom inset is the iOS home
   indicator, which body already reserves. A first attempt subtracted a flat
   110px, forgot the padding and the trail, and made the phone page scroll by
   ~80px, and 217 left the laptop pane overflowing by a measured 6px. */
.ms-side-wrap {
  min-height: calc(100dvh - 223px);
  display: flex; align-items: center; justify-content: center;
}
@media (max-width: 760px) {
  .ms-side-wrap { min-height: calc(100dvh - 200px - env(safe-area-inset-bottom, 0px)); }
}
.ms-side-both {
  --rp-check: rgba(106, 64, 24, 0.13);
  --rp-glow: rgba(255, 250, 235, 0.7);
  background: linear-gradient(165deg, var(--bg-ms-side-both) 0%, var(--bg-ms-side-both-2) 100%);
  color: var(--ink-rp-side-white);
  border-color: var(--line-ms-side-both);
  grid-column: 1 / -1;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  aspect-ratio: auto;
  gap: clamp(12px, 3vw, 20px);
  padding: clamp(16px, 3.5vw, 22px);
}
.ms-side-both:hover, .ms-side-both:focus-visible { border-color: var(--ln-green); }
/* Let the paired pawns size to their content in the wide Both panel. */
.ms-side-both .rp-side-pawn { width: auto; height: auto; aspect-ratio: auto; }
.ms-side-both .rp-side-plinth {
  left: 0; right: 0; top: auto; bottom: 0; width: auto; height: 46%;
  -webkit-mask-image: linear-gradient(to top, #000 0%, rgba(0,0,0,0.35) 55%, transparent 100%);
          mask-image: linear-gradient(to top, #000 0%, rgba(0,0,0,0.35) 55%, transparent 100%);
}
.ms-both-pawns { display: flex; align-items: flex-end; justify-content: center; gap: 0; }
.ms-both-pawns img { width: clamp(48px, 13vw, 60px); height: clamp(48px, 13vw, 60px); }
.ms-both-pawns img:first-child { margin-right: -8px; z-index: 2; }
.ms-both-pawns img:last-child {
  filter:
    drop-shadow(1px 0 0.4px var(--shadow-img)) drop-shadow(-1px 0 0.4px var(--shadow-img))
    drop-shadow(0 1px 0.4px var(--shadow-img)) drop-shadow(0 -1px 0.4px var(--shadow-img))
    drop-shadow(0 5px 7px var(--shadow-bottom-nav));
}
.rp-open-tile {
  border: 1px solid var(--ln-border-soft);
  border-radius: 16px;
  box-shadow: var(--ln-shadow-sm);
}
.rp-open-tile-name { color: var(--ln-ink); }
.rp-open-grid { grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); }
.rp-open-tile:hover {
  transform: translateY(-3px);
  border-color: var(--ln-green);
  box-shadow: var(--ln-shadow-md);
}
.rp-open-tile.done { border-color: var(--ln-green); }
/* Held down while the opening's chapter order loads — the same acknowledgement
   the chapter card gets one level down, and the same rule about sitting after
   :hover so a cursor on the tile cannot leave it looking un-tapped. */
.rp-open-tile.is-opening {
  transform: translateY(1px) scale(0.985);
  border-color: var(--ln-green);
  box-shadow: inset 0 2px 6px var(--sh-rp-chcard-press, rgba(60, 42, 12, 0.22));
  opacity: 0.88;
}
.rp-ch-badge { border-radius: 999px; padding: 2px 10px; }
.rp-custom-badge { border-radius: 999px; }
.rp-type-badge { border-radius: 999px; }
.rp-builder-panel {
  border: 1px solid var(--ln-border-soft);
  border-radius: 18px;
  box-shadow: var(--ln-shadow-sm);
}
/* Nothing in it, nothing to see. Opening the setup walk as Black clears the
   panel while White's first move plays, and the empty card read as a stray bar
   under the board (Ed, 2026-08-04). Hidden rather than removed so the desktop
   row keeps its 400px second column and the board doesn't recentre. */
.rp-builder-panel:empty { visibility: hidden; }
.rp-question { font-family: var(--ln-serif); }
.rp-option {
  border: 1px solid var(--ln-border-soft);
  border-radius: 14px;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.rp-option:hover { border-color: var(--ln-green); }
.rp-option.selected { border-color: var(--ln-green); background: var(--ln-green-tint); }
.rp-option.current { box-shadow: 0 0 0 1px var(--ln-green); }
.rp-option-badge { border-radius: 999px; }
/* The answer chip is the way back to the choice screen, so it has to read as
   tappable without becoming a loud button. */
.rpc-answer-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  text-align: left;
  border: 1px solid transparent;
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.rpc-answer-btn:hover { border-color: var(--ln-green); }
.rpc-answer-change {
  margin-left: auto;
  font-size: 0.78em;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.6;
  flex: none;
}
.rpc-answer-btn:hover .rpc-answer-change { opacity: 1; }
/* The same row one level up: the opening's whole approach, behind one Change
   control. Sits under the hero's action tiles, so it needs a little more air above
   it than the chapter version, which follows a title. */
.rp-ophero-approach { margin-top: 10px; padding: 6px 12px; }
/* One tight line: label, the approach's name, Change hard right. Nothing may
   move to a second flex line — the name shrinks and wraps its own text inside
   its span instead, so Change stays put. Applied only with .is-named, because
   the move-chip fallback is white-space: nowrap and would overflow sideways
   rather than shrink. */
.rp-ophero-approach.is-named { flex-wrap: nowrap; gap: 10px; }
.rp-ophero-approach.is-named .rpc-answer-label { flex: none; }
.rp-ophero-approach.is-named .rpc-answer-title { flex: 1 1 auto; min-width: 0; line-height: 1.25; }
.rp-ophero-approach.is-named .rpc-answer-change { flex: none; margin-left: 0; align-self: center; }
/* Colour key tying a card to its arrow on the answer screen's board. */
.rp-option-dot {
  display: inline-block;
  width: 10px; height: 10px;
  border-radius: 50%;
  margin-right: 7px;
  flex: none;
  vertical-align: middle;
}
.rp-fork-card { border-radius: 12px; }
.rp-deep-mode { border-radius: 16px; }
.rp-walk-caption, .rp-walk-flavor { font-family: var(--ln-serif); }
/* Take it back, on its own centred row under the one line of text it undoes
   (Ed, 2026-08-08). It used to trail the end of a sentence, so it sat wherever
   the words happened to stop. */
.rp-walk-undo { display: flex; justify-content: center; margin-top: 12px; }

/* ============================================================
   MASTER TAB
   ============================================================ */
.master-hero-icon {
  width: 68px; height: 68px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 1.9rem;
  background: var(--ln-green-tint);
  border-radius: 50%;
  margin-bottom: 4px;
}
.master-hero h2 {
  font-family: var(--ln-serif);
  font-size: 2.25rem;
  letter-spacing: 0.5px;
  color: var(--ln-ink);
  margin: 6px 0 8px;
}
.master-hero p { color: var(--ln-dim); font-size: 0.95rem; }
.master-pick-label {
  font-family: var(--ln-serif);
  font-size: 1.3rem;
  color: var(--ln-ink);
  border-bottom: none;
  padding-bottom: 0;
  margin-bottom: 12px;
}

/* ── Train setup controls on the --ln-* system: section labels, the "change
   repertoire" row, the Customize CTA, the mode row, the scope segmented bar,
   the select-all check, and the selection tray. ── */
.master-pick-label { font-size: 1.15rem; font-weight: 700; letter-spacing: 0.2px; }
/* The Train home's header: a trail where the hero and the "change repertoire"
   pill used to be (Ed, 2026-08-04). Same 760px column they had. */
.ms-crumb-top { max-width: 760px; margin: 0 auto; }
.ms-crumb-top .book-crumbs { margin: 0 0 14px; }

/* Customize CTA: a tappable card, not a heavy bordered banner. */
.ms-cust-btn {
  border: 1px solid var(--ln-border-soft); border-radius: 14px;
  background: var(--ln-card); box-shadow: var(--ln-shadow-sm);
  color: var(--ln-ink); font-family: var(--ln-serif);
  transition: border-color 0.15s, box-shadow 0.15s, transform 0.12s;
}
.ms-cust-btn:hover { border-color: var(--ln-green); box-shadow: var(--ln-shadow-md); transform: translateY(-2px); }
.ms-cust-btn span { color: var(--ln-dim); }

/* Mode row: three choice cards. */
.ms-mode {
  border: 1px solid var(--ln-border-soft); border-radius: 14px;
  background: var(--ln-card); box-shadow: var(--ln-shadow-sm);
  color: var(--ln-ink); font-family: var(--ln-serif);
  transition: border-color 0.15s, box-shadow 0.15s, transform 0.12s;
}
.ms-mode:hover { border-color: var(--ln-green); box-shadow: var(--ln-shadow-md); transform: translateY(-2px); }
.ms-mode.selected { border-color: var(--ln-green); background: var(--ln-green-tint); box-shadow: 0 0 0 1px var(--ln-green); }
/* Mode description: a small info callout that changes with the selected mode. */
.ms-hint {
  max-width: 760px; margin: 12px auto 0;
  display: flex; align-items: flex-start; gap: 10px;
  padding: 11px 14px; border-radius: 12px;
  background: var(--ln-green-tint); border: 1px solid var(--line-rp-deep-primary);
  color: var(--ln-ink); font-size: 0.9rem; line-height: 1.45;
}
.ms-hint-ic { flex: 0 0 auto; font-size: 1.1rem; line-height: 1.3; }
.ms-hint-tx { min-width: 0; }

/* Select-all + scope segmented bar. */
.ms-selall { font-family: var(--ln-ui); color: var(--ln-ink); }
.ms-card-check { border-color: var(--ln-border-soft); background: var(--bg-btn-ghost); }
.ms-card-check.all, .ms-card-check.some { background: var(--ln-green); border-color: var(--ln-green); }
.ms-scope { border: 1px solid var(--ln-border-soft); border-radius: 999px; overflow: hidden; }
.ms-scopebtn {
  background: transparent; border-left: 1px solid var(--ln-border-soft);
  color: var(--ln-dim); font-family: var(--ln-ui); font-weight: 600;
  transition: background 0.14s, color 0.14s;
}
.ms-scopebtn:first-child { border-left: none; }
.ms-scopebtn:hover:not(.on) { color: var(--ln-ink); background: var(--ln-green-tint); }
.ms-scopebtn.on { background: var(--ln-green); color: var(--ink-btn-primary); }

/* Selection tray. */
.ms-startbar { background: var(--ln-card); border: 1px solid var(--ln-green); box-shadow: 0 4px 16px var(--shadow-ms-startbar-2); }
.ms-startbar-info { font-family: var(--ln-serif); color: var(--ln-ink); }
.ms-startbar-clear { text-decoration: none; }

/* Train's scorecard + result card, in the Study Hall skin. (The old
   .master-side-panel / .master-side-head / .master-stat rules went with the
   right-hand card the board used to sit beside.) */
#master-msg:not(:empty), .master-stage .rep-move-history {
  border: 1px solid var(--ln-border-soft);
  box-shadow: var(--ln-shadow-sm);
}
.master-stage .rep-move-history { border-radius: 14px; }
/* Boxed result card (punishment ends, session report, holding screens): the
   app card look, with a green accent bar like the repertoire/account cards.
   The bare clean/needs-work line-end (.ms-bare) keeps no box. */
#master-msg:not(:empty) { background: var(--ln-card); border-radius: 16px; }
#master-msg:not(:empty):not(.ms-bare) { position: relative; overflow: hidden; }
#master-msg:not(:empty):not(.ms-bare)::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px; background: var(--ln-green);
}
.master-result.ok .master-result-title { color: var(--ln-green-deep); }
.master-result.shaky .master-result-title { color: var(--ln-amber-ink); }
.master-msg-text { font-family: var(--ln-serif); }
.master-result-title, .master-result-note, .master-choice-label { font-family: var(--ln-serif); }
.master-choice { border-radius: 12px; }

/* ============================================================
   MINI BOARD PREVIEWS (opening cards everywhere)
   ============================================================ */
.mini-board {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  pointer-events: none;
  user-select: none;
}

/* Card layouts around the previews */
.fable-card-board { border-bottom: 1px solid var(--ln-border-soft); }
.rp-open-tile { overflow: hidden; }
.rp-open-tile-board {
  margin: -14px -16px 2px;
  border-bottom: 1px solid var(--ln-border-soft);
}
.master-card { overflow: hidden; }
.master-card-board {
  margin: -14px -16px 2px;
  border-bottom: 1px solid var(--ln-border-soft);
}
.master-card.selected .master-card-board, .rp-open-tile.done .rp-open-tile-board {
  border-color: var(--ln-green);
}

/* Book tab home grid (reuses the Learn card look) */
.book-home .fable-section { max-width: 1060px; margin-left: auto; margin-right: auto; }
.book-home-back { margin-bottom: 12px; }

/* ============================================================
   MOBILE (iPhone)
   ============================================================ */
@media (max-width: 760px) {
  /* Opening card sections become swipeable rows (chess.com style)
     instead of one long vertical list */
  .fable-grid {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 58%;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    padding: 2px 2px 10px;
    scrollbar-width: none;
  }
  .fable-grid::-webkit-scrollbar { display: none; }
  .fable-card { scroll-snap-align: start; }
  /* 46% -> 30%: three openings per screen instead of 2.3, with a sliver of the
     fourth showing so the swipe is still discoverable (Ed, 2026-08-07).
     The narrower tile leaves 90px of text width, which two names in the book do
     not fit on one word: Nimzowitsch and Scandinavian both overran by 18px.
     Fixed by shrinking the name rather than breaking it. Hyphenation was tried
     first and looked worse: "Nimzowitsch" broke with no hyphen at all, and
     hyphens:auto split words that already fitted ("My open-ing"). */
  .rp-open-tile-name { font-size: 0.86rem; }
  .rp-open-grid {
    grid-template-columns: none !important;
    grid-auto-flow: column;
    grid-auto-columns: 30%;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    padding: 2px 2px 10px;
    scrollbar-width: none;
  }
  .rp-open-grid::-webkit-scrollbar { display: none; }
  .rp-open-tile { scroll-snap-align: start; }
  .master-cards {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 58%;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    padding: 2px 2px 10px;
    scrollbar-width: none;
  }
  .master-cards::-webkit-scrollbar { display: none; }
  .master-card { scroll-snap-align: start; }
  /* Repertoire picker (stage 1) is just simple text cards — fit them all on
     screen instead of the swipe carousel used for the board-thumbnail pickers. */
  .master-cards.master-cards-reps {
    grid-auto-flow: row;
    grid-auto-columns: auto;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    overflow-x: visible;
    scroll-snap-type: none;
    gap: 12px;
  }

  .book-content { padding: 16px 14px 70px; }

  .master-hero-icon { width: 56px; height: 56px; font-size: 1.6rem; }
  .master-hero h2 { font-size: 1.75rem; }
  .master-hero p { font-size: 0.88rem; }
  .fable-search-wrap { margin-bottom: 26px; }
  .fable-section { margin-bottom: 30px; }
  .fable-card { border-radius: 14px; }
  .fable-card-body { padding: 10px 13px 12px; }
  .rep-info-banner { border-radius: 14px; }
  .fb-page, .rp-builder-panel { border-radius: 14px; }
  .fable-story-body { padding: 14px 16px 10px; }
}

/* ============================================================
   WALKTHROUGH — first-run tour (walkthrough.js)
   ============================================================ */
header h1 { cursor: pointer; user-select: none; -webkit-user-select: none; }

.wt-overlay {
  position: fixed; inset: 0; z-index: 90000;
  background: var(--bg-wt-overlay);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
  padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  padding-top: calc(16px + env(safe-area-inset-top, 0px));
}
.wt-card {
  position: relative;
  width: min(92vw, 420px);
  max-height: min(90vh, 720px);
  overflow-y: auto;
  background: var(--ln-card);
  border: 1px solid var(--ln-border);
  border-radius: 22px;
  box-shadow: var(--ln-shadow-md);
  padding: 22px 22px 18px;
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
}
.wt-skip {
  position: absolute; top: 10px; right: 12px;
  background: none; border: none; cursor: pointer;
  color: var(--ln-dim); font: 600 0.8rem var(--ln-ui);
  padding: 6px 8px;
}
.wt-skip:hover { color: var(--ln-ink); }
.wt-visual { width: min(60vw, 240px); margin: 10px 0 2px; }
.wt-board {
  position: relative; border-radius: 10px; overflow: hidden;
  border: 1px solid var(--line-after);
  box-shadow: var(--ln-shadow-md);
}
.wt-board .wt-arrow {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none;
}
.wt-chips { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-top: 12px; }
.wt-chip {
  background: linear-gradient(180deg, var(--bg-button), var(--bg-button-2));
  border: 1px solid var(--ln-border);
  border-radius: 999px;
  padding: 5px 13px;
  font: 600 0.8rem var(--ln-ui);
  color: var(--ln-ink);
  box-shadow: var(--ln-shadow-sm), inset 0 1px 0 var(--shadow-btn-secondary);
  pointer-events: none;
}
.wt-caption { margin-top: 9px; max-width: 340px; font: italic 400 0.78rem var(--ln-ui); color: var(--ln-dim); }
.wt-tag {
  margin-top: 14px;
  font: 700 0.7rem var(--ln-ui);
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ln-green-deep);
  background: var(--ln-green-tint);
  border: 1px solid var(--line-ms-rrowdone);
  border-radius: 999px;
  padding: 3px 12px;
}
.wt-title { font-family: var(--ln-serif); color: var(--ln-ink); font-size: 1.35rem; margin: 8px 0 6px; }
.wt-body { color: var(--ln-dim); font: 400 0.92rem/1.5 var(--ln-ui); margin: 0 0 14px; max-width: 340px; }
.wt-dots { display: flex; gap: 7px; margin-bottom: 14px; }
.wt-dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--bg-wt-dot);
  border: none; padding: 0; cursor: pointer;
}
.wt-dot.on { background: var(--ln-green); }
.wt-nav { display: flex; gap: 10px; width: 100%; justify-content: center; }
.wt-nav .btn { min-width: 110px; }

@media (max-width: 760px) {
  .wt-card { padding: 20px 16px 16px; }
  .wt-visual { width: min(56vw, 220px); }
  .wt-title { font-size: 1.25rem; }
}

/* ── Train bands ─────────────────────────────────────────────
   The one way into training, wherever you meet it: openings home, chapter
   card, review screen. Amber + target, deliberately NOT the green primary
   (in Repertoire green means "your next step here"), so a band reads as a
   door to the Train tab and actually landing there feels expected. */
.rp-train-band {
  display: flex; align-items: center; gap: 12px;
  margin-top: 12px; padding: 11px 14px;
  background: var(--ln-amber-tint);
  border: 1px solid var(--line-rp-train-band);
  border-radius: 12px; cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease,
              transform 0.12s ease, box-shadow 0.15s ease;
}
.rp-train-band:hover {
  background: var(--bg-hover-5);
  border-color: var(--line-hover);
  transform: translateY(-1px);
  box-shadow: var(--ln-shadow-sm);
}
.rp-train-band:active { transform: translateY(0); }
.rp-train-band:focus-visible { outline: 2px solid var(--ln-amber-ink); outline-offset: 2px; }
.rp-train-band-icon { flex: 0 0 auto; font-size: 1.25rem; line-height: 1; }
.rp-train-band-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.rp-train-band-text b {
  font-family: var(--ln-serif); font-size: 0.98rem; font-weight: 700;
  color: var(--ln-amber-ink); letter-spacing: 0.2px;
}
.rp-train-band-text i { font-style: normal; font-size: 0.72rem; color: var(--ln-dim); }
.rp-train-band-go {
  flex: 0 0 auto; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.5px;
  text-transform: uppercase; color: var(--ln-amber-ink);
  background: var(--bg-rp-train-band-go);
  border: 1px solid var(--line-rp-train-band-go);
  border-radius: 999px; padding: 5px 12px; white-space: nowrap;
}
.rp-train-band:hover .rp-train-band-go { background: var(--bg-rp-train-band-go-2); }

/* Under the openings hero and on the review screen: same band, given room. */
.rp-train-band-wide { max-width: 560px; margin: 16px auto 6px; }

@media (max-width: 760px) {
  .rp-train-band { padding: 10px 12px; gap: 10px; }
  .rp-train-band-text b { font-size: 0.9rem; }
  .rp-train-band-go { padding: 4px 10px; }
}

/* ── Opening page redesign (Repertoire): masthead hero + chapter card grid +
   chapter detail page. Scoped so the shared .master-hero (color pick, new
   repertoire, home) is untouched. */

/* Reusable round back button. */
.rp-ophead-back {
  width: 34px; height: 34px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 1.2rem; line-height: 1; cursor: pointer;
  background: var(--ln-green-tint); color: var(--ln-ink);
  border: 1px solid var(--ln-border-soft);
  transition: background .15s, border-color .15s, transform .12s;
}
.rp-ophead-back:hover { border-color: var(--ln-green); transform: translateX(-1px); }
.rp-ophead-back:focus-visible { outline: 2px solid var(--ln-green); outline-offset: 2px; }

/* Hero masthead. .ep-pagehero is the shared page-header version used on the
   Train / Learn / Book home screens, styled identically (card + green accent
   bar + serif title) so every tab's header reads the same. */
.rp-ophero, .ep-pagehero {
  position: relative; max-width: 860px; margin: 0 auto 22px;
  padding: 14px 16px 16px;
  background: var(--ln-card, #fffaf0);
  border: 1px solid var(--ln-border-soft); border-radius: 20px;
  box-shadow: var(--ln-shadow-sm); overflow: hidden;
}
.ep-pagehero { max-width: 760px; }
.rp-ophero::before, .ep-pagehero::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 5px;
  background: var(--ln-green);
}
.ep-pagehero-title {
  font-family: var(--ln-serif); font-size: 2rem; line-height: 1.1;
  letter-spacing: 0.3px; color: var(--ln-ink); margin: 0;
}
.ep-pagehero-lead { margin: 8px 0 0; color: var(--ln-dim); font-size: 0.95rem; line-height: 1.55; }
.rp-ophero-back { position: absolute; left: 14px; top: 14px; z-index: 2; }
/* Repertoire landing ("Your Repertoires") + "New Repertoire": reuse the
   opening-page masthead so the header reads the same across the tab. These are
   top-level screens, so no back button and no side column. Narrowed to line up
   with the repertoire cards below (760px). */
.rp-reps-hero { max-width: 760px; }
.rp-ophero-lead { margin: 8px 0 0; color: var(--ln-dim); font-size: 0.95rem; line-height: 1.55; }

/* Repertoire list cards under the hero. Same card system as the rest of the
   tab: cream surface, soft border, hover lift, and a thin green accent tying
   them to the hero (muted grey when the repertoire is still empty). */
.rp-repcard {
  position: relative; max-width: 760px; margin: 0 auto 12px;
  padding: 15px 18px 15px; overflow: hidden;
  background: var(--ln-card, #fffaf0);
  border: 1px solid var(--ln-border-soft); border-radius: 16px;
  box-shadow: var(--ln-shadow-sm);
  transition: border-color .15s, box-shadow .15s, transform .12s;
}
.rp-repcard::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
  background: var(--ln-green);
}
.rp-repcard.is-empty::before { background: var(--ln-border-soft); }
.rp-repcard.is-confirm::before { background: var(--bg-rp-danger-btn); }
.rp-repcard.is-confirm { border-color: var(--line-rp-repcardis-confirm); }
.rp-repcard:hover { border-color: var(--ln-green); box-shadow: var(--ln-shadow-md); transform: translateY(-2px); }
.rp-repcard.is-editing:hover, .rp-repcard.is-confirm:hover,
.rp-repcard-form:hover { transform: none; }
.rp-repcard-form:hover { border-color: var(--ln-border-soft); box-shadow: var(--ln-shadow-sm); }
.rp-repcard-head { cursor: pointer; }
.rp-repcard-name {
  font-family: var(--ln-serif); font-size: 1.4rem; font-weight: 700;
  color: var(--ln-ink); line-height: 1.15;
}
.rp-repcard-sub { margin-top: 3px; font-size: 0.83rem; color: var(--ln-dim); line-height: 1.4; }
.rp-repcard-acts { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 13px; }
.rp-repcard.is-editing .rp-create-input { margin-bottom: 12px; }
/* Create button in its own zone below the list. */
.rp-reps-create { max-width: 760px; margin: 20px auto 0; text-align: center; }
/* The first-run start card: a board before a word, in place of the old notice
   and naming form. Same card furniture as everything else on this screen. */
.rp-startcard {
  max-width: 380px; margin: 0 auto;
  background: var(--ln-card); border: 1px solid var(--ln-border-soft);
  border-radius: 18px; box-shadow: var(--ln-shadow-sm); overflow: hidden;
}
.rp-startcard-board { max-width: 260px; margin: 16px auto 0; }
.rp-startcard-board img, .rp-startcard-board svg { display: block; width: 100%; height: auto; }
.rp-startcard-body { display: flex; flex-direction: column; gap: 12px; padding: 14px 18px 18px; }
.rp-startcard-title {
  font-family: var(--ln-serif); font-size: 1.15rem; font-weight: 700;
  margin: 0; text-align: center; text-wrap: balance;
}
.rp-startcard-body .btn { width: 100%; justify-content: center; }
/* Reset-progress in its own footer zone (was borrowing the empty-state style). */
.rp-home-reset { max-width: 760px; margin: 18px auto 0; display: flex; justify-content: center; flex-wrap: wrap; gap: 10px; }
/* Amber warning callout (e.g. Lichess data unavailable). */
.rp-warn {
  max-width: 760px; margin: 0 auto 14px;
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 14px; border-radius: 12px;
  background: var(--ln-amber-tint); border: 1px solid var(--line-rp-warn);
}
.rp-warn-ic { flex: 0 0 auto; font-size: 1.05rem; line-height: 1.3; }
.rp-warn-tx { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.rp-warn-tx b { color: var(--ln-ink); font-size: 0.9rem; }
.rp-warn-tx span { color: var(--ln-dim); font-size: 0.82rem; line-height: 1.4; }
/* Train "which repertoire?" picker: the same repertoire cards, fully tappable
   (whole card picks the repertoire) with a chevron affordance. */
.ms-repcard { display: flex; align-items: center; gap: 12px; cursor: pointer; }
.ms-repcard .ms-repcard-body { flex: 1; min-width: 0; }
.ms-repcard-go { flex: 0 0 auto; font-size: 1.3rem; line-height: 1; color: var(--ln-green); opacity: 0.5; transition: opacity 0.15s, transform 0.15s; }
.ms-repcard:hover .ms-repcard-go, .ms-repcard:focus-visible .ms-repcard-go { opacity: 1; transform: translateX(2px); }
.ms-repcard:focus-visible { outline: 2px solid var(--ln-green); outline-offset: 2px; }
/* Opening-page hero: back button + eyebrow/title share a top row, the action
   tiles sit full-width below (no left indent), matching the compact home hero. */
/* Never wraps: Explore stays pinned top-right at every width, and a long
   opening name wraps onto a second line inside its own column instead of
   shoving the button down to a row of its own (Ed, 2026-08-01). */
.rp-ophero-head { display: flex; flex-wrap: nowrap; align-items: flex-start; gap: 12px; }
.rp-ophero-headmain { flex: 1 1 auto; min-width: 0; overflow-wrap: break-word; }
/* Opening-page action tiles flow two-by-two and wrap: two per row when they
   fit, a lone tile stretches to fill instead of leaving a gap. */
.rp-ophero-acts.rp-op-acts { display: flex; flex-wrap: wrap; }
.rp-op-acts .rp-act { flex: 1 1 150px; min-width: 0; }
.rp-op-acts .rp-act-tx { min-width: 0; }
/* Keep the Train tile's line-count badge compact so it fits the narrower tile. */
.rp-op-acts .rp-act-badge { margin-left: 8px; padding: 1px 7px; }

/* The colour pick's header is a trail too (Ed, 2026-08-04):
   Repertoires › Choose color. It replaces the back pill and
   the repertoire name that sat opposite it. Same max-width and spacing as the
   .rp-home-top row it takes over from, so nothing below it moves. */
.rp-crumb-top { max-width: 760px; margin: 0 auto; }
.rp-crumb-top .book-crumbs { margin: 0 0 6px; }

/* Repertoire home hero: compact. Back button + green label share a top row,
   the action tiles sit full-width below (no left indent from the back button). */
.rp-home-hero { padding: 14px 16px; }
.rp-home-hero-head { display: flex; align-items: center; gap: 12px; }
.rp-home-hero-eyebrow {
  flex: 1 1 auto; text-align: center; padding-right: 46px;
  font-family: var(--ln-serif); font-weight: 700;
  font-size: 1.5rem; line-height: 1.1; letter-spacing: 0.3px;
  color: var(--ln-green);
}
.rp-home-hero .rp-ophero-acts { margin-top: 12px; }
/* Explore fills the acts grid's second slot; when it's absent (no openings
   yet), Change-my-options is the row's only tile — let it span both columns
   instead of leaving a dead gap. */
.rp-home-acts .rp-act:only-child { grid-column: 1 / -1; }
.rp-ophero-main { flex: 1 1 auto; min-width: 0; }
.rp-ophero-eyebrow {
  font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.09em; color: var(--ln-green); margin-bottom: 3px;
}
.rp-ophero-title {
  font-family: var(--ln-serif); font-size: 2rem; line-height: 1.1;
  letter-spacing: 0.3px; color: var(--ln-ink); margin: 0;
}
.rp-ophero-meta { display: flex; flex-wrap: wrap; gap: 6px; }
.rp-chip {
  font-size: 0.72rem; font-weight: 600; letter-spacing: 0.02em;
  color: var(--ln-dim); background: var(--bg-rp-chip);
  border: 1px solid var(--ln-border-soft); border-radius: 999px;
  padding: 3px 10px; white-space: nowrap;
}
.rp-chip.is-done { color: var(--ln-green-deep); background: var(--bg-ms-rrowdone); border-color: var(--line-rp-chipis-done); }
.rp-chip.is-progress { color: var(--ink-rp-chipis-progress); background: var(--bg-rp-chipis-progress); border-color: var(--line-rp-warn); }
.rp-chip.is-attn { color: var(--ink-rp-chipis-attn); background: var(--bg-rp-chipis-attn); border-color: var(--line-rp-chipis-attn); }
/* Actions as an equal 2-up grid of tiles: same weight, one clean block. Each
   icon sits in its own tinted chip so the tiles read as designed, not plain.
   Train keeps an amber accent so it still reads as the "test" action. */
.rp-ophero-acts {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px; margin-top: 14px;
}
.rp-act {
  display: flex; align-items: center; gap: 11px;
  padding: 10px 13px; font: inherit; cursor: pointer; text-align: left;
  background: linear-gradient(180deg, var(--bg-rp-act-2), var(--bg-rp-act)), var(--ln-card, var(--bg-rp-act-3));
  border: 1px solid var(--ln-border-soft); border-radius: 13px; color: var(--ln-ink);
  box-shadow: 0 1px 2px var(--shadow-rp-act);
  transition: border-color .14s, box-shadow .14s, transform .12s, background .14s;
}
.rp-act:hover { border-color: var(--ln-green); box-shadow: var(--ln-shadow-md); transform: translateY(-2px); }
.rp-act:hover .rp-act-ic { background: var(--bg-active-move); }
.rp-act:active { transform: translateY(0); }
.rp-act:focus-visible { outline: 2px solid var(--ln-green); outline-offset: 2px; }
.rp-act-ic {
  flex: 0 0 auto; width: 32px; height: 32px; border-radius: 9px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 1rem; line-height: 1;
  background: var(--ln-green-tint); color: var(--ln-green);
  transition: background .14s, color .14s;
}
.rp-act-tx { font-family: var(--ln-serif); font-weight: 700; font-size: 0.87rem; letter-spacing: 0.2px; line-height: 1.15; }
.rp-act-badge {
  margin-left: auto; font-size: 0.66rem; font-weight: 700; white-space: nowrap;
  padding: 1px 8px; border-radius: 999px;
  background: var(--bg-rp-act-badge); color: var(--ln-dim);
}
.rp-act.is-train { background: linear-gradient(180deg, var(--bg-rp-actis-train), var(--bg-rp-act)), var(--ln-amber-tint); border-color: var(--line-rp-warn); }
.rp-act.is-train:hover { border-color: var(--line-hover); }
.rp-act.is-train:hover .rp-act-ic { background: var(--bg-rp-act-ic); }
.rp-act.is-train .rp-act-ic { background: var(--bg-rp-act-ic-2); color: var(--ln-amber-ink); }
.rp-act.is-train .rp-act-tx { color: var(--ln-amber-ink); }
.rp-act.is-train .rp-act-badge {
  background: var(--bg-rp-act-badge-2); color: var(--ln-amber-ink); border: 1px solid var(--line-rp-warn);
}
/* Explore rides on the title row, vertically centred against the eyebrow+title
   block. It's the hero's only side item now the progress ring is gone. */
.rp-ophero-side { flex: 0 0 auto; align-self: center; display: flex; align-items: center; gap: 12px; }

/* Explore / Chapters: a single action button labelled by its destination, in its
   own zone on the title row. Not a stateful toggle. A SQUARE (Ed, 2026-08-01):
   word over glyph, no arrow. It shares the row with the opening's name, so the
   74px it costs is ~60px handed back to the title — enough to keep two-word names
   like French Defense on one line. Fixed size in both states so the control never
   moves under your thumb. */
/* A pill, glyph then word on one row (Ed, 2026-08-25). As a 74px square it set
   the height of the hero's title row, which is where the gap between the
   opening's name and the approach row came from. */
.rp-viewbtn {
  display: inline-flex; flex-direction: row; align-items: center; justify-content: center;
  gap: 7px; padding: 8px 14px; flex: none;
  font: inherit; cursor: pointer; text-align: left;
  background: var(--ln-card, #fffaf0); border: 1px solid var(--ln-border-soft);
  border-radius: 999px; color: var(--ln-ink);
  transition: border-color .15s, box-shadow .15s, transform .12s, background .15s;
}
.rp-viewbtn:hover { border-color: var(--ln-green); box-shadow: var(--ln-shadow-sm); transform: translateY(-1px); }
.rp-viewbtn:focus-visible { outline: 2px solid var(--ln-green); outline-offset: 2px; }
.rp-viewbtn.is-explore { background: var(--ln-green-tint); border-color: var(--line-rp-viewbtnis-explore); }
.rp-viewbtn.is-explore:hover { border-color: var(--ln-green); }
.rp-viewbtn-ic { flex: 0 0 auto; font-size: 1.15rem; line-height: 1; }
.rp-viewbtn-tx { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
/* nowrap: "Chapters" is the longer of the two labels and sits ~59px wide inside
   62px of content box. It must never wrap silently in a fixed-height square. */
.rp-viewbtn-tx b { font-family: var(--ln-serif); font-size: 0.82rem; font-weight: 700; color: var(--ln-ink); white-space: nowrap; }
.rp-viewbtn-tx i { font-style: normal; font-size: 0.68rem; color: var(--ln-dim); }

/* Whole-repertoire summary, root of the combined board only. */
.rpm-sum { max-width: 420px; margin: 10px auto 2px; }
.rpm-sum-bar {
  height: 6px; border-radius: 999px; overflow: hidden;
  background: var(--bg-rpm-sum-bar);
}
.rpm-sum-fill { height: 100%; background: var(--ln-green); border-radius: 999px; }
.rpm-sum-tx { margin-top: 6px; text-align: center; font-size: 0.82rem; color: var(--ln-ink); }

/* Gaps: moves real opponents play that you have nothing for. Deliberately not
   buttons and deliberately quiet, they are information, not a to-do list. */
/* Moves real players reach for here that your repertoire has nothing for. The
   SAME rows as the moves above (Ed, 2026-08-26: "make them the same design as
   the moves above"), so it is one .rpm-moves grid with one difference: a dashed
   border. That dash is the whole signal — the "Played here, but nothing in your
   repertoire" header went with it ("The dotted line is enough"), and so did the
   word under each move, which spent a line saying "Sometimes played" where every
   other row on this board shows a number.
   They share ONE .rpm-moves grid with the prepared moves (Ed, 2026-08-26: "all
   the moves should be in one big group not separated") — two grids left a hole
   in the right-hand column whenever the prepared count was odd. So there is no
   .rpm-holes container any more; the dash on each row is the whole separation. */
.rpm-mv.gap {
  border-style: dashed; border-color: var(--line-rpm-hole); background: var(--bg-rpm-hole);
}
.rpm-mv.gap:hover { border-color: var(--text-dim); background: var(--bg-hover-17); }
.rpm-mv.gap .rpm-mv-san { color: var(--ln-dim); }
/* NOTHING EMITS .rpm-mv-more as of 2026-08-27 (Ed: "Can we get rid of +2 more and
   just always show all of the moves automatically?"). Every gap move is listed;
   the 1% floor is the only thing that leaves one out. Kept as the record of what
   the class was, like .rpm-gap above it.

   Its one lasting lesson is in the selector: written with BOTH classes, for the
   same reason .rpm-mv.gap needs two — .rpm-mv sets `background: var(--surface)`
   and sits LATER in this file, so a single-class rule here ties on specificity
   and loses. That rendered this row in the darker surface tan while every row
   beside it was the pale dashed one, and it was invisible in the diff. */
.rpm-mv.rpm-mv-more {
  border-style: dashed; border-color: var(--line-rpm-hole); background: var(--bg-rpm-hole);
  display: flex; align-items: center; justify-content: center;
  font-size: 0.78rem; color: var(--ln-dim);
}

/* Chapter card grid */
.rp-chgrid-label {
  max-width: 860px; margin: 0 auto 10px;
  font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--ln-dim);
}
.rp-chgrid {
  max-width: 860px; margin: 0 auto;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px;
}
.rp-chcard {
  display: flex; flex-direction: column; text-align: left; padding: 0;
  font: inherit; cursor: pointer; overflow: hidden;
  background: var(--ln-card, #fffaf0);
  border: 1px solid var(--ln-border-soft); border-radius: 16px;
  box-shadow: var(--ln-shadow-sm);
  transition: transform .13s ease, border-color .13s ease, box-shadow .13s ease;
}
.rp-chcard:hover { transform: translateY(-3px); border-color: var(--ln-green); box-shadow: var(--ln-shadow-md); }
.rp-chcard:focus-visible { outline: 2px solid var(--ln-green); outline-offset: 2px; }
.rp-chcard.is-new { opacity: 0.9; }
.rp-chcard.is-next { border-color: var(--ln-green); box-shadow: 0 0 0 1px var(--ln-green), var(--ln-shadow-sm); }
/* Held down while the page waits for this chapter's reply order (Ed, 2026-08-28:
   "don't want the user to think the screen is stuck"). Measured with the games
   database hanging: 0.78s, capped, with the chapter list still on screen — so
   nothing looks broken, but the tap looked like it did nothing.
   A pressed state, not a spinner: on a normal connection the wait is about
   100ms, where a spinner would be a flash. It wins over :hover because it is
   later with the same specificity, so a cursor sitting on the card cannot leave
   it looking un-tapped. */
.rp-chcard.is-opening {
  transform: translateY(1px) scale(0.985);
  border-color: var(--ln-green);
  box-shadow: inset 0 2px 6px var(--sh-rp-chcard-press, rgba(60, 42, 12, 0.22));
  opacity: 0.88;
}
.rp-chcard-board { position: relative; border-bottom: 1px solid var(--ln-border-soft); }
.rp-chcard-board img, .rp-chcard-board svg { display: block; width: 100%; height: auto; }
/* "Start here", over the board on the two cards that ASK you something: the
   opening's approach card and a chapter's answer card. It has its own class
   because the chapter cards' "Up next" chip, which used to share one, is gone
   (Ed, 2026-08-31) — and a shared name would have taken this with it, which is
   exactly what happened when it did. Absolute, so it sits ON the board instead
   of beside it: in flow it squeezes the board narrow and lands in the margin. */
.rp-startchip {
  position: absolute; top: 8px; left: 8px;
  font-size: 0.6rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--ink-rp-danger-btn); background: var(--ln-green); border-radius: 999px; padding: 2px 9px;
  box-shadow: 0 1px 3px var(--shadow-header);
}
.rp-chcard-body { display: flex; flex-direction: column; gap: 4px; padding: 11px 13px 12px; flex: 1; }
.rp-chcard-top { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.rp-chcard-eyebrow {
  font-size: 0.63rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ln-dim);
}
.rp-chcard-chip {
  font-size: 0.63rem; font-weight: 700; letter-spacing: 0.02em;
  border-radius: 999px; padding: 2px 8px; white-space: nowrap;
  color: var(--ln-dim); background: var(--bg-rp-chip);
}
.rp-chcard-chip.is-done { color: var(--ln-green-deep); background: var(--ln-green-tint); }
.rp-chcard-chip.is-progress { color: var(--ink-rp-chipis-progress); background: var(--ln-amber-tint); }
.rp-chcard-chip.is-attn { color: var(--ink-rp-chipis-attn); background: var(--bg-rp-chcard-chipis-attn); }
/* The move and how often you face it share one row (Ed, 2026-09-14, option 4 of
   docs/chapter-percent-mock.html). Baseline-aligned, so the big figure sits on the
   move's own line rather than floating above it; min-width on the move lets a long
   label wrap inside its half instead of pushing the share off the card. */
.rp-chcard-moverow { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.rp-chcard-moverow .rp-chcard-move { min-width: 0; }
.rp-chcard-move {
  font-family: ui-monospace, Menlo, monospace; font-size: 1.28rem; font-weight: 700;
  color: var(--ln-ink); line-height: 1.1;
}
/* The digits are the number; the sign is a unit, so it is set smaller and the eye
   reads down a column of figures rather than a column of percent signs. Tabular so
   34 and 38 line up across the grid. */
.rp-chcard-pct {
  flex: none; font-size: 1.24rem; font-weight: 800; line-height: 1;
  color: var(--ln-green); font-variant-numeric: tabular-nums;
}
.rp-chcard-pct span { font-size: 0.78rem; font-weight: 700; }
.rp-chcard-sub { font-size: 0.78rem; color: var(--ln-dim); font-style: italic; line-height: 1.25; }
/* The "you play" card on a door page carries the Change control (Ed, 2026-09-05:
   "For this flow move the approach to the card"). The eyebrow and Change group on
   the left so the status chip keeps the right end of the row to itself. */
.rp-doorcard-foot { display: flex; justify-content: flex-end; margin-top: 4px; }
.rp-doorcard-change {
  font-size: 0.63rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ln-green); cursor: pointer; flex: none; opacity: 0.8;
}
.rp-doorcard-change:hover { opacity: 1; text-decoration: underline; }
/* The card's foot (a progress bar and "2/4 lines") went on 2026-08-30 with the
   total it measured — see rpChapterCardHtml. The chip says both now. */
/* Wide card: the chapter card turned on its side, board beside the words rather
   than above them, with a go chevron. Used by the approach card in the chapter
   grid and by the chapter page's big card. A full-width board would be a
   half-screen of chess for one line, which is why the board is a side panel. */
.rp-widecard { flex-direction: row; align-items: stretch; text-align: left; }
.rp-widecard .rp-chcard-board {
  flex: 0 0 42%; max-width: 260px; align-self: stretch;
  border-bottom: none; border-right: 1px solid var(--ln-border-soft);
  display: flex; align-items: center;
}
.rp-widecard .rp-chcard-body { justify-content: center; gap: 5px; padding: 14px 16px; }
.rp-widecard .rp-chcard-sub { font-style: normal; }
.rp-widecard-go {
  flex: 0 0 auto; align-self: center; padding-right: 14px;
  font-size: 1.3rem; line-height: 1; color: var(--ln-green); opacity: 0.55;
  transition: opacity .13s ease, transform .13s ease;
}
.rp-widecard:hover .rp-widecard-go { opacity: 1; transform: translateX(2px); }
/* The approach card is a wide card that also has to span the chapter grid. */
.rp-chgrid .rp-appcard { grid-column: 1 / -1; }

/* The chapter page's big card: the one thing to do here, above the lines. */
/* Real air above it: the action row sets margin-bottom 2px, so with no top
   margin here the card sat flush against Train and Build more lines and read as
   part of the same block. Margins collapse, so this is the whole gap. */
.rp-chbig { width: 100%; margin: 12px 0 14px; }
.rp-chbig .rp-chcard-board { flex-basis: 38%; max-width: 200px; }
.rp-chbig-eyebrow {
  font-size: 0.64rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.09em; color: var(--ln-green);
}
.rp-chbig-name { font-family: var(--ln-serif); font-size: 1.15rem; line-height: 1.2; color: var(--ln-ink); }
.rp-chbig-desc { font-size: 0.82rem; line-height: 1.4; color: var(--ln-dim); }
.rp-chbig.is-more .rp-chbig-eyebrow { color: var(--ln-dim); }

/* The answer card: the chapter's board and its question as one object, standing
   in for the old board column + "Choose your response" pill + line of grey text
   (Ed, 2026-08-07). Same wide-card skeleton as the approach card, so the two
   screens read as the same move one level apart. */
.rp-chpage-ask { max-width: 860px; margin: 0 auto; }
.rp-anscard { width: 100%; }
/* The wide card's shared 260px board cap is for a thumbnail beside a line of
   text. Here the board IS the screen, so lift it, and take a bigger share of
   the card than the other wide cards do: with the explanatory line gone the
   words are three short ones, and the board is what the screen is for.
   48% of the 860px card is 413px, against the 300px the old left-hand column
   gave it. 48 and not more: at 56% the words get 292px and "Black plays
   2...Bb4" wraps onto two lines, which is worse than the space it saves. */
.rp-anscard .rp-chcard-board { max-width: none; flex-basis: 48%; }
.rp-anscard .rp-chcard-body { gap: 7px; padding: 22px 24px; }
.rp-anscard-move {
  font-family: ui-monospace, Menlo, monospace; font-size: 1.55rem; font-weight: 700;
  line-height: 1.15; color: var(--ln-ink);
}
/* A short rule, not a full-width one: it separates the move from the question
   without drawing a line across the card. */
.rp-anscard-rule { width: 54px; height: 1px; background: var(--ln-border-soft); margin: 3px 0 2px; }
.rp-anscard-name { font-family: var(--ln-serif); font-size: 1.45rem; line-height: 1.2; color: var(--ln-ink); }

/* Group cards: one per reply. They open IN PLACE (Ed, 2026-08-01), so the rows
   stay exactly where they were and the page gains no navigation level.
   REBUILT 2026-08-28 as a COMPACT ROW inside the replies card: a colour dot, the
   move and its covered count on one line, a tick and a chevron. The 64px board
   that used to lead the row is gone — one board now sits above the whole list
   carrying an arrow per reply, so a thumbnail here was the same picture twice,
   and dropping it is what lets six replies fit on one screen. */
.rp-chgroup { background: none; border: 0; border-radius: 0; margin: 0; }
.rp-chgroup + .rp-chgroup { border-top: 1px solid var(--ln-border-soft); }
.rp-chgroup-sum {
  display: flex; align-items: center; gap: 13px; padding: 9px 12px 9px 13px;
  cursor: pointer; list-style: none;
}
.rp-chgroup-sum::-webkit-details-marker { display: none; }
.rp-chgroup-sum:hover { background: var(--bg-hover-18); }
.rp-chgroup[open] > .rp-chgroup-sum { background: var(--ln-green-tint); }
/* The one thing tying a row to its arrow, now that the row has no board. */
.rp-chgroup-dot {
  flex: 0 0 10px; width: 10px; height: 10px; border-radius: 50%;
  box-shadow: 0 0 0 1px var(--sh-rp-chdot, rgba(0, 0, 0, 0.14));
}
.rp-chgroup-board {
  flex: 0 0 64px; width: 64px; border-radius: 9px; overflow: hidden;
  border: 1px solid var(--ln-border-soft); display: block;
}
.rp-chgroup-board img, .rp-chgroup-board svg { display: block; width: 100%; height: auto; }
/* One line, not two: the label is a move and the meta is four words, so stacking
   them made every row 40px for no gain. They wrap as a pair on a narrow phone. */
.rp-chgroup-text {
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 3px 8px;
}
.rp-chgroup-label {
  font-family: ui-monospace, Menlo, monospace; font-size: 0.98rem; font-weight: 700;
  color: var(--ln-ink);
}
.rp-chgroup-name {
  font-family: var(--ln-serif); font-size: 0.86rem; color: var(--ln-ink);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rp-chgroup-meta { font-size: 0.72rem; color: var(--ln-dim); }
/* How often they play this reply, at the right end of the row — the same place
   and the same treatment the chapter card gives it one level up. Smaller than the
   card's, because a row is a row: it sits beside 0.98rem of move rather than
   under a board. Tabular so the figures line up down the list. */
.rp-chgroup-pct {
  flex: none; font-size: 1.02rem; font-weight: 800; line-height: 1;
  color: var(--ln-green); font-variant-numeric: tabular-nums;
}
.rp-chgroup-pct span { font-size: 0.7rem; font-weight: 700; }
.rp-chgroup-chev { flex: 0 0 auto; color: var(--ln-green); opacity: 0.55; transition: transform .15s ease; }
.rp-chgroup[open] .rp-chgroup-chev { transform: rotate(180deg); }
.rp-chgroup-body { padding: 2px 13px 12px; }
/* The card's own two actions, side by side at the foot of it (Ed, 2026-08-29):
   the green one learns the next line IN THIS REPLY, the quiet one adds another.
   The green one is absent when this reply has nothing left, so a finished card
   is one quiet button. `margin-top: 0` beats .rp-more-btn's own 5px, which was
   for a button standing alone under a list. */
/* The card's one action, FIRST in the body and set as a line of text rather than
   a button (Ed, 2026-08-29: "the button feels outdated"). Green words, an arrow,
   nothing around them. "Learn the next line" while this reply has something
   written to learn, "Add more lines" once it is all covered — never both. */
.rp-chgo {
  display: flex; align-items: center; gap: 8px;
  font: inherit; font-size: 0.86rem; font-weight: 700; cursor: pointer;
  color: var(--ln-green-deep); background: none; border: 0;
  padding: 10px 0 0; margin: 0; text-align: left;
}
.rp-chgo:hover { color: var(--ln-green); }
.rp-chgo:focus-visible { outline: 2px solid var(--ln-green); outline-offset: 3px; border-radius: 4px; }
.rp-chgo-ar { font-size: 0.95rem; line-height: 1; }
/* What you have already covered, under a rule: the record, below the one thing
   to do. Empty on a reply you have not learned in, and then the rule goes too. */
.rp-chgroup-record {
  margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--ln-border-soft);
  display: flex; flex-direction: column; gap: 2px;
}
/* Scoped to this card, not to `.rpc-line-act` at large: the chapter-card grid
   still uses the small uppercase version of that class. */
.rp-chgroup-record .rp-ch-line { padding: 4px 2px; }
.rp-chgroup-record .rpc-line-name {
  flex: 1 1 auto; min-width: 0; font-family: var(--ln-serif); font-size: 0.86rem; color: var(--ln-ink);
}
.rp-chgroup-record .rpc-line-act {
  margin-left: auto; font-size: 0.95rem; text-transform: none; letter-spacing: 0;
  color: var(--ln-faint); opacity: 1;
}
.rp-chgroup-record .rp-ch-line.clickable:hover .rpc-line-act { color: var(--ln-green-deep); }

/* ============================================================
   THE CHAPTER PAGE, REBUILT  (Ed, 2026-08-28)
   "I kind of hate the long cards like this… I want it to be user friendly and
   not intimidating, and very easy to navigate."
   docs/chapter-page-redesign-mock.html then chapter-page-final-mock.html — five
   rounds, and what he settled on is Mix C: ONE card that welds a board carrying
   an arrow per reply to a compact list of those replies, so the picture and the
   list are the same object. Tap an arrow or a row and the board dims to that one
   arrow.
   ============================================================ */
/* ONE CENTRED COLUMN AT EVERY WIDTH. The two-column laptop layout shipped
   earlier the same day put the board beside the replies, which only worked while
   the replies were tall cards; with one board carrying every reply's arrow they
   ARE one object, and splitting them across columns breaks the tie the redesign
   exists for. `.rp-chpage.is-v2` outranks the 1100px rule's `.rp-chpage` on
   specificity, so it holds at every width without a media query of its own.
   The bottom padding is the FLOATING BAR's room and belongs only to a page that
   has one: it is fixed, so the last button would otherwise sit under it, but with
   no bar the same space is dead paper you can scroll into. 78px is the bar's own
   height plus its offset off the nav plus a small gap — measured, not guessed. */
.rp-chpage.is-v2 { max-width: 520px; padding-bottom: 14px; }
.rp-chpage.is-v2.has-foot { padding-bottom: 78px; }
/* The header board is a THUMBNAIL beside its caption. The big board on this page
   is the arrows board below, and two full-size boards stacked is the same
   position twice. */
.rp-chpage-boardcard.is-slim {
  display: flex; align-items: center; width: 100%; max-width: none; margin: 0 0 12px;
}
.rp-chpage-boardcard.is-slim .rp-chpage-board {
  flex: 0 0 92px; width: 92px; border-right: 1px solid var(--ln-border-soft);
}
.rp-chpage-boardcard.is-slim .rp-chpage-cap {
  flex: 1 1 auto; min-width: 0; border-top: 0; padding: 8px 10px;
}
.rp-chsec { display: flex; align-items: baseline; gap: 8px; margin: 0 0 9px; }
.rp-chsec h3 {
  margin: 0; font-size: 0.66rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.1em; color: var(--ln-dim);
}
.rp-chsec-n { font-size: 0.66rem; color: var(--ln-faint); }
/* The card: board on top, replies under it, one frame around both. */
.rp-chreplies {
  background: var(--ln-card); border: 1px solid var(--ln-border-soft);
  border-radius: 16px; overflow: hidden; box-shadow: var(--ln-shadow-sm);
}
/* Capped by HEIGHT, not by a fixed width. The board is square, so a vh cap on
   its width caps how much of the window it takes, and the replies under it stay
   on screen with it — which is the whole point of welding them into one card.
   A flat 318px cap left a band of empty card either side of the board on a
   laptop; no cap at all pushed the replies off the bottom of the window. */
.rp-chboard {
  position: relative; margin: 0 auto; max-width: min(100%, 42vh);
  border-bottom: 1px solid var(--ln-border-soft);
}
/* A laptop window is SHORTER than a phone's (870 against 932) while its header
   and crumb row cost the same, so the same fraction leaves the replies off the
   bottom. Measured over the seeded openings: 12 of 20 chapter pages fitting
   whole above the bar at 42vh, 17 at 36vh — the same 17 the phone gets, with the
   worst remainder a six-reply page hanging 31px under it. The phone keeps the
   bigger board (391px against 313px). */
@media (min-width: 761px) { .rp-chboard { max-width: min(100%, 36vh); } }
.rp-chboard > img { display: block; width: 100%; height: auto; }
/* The overlay is square with the board, so the 800-unit viewBox maps one square
   to 100 units and the arrows are drawn with epbSetArrows' own numbers. */
.rp-chboard > svg {
  position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none;
}
/* One tap target per arrow, on the square it lands on. */
.rp-chhit {
  position: absolute; width: 12.5%; height: 12.5%; transform: translate(-50%, -50%);
  border: 0; background: none; padding: 0; cursor: pointer; border-radius: 50%;
}
.rp-chhit:hover { background: var(--ln-green-tint2); }
.rp-chhit:focus-visible { outline: 2px solid var(--ln-green); outline-offset: 0; }
/* Opening ONE reply dims the board to that reply's arrow. Two open at once is
   ambiguous, so the board goes back to showing them all (rpChReplySync). */
.rp-cha { transition: opacity .16s ease; }
.rp-chboard > svg.is-dim .rp-cha { opacity: 0.16; }
.rp-chboard > svg.is-dim .rp-cha.is-on { opacity: 1; }
.rp-ch-lines { display: block; }
/* No board means nothing for a colour dot to point at, so it goes. */
.rp-chreplies.is-list .rp-chgroup-dot { display: none; }
.rp-chreplies.is-list .rp-chgroup-sum { padding-left: 14px; }
/* ---- The foot bar: the one thing to do, pinned above the tab bar ----
   The big card's three moods are this button's three labels now, off the same
   scan (rpChapterNextScan), so the button and the list can never point at
   different things. THE RULE, NOT THE MOVE (Ed): "Pick your move" / "Learn the
   next line" / "Add more lines" — so it never changes width and never wraps. */
.rp-chfoot {
  position: fixed; left: 0; right: 0; z-index: 6;
  bottom: calc(54px + env(safe-area-inset-bottom, 0px) + 10px);
  display: flex; gap: 9px; align-items: stretch;
  max-width: 520px; margin: 0 auto; padding: 0 14px;
  pointer-events: none;
}
.rp-chfoot > * { pointer-events: auto; }
/* Focus mode hides the tab bar, so the bar drops to the safe inset with it. */
body.ep-focus .rp-chfoot { bottom: calc(env(safe-area-inset-bottom, 0px) + 10px); }
/* Half and half (Ed, 2026-08-29), so `flex: 1 1 0` and not `1 1 auto` — auto
   sizes each half by its own words, which made "Learn the next line" the wider
   of the two by a third. Centred, because a left-aligned label in half a bar
   with no chevron after it reads as unfinished. */
.rp-chfoot-go {
  flex: 1 1 0; min-width: 0; display: flex; align-items: center; justify-content: center; gap: 8px;
  font: inherit; font-size: 0.88rem; font-weight: 700; text-align: center; cursor: pointer;
  color: var(--ink-rp-primary-btn, #fff);
  background: linear-gradient(180deg, var(--bg-rp-foot-go-1, #4a8c1e), var(--ln-green));
  border: 1px solid var(--ln-green-deep); border-radius: 999px; padding: 12px 18px;
  box-shadow: var(--sh-rp-foot, 0 6px 18px rgba(30, 50, 10, 0.35));
}
/* Nothing outstanding here, so the button steps down rather than shouting. */
.rp-chfoot-go.is-calm {
  color: var(--ln-ink);
  background: linear-gradient(180deg, var(--bg-rp-foot-calm-1, #e6dcb6), var(--ln-card2));
  border-color: var(--ln-border);
  box-shadow: var(--sh-rp-foot-calm, 0 4px 14px rgba(60, 42, 12, 0.18));
}
.rp-chfoot-ic { flex: none; font-style: normal; }
.rp-chfoot-tx { flex: 0 1 auto; min-width: 0; }
.rp-chfoot-chev { flex: none; opacity: 0.85; }
/* Add another reply and Train, in the page's own flow at the foot of the list
   (Ed, 2026-08-29: "The only floating button should be the learn next line
   button"). Two matching quiet pills, Add first: building a reply is part of the
   same job as learning one, and Train is where you go when that job is done. */
.rp-chbtm { display: flex; flex-direction: column; gap: 9px; margin-top: 13px; }
.rp-chbtm-btn {
  display: flex; align-items: center; justify-content: center; gap: 9px; width: 100%;
  font: inherit; font-size: 0.88rem; font-weight: 700; cursor: pointer;
  color: var(--ln-ink);
  background: linear-gradient(180deg, var(--bg-rp-foot-calm-1, #e6dcb6), var(--ln-card2));
  border: 1px solid var(--ln-border); border-radius: 999px; padding: 12px 16px;
  box-shadow: var(--sh-rp-foot-calm, 0 4px 14px rgba(60, 42, 12, 0.18));
}
.rp-chbtm-btn:hover { background: var(--ln-card2); }
.rp-chbtm-btn:focus-visible { outline: 2px solid var(--ln-green); outline-offset: 2px; }
.rp-chbtm-ic { flex: none; font-size: 0.98rem; line-height: 1; }
/* The count rides inside the pill rather than pinned to its edge, so both
   buttons read as the same object with one of them carrying a number. */
.rp-chbtm-n {
  flex: none; font-size: 0.7rem; font-weight: 700; white-space: nowrap;
  color: var(--ln-dim); background: var(--ln-card);
  border: 1px solid var(--ln-border); border-radius: 999px; padding: 2px 9px;
}
.rp-chfoot-go:focus-visible {
  outline: 2px solid var(--ln-green); outline-offset: 2px;
}
/* Two halves plus Train is three things on a 430px phone, so the words step down
   rather than wrap. */
@media (max-width: 430px) {
  .rp-chfoot { padding: 0 12px; }
}

/* "Chosen moves" at the top of a group whose question you answered. Without it the
   question is one-way: the rows only hold the move you picked, so the alternatives
   are off the page entirely.
   Written as the LINE OF PLAY it makes — their move plain, yours a chip you can tap
   to re-pick from there — one row per branch (Ed, 2026-08-23, on the flat list of
   six moves it replaces: "this kind of just looks ridiculous"). The flat version
   printed "7...Nxe4" twice, because those answer 7.Nc3 and 7.Kf1 and a bare list of
   your own moves has no way to say so. Four treatments: docs/chapter-your-moves-mock.html.
   AT MOST THREE MOVES, and no fold: capped, the block is one line of text on 86% of
   the book's cards and three at the very worst, so the disclosure it used to hide
   behind had nothing left to hide. See RP_CHOSEN_MAX in repertoire.js. */
.rp-chosen {
  padding: 5px 2px 7px; border-bottom: 1px solid var(--ln-border-soft); margin-bottom: 3px;
}
/* One chosen move keeps its label on the same baseline as the move, which is how
   the row it replaces read. Stacked, it costs a whole line on 56% of the cards. */
.rp-chosen.is-one { display: flex; align-items: baseline; gap: 8px; }
.rp-chosen.is-one .rp-chosen-row { flex: 1 1 auto; min-width: 0; }
.rp-chosen-row { display: flex; align-items: baseline; gap: 8px; padding: 2px 0; }
.rp-chosen-mv {
  flex: 1 1 auto; min-width: 0;
  font-family: ui-monospace, Menlo, monospace; font-size: 0.79rem; line-height: 1.9;
}
.rp-chthem { color: var(--ln-dim); }
.rp-chmine {
  display: inline-block; font: inherit; font-weight: 700; color: var(--ln-green-deep);
  background: var(--ln-green-tint); border: 1px solid var(--line-rp-chmine);
  border-radius: 6px; padding: 0 5px; margin: 0 3px 0 1px; cursor: pointer;
}
.rp-chmine:hover { background: var(--ln-green-tint2); border-color: var(--ln-green); }
.rp-chmine:focus-visible { outline: 2px solid var(--ln-green); outline-offset: 2px; }
.rp-chosen-row .rp-chans-change { align-self: center; }
.rp-chans-lab {
  font-size: 0.6rem; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--ln-dim);
}
/* The one choice still to make, at the TOP of a card whose other lines are done.
   Asked over the position it is about, the way a person would say it. */
.rp-chask-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  margin: 6px 0 8px; padding: 8px; cursor: pointer; font: inherit; text-align: left;
  background: none;
  border: 1px solid var(--ln-green, #3e7818); border-radius: 12px;
}
.rp-chask-row-board {
  flex: 0 0 54px; width: 54px; border-radius: 7px; overflow: hidden;
  border: 1px solid var(--ln-border-soft); display: block;
}
.rp-chask-row-board img, .rp-chask-row-board svg { display: block; width: 100%; height: auto; }
.rp-chask-row-t { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.rp-chask-row-q { font-weight: 600; font-size: 0.93rem; line-height: 1.25; color: var(--ln-ink); }
.rp-chask-row-ways { font-size: 0.76rem; color: var(--ln-dim); }
.rp-chask-row-go {
  flex: 0 0 auto; align-self: center; color: var(--ln-green); font-weight: 700;
  font-size: 0.69rem; letter-spacing: 0.06em; text-transform: uppercase;
}
.rp-chans-change {
  margin-left: auto; background: none; border: 0; padding: 2px 4px; cursor: pointer;
  font: inherit; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--ln-green);
}

/* Chapter detail page */
.rp-chpage { max-width: 860px; margin: 0 auto; }
/* The way out on the left, which chapter this is on the right (Ed, 2026-08-25).
   The row owns the gap under it, so neither child carries its own margin and
   the two sit on one baseline. */
.rp-chpage-top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 14px;
}
.rp-chpage-top .rp-chpage-crumb, .rp-chpage-top .rp-chpage-eyebrow { margin-bottom: 0; }
/* The eyebrow holds one line and the crumb takes the slack. The longest opening
   name ("Queen's Indian Defense · as White") wraps at 430px whatever we do, and
   letting BOTH wrap put "CHAPTER 1" over two lines beside it. A button centres
   its text by default, so the wrapped crumb needs telling to stay left. */
.rp-chpage-top .rp-chpage-eyebrow { flex: none; white-space: nowrap; }
.rp-chpage-top .rp-chpage-crumb { min-width: 0; text-align: left; }
.rp-chpage-crumb {
  font: inherit; font-size: 0.82rem; font-weight: 600; cursor: pointer;
  color: var(--ln-dim); background: none; border: none; padding: 4px 0; margin-bottom: 14px;
}
.rp-chpage-crumb:hover { color: var(--ln-ink); }
.rp-chpage-cols { display: flex; gap: 26px; align-items: flex-start; }
/* On a laptop the left column is the whole chapter: its name and your answer on
   top of the board, the board, then the chapter's actions underneath (Ed,
   2026-08-07). The right column is left to the lines. Narrow screens keep the
   old arrangement — see the 760px block below. */
.rp-chpage-boardcol { flex: 0 0 auto; width: 340px; max-width: 44vw; }
.rp-chpage-board {
  border-radius: 12px; overflow: hidden; border: 1px solid var(--ln-border-soft);
  box-shadow: var(--ln-shadow-sm);
}
.rp-chpage-board img, .rp-chpage-board svg { display: block; width: 100%; height: auto; }
.rp-chpage-detail { flex: 1 1 auto; min-width: 0; }
.rp-chpage-eyebrow {
  display: flex; align-items: center; gap: 8px; margin-bottom: 5px;
  font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ln-dim);
}
.rp-chpage-move {
  font-family: ui-monospace, Menlo, monospace; font-size: 2rem; font-weight: 700;
  color: var(--ln-ink); margin: 0 0 4px;
}
.rp-chpage-tag { font-family: var(--ln-serif); color: var(--ln-dim); font-size: 0.95rem; margin-bottom: 12px; }
.rp-chpage-note { color: var(--ln-dim); font-size: 0.9rem; margin: 10px 0; }
.rp-chpage-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.rp-chpage-actions .btn { font-size: 0.86rem; }

/* ---- The opening's pages on a wide laptop ----
   Ed, 2026-08-28, from a chapter page on a 1869px screen: "sizing here too,
   should be much bigger". The page was capped at 860px with a 340px board, so a
   thousand pixels of window went unused.

   ONE width for the opening page and the chapter page, so walking between them
   never changes how wide the app is. 1180 rather than the full window: at 1400
   the group cards stretch to 854px and go mostly empty, which is wider without
   being better. This way the board takes most of the gain (340 -> 520) and the
   right-hand column gets a readable 634px.

   The openings home takes the same width (Ed, 2026-08-28: "yes widen it and make
   the cards bigger"). Its grid needed three caps lifted, not one: the section
   wrapper is capped at 760px as well as the grid inside it, so raising the grid
   alone left it at 760 and only the header moved. */
@media (min-width: 1100px) {
  :root { --rp-page-w: min(1180px, 94vw); }
  .rp-ophero { max-width: var(--rp-page-w); }
  .rp-chgrid, .rp-chgrid-label { max-width: var(--rp-page-w); }
  .rp-chpage, .rp-chpage-ask { max-width: var(--rp-page-w); }
  .rp-chpage-boardcol { width: 520px; }
  .rp-open-section, .rp-open-grid, .rp-home-reset { max-width: var(--rp-page-w); }
  .rp-contcard { max-width: var(--rp-page-w); }
  /* The search box was never in this list, which did not show while it sat
     tucked between the card and the grid. Above the card (Ed, 2026-08-31) it
     leads the page, and a 760px box over a 1180px card reads as a mistake. */
  .rp-open-search { max-width: var(--rp-page-w); }
  /* Bigger cards, not more of them: at the 170px floor a wider grid just adds
     columns and leaves the tiles the size they were. 280 gives four across at
     1180 and takes each tile from 181px to 283px. */
  .rp-open-grid { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
}
.rp-chpage .rp-train-pill { margin: 14px 0 0; }

/* Slim train pill: the old amber band's job (own zone, its colour) without the
   height. Centred on its own line. */
.rp-train-pill {
  display: flex; width: fit-content; align-items: center; gap: 8px;
  margin: 13px auto 0; padding: 7px 16px; font: inherit; cursor: pointer;
  background: var(--ln-amber-tint); border: 1px solid var(--line-rp-train-band);
  border-radius: 999px; color: var(--ln-amber-ink);
  transition: background .15s, border-color .15s, transform .12s, box-shadow .15s;
}
.rp-train-pill:hover {
  background: var(--bg-hover-5); border-color: var(--line-hover);
  transform: translateY(-1px); box-shadow: var(--ln-shadow-sm);
}
.rp-train-pill:active { transform: translateY(0); }
.rp-train-pill:focus-visible { outline: 2px solid var(--ln-amber-ink); outline-offset: 2px; }
.rp-train-pill-icon { font-size: 1rem; line-height: 1; }
.rp-train-pill-txt {
  font-family: var(--ln-serif); font-weight: 700; font-size: 0.9rem; letter-spacing: 0.2px;
}
.rp-train-pill-n {
  font-size: 0.72rem; font-weight: 700; padding: 1px 9px; border-radius: 999px;
  background: var(--bg-rp-train-pill-n); border: 1px solid var(--line-rp-warn);
  white-space: nowrap;
}

@media (max-width: 760px) {
  .rp-ophero-title, .ep-pagehero-title { font-size: 1.6rem; }
  .rp-ophero-acts { grid-template-columns: 1fr; }
  /* Home hero keeps its two actions side by side even on a phone. */
  .rp-home-acts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Two compact cards per row on a phone instead of one giant board each. */
  .rp-chgrid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .rp-chcard-move { font-size: 1.1rem; }
  .rp-chcard-body { padding: 9px 10px 10px; }
  /* Board first on a phone, then the move and your answer, then the actions,
     then the lines. The laptop's left column holds the first three in DOM order,
     so the phone puts the board back on top with `order` and restates the
     spacing the old markup got for free. Flex items don't collapse their
     margins, so each gap is written once and only once.
     Putting the move row ABOVE the board here was built and reverted the same
     day (Ed, 2026-08-25: "I actually like it better before"). It measured well —
     the first card came 12px higher — but on a chapter with no answer yet the
     next thing under the board is the answer card, which carries a board of its
     own, and with nothing between them the page reads as two stacked boards.
     The mock only ever showed a chapter that HAD an answer. */
  .rp-chpage-cols { flex-direction: column; gap: 0; }
  .rp-chpage-boardcol { display: flex; flex-direction: column; width: 100%; max-width: none; margin: 0; }
  /* The cap is welded under the board now, so the CARD carries the cap the board
     used to — and with it the `order: -1` that put the board first is gone,
     because board and caption are one element and already in that order.
     440, not the old 360 (Ed, 2026-08-28: "the board got smaller, make it
     bigger"). It did not get smaller — it measures 358px on this branch and 358
     on main, identical — but 360 was leaving 38px of the page's 398 unused, so
     the honest fix for "make it bigger" is to lift the cap rather than defend
     the number. Board 358 -> 396 on a 430px phone. The ceiling still exists for
     a big screen that has not yet reached the 761px two-column layout. */
  .rp-chpage-boardcard { width: 100%; max-width: 440px; margin: 0 auto 12px; }
  .rp-chpage-acts { margin-top: 0; }
  /* Stacked column: without this the detail shrinks to its content width
     (align-items: flex-start), so short-content states didn't use the full
     screen width. Force it full-width so every state fills the screen. */
  .rp-chpage-detail { width: 100%; }
  /* The answer card STACKS on a phone, unlike the other wide cards. Those carry
     a line of text beside a thumbnail; this one carries the move at 1.55rem and
     a sentence, and a 42% board leaves about 200px for them. Board on top at
     full width, words underneath, which also gives the phone the biggest board
     of any state on this page. */
  /* Fit the whole card on screen without scrolling (Ed, 2026-08-07). Cap the
     BOARD's height and leave the card full width, so the words keep the same
     width and the same height whatever the board does. Narrowing the card
     instead was the first attempt and it fights itself: the text wraps onto
     more lines as fast as the board shrinks, and at 375x667 that still ran 10px
     under the tab bar.
     Measured against `dvh`, the height actually visible with Safari's toolbars
     showing, not `vh`, which counts the space they cover. 350 is the rest of
     the screen: header 52, crumb 24, the card's own words 131, the tab bar 54,
     and a gap. It was 400 while the card carried an explanatory line; dropping
     that line took the words from 215 to 131, which is why the board can be
     bigger now. On a tall screen (the installed app) max-width wins instead and
     the board runs the full width of the card. */
  .rp-anscard { flex-direction: column; }
  .rp-anscard .rp-chcard-board {
    flex: 0 0 auto; width: 100%; max-width: min(100%, calc(100dvh - 350px));
    margin-inline: auto;
    border-right: none; border-bottom: 1px solid var(--ln-border-soft);
  }
  .rp-anscard .rp-chcard-body { padding: 16px 18px 18px; }
  .rp-anscard-move { font-size: 1.35rem; }
  .rp-anscard-name { font-size: 1.25rem; }
  /* Scoped to this card only: the approach card and the big card still sit as
     rows on a phone, and their chevron must stay centred beside the words. */
  .rp-anscard .rp-widecard-go { align-self: flex-end; padding: 0 18px 14px 0; }
}

/* ── Chapter list: make it read as chess, not a contents page ──────────────
   Was seven near-identical boxes with the position shrunk into a corner and
   the move buried mid-sentence. Now: the move leads at full size, the board
   carries real weight, one card is clearly your next move, and finished work
   shrinks out of the way instead of taking the same room as everything else. */

/* The move is the card's identity, so it's set like a headline. Scoped to the
   title so the small green "your answer" chip elsewhere is untouched. */
.rpc-title .rpc-move {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 1.34rem; font-weight: 700; letter-spacing: -0.01em;
  padding: 2px 12px; border-radius: 9px; vertical-align: 0;
  background: var(--bg-rpc-move-2); border: 1px solid var(--line-rpc-move);
}
.rpc.learned .rpc-title .rpc-move {
  background: var(--bg-ms-rrowdone); border-color: var(--line-rpc-move-2);
}

/* The board is the most recognisable thing on the card. Give it room, square
   corners closer to a real board, and a little depth. */
.rpc-board {
  flex-basis: 104px; width: 104px; border-radius: 7px;
  box-shadow: 0 2px 5px var(--shadow-rpc-board), 0 0 0 1px var(--shadow-rpc-board-2);
}

/* A spine down the list: the chapters are one opening branching, not seven
   unrelated cards. Sits behind the cards, stops before the last one. */
#rep2-chapters .rp-chapter-card.rpc { position: relative; }
#rep2-chapters .rp-chapter-card.rpc::after {
  content: ''; position: absolute; left: -1px; top: 100%; height: 14px;
  width: 2px; background: linear-gradient(var(--bg-after-7), var(--bg-after-8));
}
#rep2-chapters .rp-chapter-card.rpc:last-of-type::after { display: none; }

/* Your next move. The one card that answers "what do I do now". */
.rp-chapter-card.rpc.rpc-next {
  border-color: var(--line-rp-chapter-cardrpcrpc-next);
  box-shadow: 0 2px 4px var(--shadow-rp-chapter-cardrpcrpc-next), 0 12px 30px var(--shadow-rp-chapter-cardrpcrpc-next-2);
  border-style: solid;
}
.rp-chapter-card.rpc.rpc-next::before { background: var(--accent, #3e7818); opacity: 1; }
.rpc-next .rpc-board { flex-basis: 132px; width: 132px; }
.rpc-next .rpc-title .rpc-move { font-size: 1.6rem; }
/* The marker sits in the badge slot on the right of the eyebrow, replacing
   "Not started" rather than stacking a second label above the move. Appending it
   to "Chapter N" ran it under the board on a phone. "Up next" reads for both an
   untouched and a part-built chapter, and can't be misread as a chess move. */
.rpc-next .rpc-eyebrow { flex-wrap: nowrap; }
.rpc-next .rp-ch-badge { display: none; }
.rpc-next .rpc-eyebrow::after {
  content: 'Up next'; flex: none; margin-left: auto; padding: 2px 10px;
  border-radius: 99px; font-size: 0.63rem; letter-spacing: 0.09em;
  text-transform: uppercase; font-weight: 700; white-space: nowrap;
  background: var(--bg-rpc-movegreen); color: var(--accent, #3e7818);
}

/* Finished chapters collapse to one quiet line: tick, move, name. The green
   medal and border already say "covered", so the badge is dropped rather than
   left shouting over the move that identifies the row. */
.rp-chapter-card.rpc.learned.collapsed { padding: 7px 12px 7px 17px; opacity: 0.8; }
.rp-chapter-card.rpc.learned.collapsed:hover { opacity: 1; }
.rpc.learned.collapsed .rpc-head { align-items: center; gap: 10px; }
.rpc.learned.collapsed .rpc-titles {
  display: flex; align-items: baseline; gap: 10px; min-width: 0;
}
.rpc.learned.collapsed .rpc-eyebrow { display: none; }
.rpc.learned.collapsed .rpc-title { margin: 0; flex: none; }
.rpc.learned.collapsed .rpc-title .rpc-move {
  font-size: 1rem; padding: 1px 9px; background: transparent; border-color: transparent;
}
.rpc.learned.collapsed .rpc-tagline {
  margin: 0; font-size: 0.82rem; opacity: 0.7;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rpc.learned.collapsed .rpc-board { flex-basis: 38px; width: 38px; box-shadow: none; opacity: 0.75; }
.rpc.learned.collapsed .rpc-medal { flex-basis: 24px; width: 24px; height: 24px; margin-top: 0; }
.rpc.learned.collapsed .rpc-medal-num { font-size: 0.78rem; }

/* Untouched chapters read as an outline, not a stack of demands. */
.rp-chapter-card.rpc.locked:not(.rpc-next) { opacity: 0.82; }
.rp-chapter-card.rpc.locked:not(.rpc-next):hover { opacity: 1; }

@media (max-width: 480px) {
  .rpc-board { flex-basis: 84px; width: 84px; }
  .rpc-next .rpc-board { flex-basis: 104px; width: 104px; }
  .rpc-title .rpc-move { font-size: 1.18rem; }
  .rpc-next .rpc-title .rpc-move { font-size: 1.36rem; }
  .rpc-next .rpc-eyebrow > span:first-child::after { font-size: 0.58rem; margin-left: 6px; }
}

/* ── Board view ────────────────────────────────────────────────────────────
   The repertoire as a position you play through instead of a list you scroll.
   One board, the opponent's replies drawn on it, tap a square to descend. */
.rpm { max-width: 760px; margin: 0 auto; }
.rpm-trail {
  display: flex; flex-wrap: wrap; gap: 5px; align-items: center; margin-bottom: 12px;
}
.rpm-crumb {
  font: inherit; font-family: ui-monospace, Menlo, monospace; font-size: 0.78rem;
  cursor: pointer; background: var(--surface, #fffaf0);
  border: 1px solid var(--border, #ddd0b4); color: var(--text-dim, #6f6151);
  padding: 3px 10px; border-radius: 99px;
}
.rpm-crumb:hover { color: var(--text); border-color: var(--text-dim); }
.rpm-crumb.now {
  background: var(--text, #2a2016); color: var(--surface, #fdf8ec); border-color: transparent;
}
.rpm-sep { opacity: 0.4; }

/* How wide the Explore board is. ONE number, three readers: the board itself,
   the move grid under it and the trail card above it, so the screen reads as a
   single column at every width instead of a wide board over a narrow list.
   Same shape as --ms-bw on the drill. */
:root { --rpm-bw: 460px; }
.rpm-stage { width: 100%; max-width: var(--rpm-bw); margin: 0 auto; }

/* The path: where you are in the tree, and the way back up. It sits at the foot
   of the panel, in the slot the Move list used to hold (Ed moved it down from
   under the board on 2026-08-26), so its top margin is the 14px that one had.
   ONE line at every depth — see rpMapPathHtml for the measurement behind that.
   It is a rule on this screen rather than a preference: .rpm-below is held to a
   fixed min-height precisely so walking the tree never changes the page height
   and never jumps the board, and a row that wraps would be 48px on one node and
   126px on the next. So the moves scroll sideways instead of wrapping. */
.rpm-path {
  position: relative; display: flex; align-items: center; gap: 6px;
  width: 100%; max-width: 460px; margin: 14px auto 0; padding: 8px 10px;
  border-radius: 12px;
  background: var(--bg-rpm-path); border: 1px solid var(--ln-border-soft);
}
.rpm-path-up {
  flex: none; width: 32px; height: 32px; border-radius: 50%; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--ln-ui); font-size: 1.05rem; line-height: 1;
  background: var(--bg-button-3); border: 1px solid var(--line-button); color: var(--ln-dim);
}
.rpm-path-up:hover:not(:disabled) { background: var(--bg-hover-15); color: var(--ln-ink); }
.rpm-path-up:disabled { opacity: 0.35; cursor: default; }
/* overscroll-behavior-x keeps a swipe that runs out of track from turning into
   the browser's back-navigation gesture on iOS. */
.rpm-path-track {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 2px;
  overflow-x: auto; overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch;
  scrollbar-width: none; padding: 2px 10px 2px 2px;
}
.rpm-path-track::-webkit-scrollbar { display: none; }
/* Numbered, the way the app's move list writes moves everywhere else, so 4. c3
   and 4... c3 can never be read as the same move (Ed picked D4 for this). */
.rpm-path-num {
  flex: none; font-family: var(--ln-serif); font-size: 0.78rem; font-weight: 700;
  color: var(--ln-dim); margin-left: 5px; font-variant-numeric: tabular-nums;
}
.rpm-path-num:first-child { margin-left: 0; }
.rpm-path-mv {
  flex: none; font-family: 'Roboto Mono', 'Courier New', monospace; font-size: 0.88rem;
  padding: 2px 6px; border-radius: 5px; cursor: pointer;
  background: none; border: 0; color: var(--ln-dim); white-space: nowrap;
}
.rpm-path-mv:hover { background: var(--ln-green-tint); color: var(--ln-ink); }
.rpm-path-mv.is-here { background: var(--bg-ep-mv-active); color: var(--ln-ink); font-weight: 700; }
/* The fades are the only cue that the track scrolls, so they only appear when it
   actually does (rpMapPathPin adds .is-scroll) — over a path that already fits
   they would just wash out the first move for no reason. Never over the back
   button: the left one starts past it (32px button + 10px padding + 6px gap). */
.rpm-path-fade { display: none; position: absolute; top: 1px; bottom: 1px; width: 24px; pointer-events: none; }
.rpm-path.is-scroll .rpm-path-fade { display: block; }
.rpm-path-fade-l { left: 48px;
  background: linear-gradient(90deg, var(--bg-rpm-path), var(--bg-rpm-path-0)); }
.rpm-path-fade-r { right: 1px; border-radius: 0 11px 11px 0;
  background: linear-gradient(270deg, var(--bg-rpm-path), var(--bg-rpm-path-0)); }
.rpm-board {
  width: 100%; height: auto; display: block; border-radius: 10px;
  border: 2px solid var(--dark-sq);
  box-shadow: 0 2px 6px var(--shadow-rpm-board), 0 14px 36px var(--shadow-rpc-board);
}
.rpm-hit { cursor: pointer; }
.rpm-hit:hover rect:first-child { fill-opacity: 0.3; }
.rpm-hit:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Interactive Explore board: real DOM squares (so pieces drag) + a scaling
   SVG arrow overlay. */
/* Coordinates are inside the squares now (epSquareCoord), so the board fills
   its column. Kept as a wrapper rather than flattened: rpm-domwrap is what the
   arrow overlay positions against. */
.rpm-coords { width: 100%; }
.rpm-domwrap { position: relative; width: 100%; }

/* Analyze mode: horizontal eval bar (aligned to the board column, past the rank
   gutter) + the analysis panel. Same palette/mapping as the vertical bar. */
.rpm-analyze .rpm-evalh { margin: 0 0 9px; }
.rpm-evalh {
  position: relative; width: auto; height: 18px; border-radius: 5px; overflow: hidden;
  background: var(--bg-eval-bar);                          /* Black's side */
  border: 1px solid var(--border, #ddd0b4);
  box-shadow: inset 0 1px 3px var(--shadow-bottom-nav);
}
.rpm-evalh-fill {
  position: absolute; left: 0; top: 0; bottom: 0; width: 50%;
  background: var(--light-sq);                          /* White's side */
  transition: width 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}
.rpm-evalh-fill.thinking { animation: evalPulse 1.1s ease-in-out infinite; }
.rpm-evalh::after {                             /* dead-even centre line (the "0.0" reference) */
  content: ''; position: absolute; top: 0; bottom: 0; left: 50%; width: 2px;
  margin-left: -1px; background: var(--bg-after-9); pointer-events: none; z-index: 1;
}
.rpm-evalh.lead-black::after { background: var(--bg-after); }
.rpm-evalh-num {
  position: absolute; top: 0; bottom: 0; display: flex; align-items: center;
  font-family: monospace; font-size: 0.66rem; font-weight: 700; letter-spacing: -0.5px;
  pointer-events: none; z-index: 3;
}
.rpm-evalh-num.at-white { left: 6px; color: var(--ink-eval-bar-numat-white); }
.rpm-evalh-num.at-black { right: 6px; color: var(--light-sq); }
.rpm-evalh-num.eq { color: var(--ink-eval-bar-numeq); }

/* The eval word (White edge / Better / …) rides inside the bar on the winning
   side, just inboard of the number, so the bar runs full-width and its centre is
   the true middle. */
.rpm-an-word {
  position: absolute; top: 0; bottom: 0; left: 38px; right: auto;
  display: flex; align-items: center;
  font-size: 0.64rem; font-weight: 700; white-space: nowrap;
  color: var(--ink-eval-bar-numat-white); pointer-events: none; z-index: 3;
}
.rpm-evalh.lead-black .rpm-an-word { left: auto; right: 38px; color: var(--light-sq); }
.rpm-an-moves { text-align: center; font-family: ui-monospace, Menlo, monospace; font-size: 0.82rem; max-width: 440px; margin: 8px auto 0; line-height: 1.7; }
.rpm-an-mv { white-space: nowrap; }
/* NOTHING EMITS .rpm-an-open or .rpm-actions as of 2026-08-26 — the standalone
   Analyze button and the green primary beside it became chips in the card's own
   foot row (.rpm-foot), so this board has one button treatment. Kept as a record
   of what the classes were, the way .rpm-offbook-acts is. */
.rpm-an-open { display: flex; justify-content: center; margin-top: 14px; }
/* The move list sits at the FOOT of the Explore board, under Analyze (Ed,
   2026-08-07), so it needs the same gap the button above it carries. */
.rpm .ep-moves { margin-top: 14px; }
.rpm-dom {
  display: grid; grid-template-columns: repeat(8, 1fr); grid-template-rows: repeat(8, 1fr);
  width: 100%; aspect-ratio: 1 / 1; border-radius: 10px; overflow: hidden;
  border: 2px solid var(--dark-sq);
  box-shadow: 0 2px 6px var(--shadow-rpm-board), 0 14px 36px var(--shadow-rpc-board);
  touch-action: none; user-select: none; -webkit-user-select: none;
}
.rpm-sq { position: relative; display: flex; align-items: center; justify-content: center; }
.rpm-sq.lt { background: var(--light-sq); }
.rpm-sq.dk { background: var(--dark-sq); }
.rpm-sq.movable { cursor: grab; }
.rpm-sq.movable:active { cursor: grabbing; }
.rpm-sq.movable:hover { filter: brightness(1.08); }
.rpm-sq.drag-over { filter: brightness(1.3); outline: 3px solid var(--line-squaredrag-over); outline-offset: -3px; z-index: 2; }
.rpm-sq.lm::before { content: ''; position: absolute; inset: 0; background: var(--bg-before-2); pointer-events: none; }
.rpm-sq.sel::before { content: ''; position: absolute; inset: 0; box-shadow: inset 0 0 0 4px var(--shadow-before); pointer-events: none; z-index: 1; }
.rpm-sq.tgt::after { content: ''; position: absolute; width: 30%; height: 30%; border-radius: 50%; background: var(--bg-after-10); pointer-events: none; }
.rpm-sq.cap::after { content: ''; position: absolute; inset: 7%; border-radius: 50%; border: 5px solid var(--line-rp-chcardis-done); pointer-events: none; }
.rpm-pc { width: 88%; height: 88%; position: relative; z-index: 2; }
/* Behind the pieces (z-index 1 < piece 2): branch hints sit under the board's
   pieces so a dragged piece is never hidden by an arrow. */
.rpm-ov { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1; }

/* ==========================================================================
   Unified responsive board (.epb-*) — the Explore look brought to the app's
   other boards (Book study first). A horizontal eval bar on top (bar + number
   + word), a coordinate-guttered board that stretches to fill its column with a
   scaling viewBox arrow overlay. Built by ensureBoardStructure() in app.js.
   ========================================================================== */
.epb { width: 100%; max-width: 460px; margin: 0 auto; }
/* How big a board may get on a wide screen. ONE number, because .epb's own
   max-width was never what governed: the board columns carry fixed flex-bases
   (480px for the line study, 500px for the lesson and Repertoire builder) and
   those won. Raising .epb alone measured no change at all on a laptop.
   Ed, 2026-08-07: at 1920x1080 the board rendered 480px, a quarter of the
   screen width, on the app's most-used page.
   Height-governed, so a short laptop still fits and only tall screens grow.
   Phone is untouched: the base 460px below stays, and this whole block needs
   760px of width and 720px of height before it applies. */
@media (min-width: 760px) and (min-height: 720px) {
  /* The ceiling was 720px and the containers were 1100px, so on a tall screen the
     board stopped growing with hundreds of pixels of empty page around it (Ed,
     2026-08-28: "we have soooo much room here. Make the board much bigger. look
     how lichess does it"). Measured on the Repertoire walk at 1789x1180: the
     board was 671px, because 1100 of container less the 400px panel and the gap
     left it that much — the height was never the thing binding.

     Raised to 1040px, which is a ceiling and not a target: the height expression
     is what governs on every real window, and it is unchanged. So this can only
     ever grow a board on a screen that already had the room, and it cannot move
     one on a screen where height was already the binder. Laptop only, by the two
     conditions on this block. */
  :root { --ep-board-max: min(1040px, calc(100vh - 300px)); }
  .epb { max-width: var(--ep-board-max); }
  /* The wrappers that actually decide the width. Widened with the ceiling, or the
     board keeps stopping at the container instead of at the window. */
  .book-line-inner { max-width: min(1500px, 94vw); }
  #rep2-builder .master-arena-inner { max-width: min(1500px, 94vw); }
  /* The lesson card, same reason. Its board stays fixed across steps because
     fbFitBoard still sizes it from the height budget, which is step-independent;
     widening the card only raises the width it is measured against. */
  .fable-player-inner { max-width: min(1600px, 94vw); }
  /* The walk's trail is capped to what it sits over — the board, the gap and the
     400px panel — the way the drill's and the study board's already are. Without
     it the wider container above stretches the card the full width of the window
     while the board and panel stay centred and narrower. Later than the
     styles.css rule that zeroes these margins, on equal specificity, so it wins. */
  body.ep-focus #rep2-builder-crumbs {
    max-width: max(360px, calc(var(--ep-board-max) + 22px + 400px));
    margin-left: auto; margin-right: auto;
  }
  .book-board-col { flex-basis: var(--ep-board-max); }
  .master-board-row .fable-board-col {
    flex-basis: var(--ep-board-max); max-width: var(--ep-board-max);
  }
  /* The options card stops short of the board (Ed, 2026-08-07). Its 540px cap
     in styles.css is a fixed number that never knew how tall the board was, so
     the two columns ended at different heights and the card scrolled early.
     Tie it to the board instead: same top, same bottom, and it grows with the
     board on a taller screen. Phone keeps its own `max-height: none`. */
  .rp-builder-panel { max-height: var(--ep-board-max); }
}

/* Horizontal eval bar on top: the bar spans the whole board width, and BOTH the
   number and the plain-English word sit INSIDE it (on the leading side). Keeping
   the word inside is what lets the bar run full-width, so its centre is the true
   visual middle and the even line reads honestly. */
.epb-evalh { margin: 0 0 7px; }

/* The horizontal eval bar itself (fills left→right for White's share). Same
   palette/mapping as the vertical bar; renderEvalResult picks orientation via
   the eval-bar-h class. */
.eval-bar-h {
  position: relative; width: 100%; height: 18px; border-radius: 5px; overflow: hidden;
  background: var(--bg-eval-bar);                            /* Black's side */
  border: 1px solid var(--border, #ddd0b4);
  box-shadow: inset 0 1px 3px var(--shadow-bottom-nav);
}
.eval-bar-h .eval-bar-fill {
  position: absolute; left: 0; top: 0; bottom: 0; right: auto; height: auto; width: 50%;
  background: var(--light-sq);                            /* White's side */
  transition: width 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}
.eval-bar-h .eval-bar-fill.thinking { animation: evalPulse 1.1s ease-in-out infinite; }
.eval-bar-h::after {                              /* dead-even centre line (the "0.0" reference) */
  /* NB: the vertical-bar `.eval-bar::after` (styles.css) matches this same element
     at equal specificity, so explicitly reset its right/height/top or they leak in
     and collapse this into a 2x1px dot. */
  content: ''; position: absolute; top: 0; bottom: 0; left: 50%; right: auto;
  width: 2px; height: auto; margin-left: -1px;
  background: var(--bg-after-11); pointer-events: none; z-index: 1;
}
.eval-bar-h.lead-black::after { background: var(--bg-after); }
/* Number + word share the leading (winning) side; both sit above the centre line. */
.eval-bar-h .eval-bar-num {
  position: absolute; top: 0; bottom: 0; left: auto; right: auto;
  display: flex; align-items: center;
  font-family: monospace; font-size: 0.66rem; font-weight: 700; letter-spacing: -0.5px;
  text-align: left; pointer-events: none; z-index: 3;
}
.eval-bar-h .eval-bar-num.at-white { left: 6px; color: var(--ink-eval-bar-numat-white); }
.eval-bar-h .eval-bar-num.at-black { right: 6px; color: var(--light-sq); }
.eval-bar-h .eval-bar-num.eq { color: var(--ink-eval-bar-numeq); }
/* The word rides just inboard of the number, on the same (winning) side. */
.epb-evalword {
  position: absolute; top: 0; bottom: 0; left: 38px; right: auto;
  display: flex; align-items: center;
  font-size: 0.64rem; font-weight: 700; white-space: nowrap;
  color: var(--ink-eval-bar-numat-white); pointer-events: none; z-index: 3;
}
.eval-bar-h.lead-black .epb-evalword { left: auto; right: 38px; color: var(--light-sq); }

/* Coordinates are inside the squares now (epSquareCoord in app.js), which gives
   the board back the 19px the outside gutters took: 395px to 414px on a 430px
   screen. Kept as a wrapper rather than flattened: epb-boardwrap is what the
   arrow overlay positions against. */
.epb-coords { width: 100%; }
.epb-boardwrap { position: relative; width: 100%; }

/* The labels themselves: rank in the top-right of the right-hand file, file in
   the bottom-left of the bottom rank. Tinted to the OPPOSITE square colour so
   they read on both, and pointer-events: none so they never eat a tap or a
   drag meant for the square. */
.epb-cd {
  position: absolute;
  font-family: ui-monospace, Menlo, monospace;
  font-size: 0.58rem;
  font-weight: 700;
  line-height: 1;
  pointer-events: none;
  user-select: none;
  z-index: 3;
}
.epb-cd-rank { top: 2px; right: 3px; }
.epb-cd-file { bottom: 2px; left: 3px; }
.square.light > .epb-cd, .rpm-sq.lt > .epb-cd { color: var(--ink-epb-cd); }
.square.dark  > .epb-cd, .rpm-sq.dk > .epb-cd { color: var(--ink-epb-cd-2); }

/* The board: real DOM squares (so pieces drag/slide) that stretch to fill the
   column, plus a scaling SVG arrow overlay. */
.epb-board {
  display: grid; grid-template-columns: repeat(8, 1fr); grid-template-rows: repeat(8, 1fr);
  width: 100%; aspect-ratio: 1 / 1; border-radius: 10px; overflow: hidden;
  border: 2px solid var(--dark-sq);
  box-shadow: 0 2px 6px var(--shadow-rpm-board), 0 14px 36px var(--shadow-rpc-board);
  touch-action: none; user-select: none; -webkit-user-select: none;
}
.epb-board .square { width: auto; height: auto; }
.epb-board .piece-img { width: 88%; height: 88%; position: relative; z-index: 2; }
.epb-ov { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1; }

/* The same solid card as a line's end (Ed, 2026-08-25: "I want the card to be
   solid just like the other style, not the see through style"). It was a 10%
   amber wash over the page, which reads as translucent against the paper
   texture. Surface, soft border, 18px, small shadow — the .rp-builder-panel
   values, so a card of choices looks the same wherever you meet one. */
.rpm-offbook {
  max-width: 460px; margin: 14px auto 0; text-align: center;
  background: var(--surface); border: 1px solid var(--ln-border-soft);
  border-radius: 18px; padding: 16px; box-shadow: var(--ln-shadow-sm);
}
/* Follows .rp-builder-panel down to 14px on a phone. It has to sit HERE rather
   than in the 760px block up at the panel's own rule: both are single-class
   selectors, so the later one wins and this block is later. */
@media (max-width: 760px) { .rpm-offbook { border-radius: 14px; } }
.rpm-offbook-note { color: var(--text-dim, #6f6151); margin-bottom: 13px; line-height: 1.45; }
/* NOTHING EMITS this as of 2026-08-25 — the off-book card uses the line end's
   .ms-tiles now, so a card of choices looks the same wherever you meet one. */
.rpm-offbook-acts { display: flex; gap: 9px; justify-content: center; flex-wrap: wrap; }

/* No top margin (Ed, 2026-08-26, circling the space under the board). It has
   exactly one call site and is always the FIRST thing in .rpm-below, whose own
   18px padding-top is already the gap under the board — so the 15px on top of
   that was the same separation written twice, and it made this screen the odd
   one out: 33px here against 18px on every other Explore screen, where the moves
   or the card come first. Now they all sit at 18px. */
.rpm-ask {
  text-align: center; margin: 0 0 11px;
  font-family: var(--ln-serif, Georgia, serif); font-size: 1.02rem;
}
/* Everything below the board. A fixed min-height keeps the page height steady as
   the number of moves changes node to node, so navigating never clamps the
   scroll and jumps the board. Content sits at the top; any slack is empty space
   at the bottom (before the Reset button), out of the way. 18px gap under the
   board. */
.rpm-below { min-height: 300px; padding-top: 18px; }
@media (max-width: 480px) { .rpm-below { min-height: 250px; } }
/* The card's own foot row, standing on its own where there is no card to hang
   under (Ed, 2026-08-26: "it still has the old button style"). Same chips, so
   there is ONE button treatment on this board — but no hairline, because up
   here it is separating nothing: the moves or the caption above it already end,
   and a rule across an otherwise empty box reads as a broken card. Capped to
   the board's width so two chips don't stretch the length of a laptop. */
.rpm-foot.ms-foot {
  border-top: 0; margin-top: 14px; padding-top: 0;
  max-width: 420px; margin-left: auto; margin-right: auto;
}

/* The opponent's moves: two to a row, each carrying how often it is played.
   Design E2 of docs/explore-halfrow-mock.html — see rpMapPathHtml's twin comment
   in repertoire.js for why two lines rather than one. Short version: at half
   width the NAME is what breaks, and only the two-line shape leaves it enough
   room (155px, where all 22 of the book's move names fit; one line leaves 58px
   and fits exactly one).

   ONE column below 380px. Two 155px name boxes need the width, and a phone that
   narrow has none to give. */
.rpm-moves { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; max-width: var(--rpm-bw); margin: 0 auto; }
@media (max-width: 380px) { .rpm-moves { grid-template-columns: 1fr; } }
.rpm-mv {
  position: relative; overflow: hidden; font: inherit; cursor: pointer;
  display: block; text-align: left; padding: 8px 11px; border-radius: 11px;
  background: var(--surface, #fffaf0); border: 1px solid var(--border, #ddd0b4);
  transition: transform .08s, border-color .15s;
}
.rpm-mv:hover { border-color: var(--text-dim); }
.rpm-mv:active { transform: translateY(1px); }
.rpm-mv:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* How often this move is played, as the row's own background rather than a bar:
   a bar would take width from the name, which is the one thing this layout is
   short of. Deliberately NOT green — green already means "you have a reply saved
   for this" here, and one colour cannot carry two meanings. It sits UNDER the
   text, which is why everything else in the row is positioned. */
.rpm-mv-fill {
  position: absolute; left: 0; top: 0; bottom: 0; display: block;
  background: var(--bg-rpm-mv-fill); pointer-events: none;
}
.rpm-mv-head {
  position: relative; display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
}
.rpm-mv-san { font-family: ui-monospace, Menlo, monospace; font-size: 1.02rem; font-weight: 700; }
/* Tabular figures so the numbers line up down each column — the whole reason for
   showing a percentage is comparing it with the others. */
.rpm-mv-pct {
  flex: none; font-family: ui-monospace, Menlo, monospace; font-size: 0.78rem; font-weight: 700;
  color: var(--ln-dim); font-variant-numeric: tabular-nums;
}
.rpm-mv.all { border-color: var(--line-rp-chcardis-done); background: var(--bg-rp-deep-primary); }
.rpm-mv.all .rpm-mv-san { color: var(--ln-green-deep); }
.rpm-mv.some { border-color: var(--line-rp-train-band-go); background: var(--bg-rpm-chipsome); }
.rpm-mv.some .rpm-mv-san { color: var(--ink-rp-chipis-progress); }
/* Nothing saved against this move. There was no rule for it at all, so the move
   took whatever colour the browser gives a bare button: measured black in
   Chromium, and blue in Ed's screenshot on his iPhone. Grey is the whole signal
   now (Ed, 2026-08-25) — green means you answer this move, grey means you
   don't — so it can't be left to the browser. */
.rpm-mv.none .rpm-mv-san { color: var(--text-dim); }

.rpm-actions {
  display: flex; gap: 9px; justify-content: center; flex-wrap: wrap; margin-top: 18px;
}
/* No max-width on .rpm-stage here: the board is meant to be big on a phone
   (Ed, 2026-08-07). It keeps the 460px base cap and fills the screen below it. */
@media (max-width: 480px) {
  .rpm-ask { font-size: 0.95rem; }
  .rpm-mv { padding: 7px 10px; }
}
/* Wide laptop/desktop: the board had acres of empty space around a tiny map.
   Grow it, but keep it height-aware so it still fits a short laptop screen. */
@media (min-width: 760px) and (min-height: 720px) {
  /* .rpm wraps .rpm-stage, so it has to clear the stage's ceiling or it caps it. */
  .rpm { max-width: 800px; }
  :root { --rpm-bw: min(1040px, calc(100vh - 340px)); }

  /* As many moves to a row as fit, never narrower than 220px (Ed, 2026-08-28:
     "3 moves per row"). On a laptop the board grew to 720px and this grid stayed
     at 460, so eleven moves ran six rows deep under a board twice their width.

     auto-fit rather than a flat repeat(3): the board is height-governed, so its
     width is not one number, and three fixed columns would squeeze the cards to
     121px on a 721px-tall window, where calc(100vh - 340px) puts the board at
     381px. At Ed's 720px board this is exactly three across; it drops to two as
     the board narrows, and never below 220px a card. The rail below sets three
     outright, because there the width is a number we choose. */
  .rpm-moves { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
}

/* The Explore trail on a laptop: a card over the board, not a line of text
   stranded at the top-left corner of the window (Ed, 2026-08-28). Same treatment
   the drill and the lesson trails already take at this width, and capped to the
   board's own width so the trail, the board and the moves read as one column.
   Floored at 360px: on a short window the board shrinks with the height, and
   below that the trail stops matching it and starts wrapping instead. */
@media (min-width: 761px) {
  .rp-map-crumbs {
    max-width: max(360px, var(--rpm-bw));
    margin: 0 auto 10px;
    padding: 9px 18px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 18px;
    box-shadow: 0 1px 3px var(--shadow-hover);
    font-size: 1rem;
    gap: 5px 8px;
  }
}

/* ---- Wide laptop: everything under the board moves to its right ----
   Ed, 2026-08-28: "move everything from the bottom to the right to match how we
   usually do things, 3 moves per row instead of 4". The same two-column shape
   the Repertoire walk and the lesson already use at this width: board on the
   left, the panel that talks about it on the right.

   The rail is a fixed 520px because three move cards need it (3 x 160 + 2 x 8 =
   512), and a flexible rail would silently drop to two columns on a narrower
   window. The board keeps its own width and shrinks before the rail does, so the
   moves never re-flow.

   The container is exactly board + gap + rail, so the trail card above lines up
   with both edges rather than floating over the middle of them. */
@media (min-width: 1280px) and (min-height: 720px) {
  :root { --rpm-gap: 22px; --rpm-rail: 520px; }
  .rpm, .rp-map-crumbs {
    max-width: min(calc(var(--rpm-bw) + var(--rpm-gap) + var(--rpm-rail)), 96vw);
  }
  .rpm { display: flex; align-items: flex-start; gap: var(--rpm-gap); }
  /* flex-basis is the board's own width and it may only SHRINK: on a window too
     narrow for both, the board gives way and the rail keeps its three columns. */
  .rpm-main { flex: 0 1 var(--rpm-bw); min-width: 0; }
  .rpm-rail { flex: 0 0 var(--rpm-rail); min-width: 0; }
  /* The grid is the rail's width now, not the board's. */
  .rpm-moves { grid-template-columns: repeat(3, 1fr); max-width: none; }
}

/* Your committed answer: settled, not another open question.
   NOTHING EMITS .mine as of 2026-08-25 — Explore stopped drawing a button for
   the move you've committed, because you play it on the board. Kept as the one
   place that knows what a settled move looks like. */
.rpm-mv.mine {
  border-color: var(--line-rpm-chipmine); background: var(--ln-green-tint);
  box-shadow: 0 0 0 1px var(--shadow-focus);
}
.rpm-mv.mine .rpm-mv-san { color: var(--ln-green-deep); }

/* NOTHING EMITS any of this as of 2026-08-26 — the Explore board offers the
   practise card (.rpm-offbook + .ms-tiles + .ms-foot) instead of a list of every
   line through the position. Kept with rpMapNode's `lines` field, which built
   these rows; the two go together if either is ever removed. */
.rpm-lines {
  max-width: 420px; margin: 18px auto 0; display: flex; flex-direction: column; gap: 6px;
}
.rpm-lines-head {
  font-size: 0.66rem; letter-spacing: 0.1em; text-transform: uppercase;
  opacity: 0.55; margin-bottom: 2px; text-align: center;
}
.rpm-line {
  font: inherit; cursor: pointer; display: flex; align-items: center; gap: 10px;
  width: 100%; text-align: left; padding: 9px 13px; border-radius: 10px;
  background: var(--surface, #fffaf0); border: 1px solid var(--border, #ddd0b4);
  transition: transform .08s, border-color .15s;
}
.rpm-line:hover { border-color: var(--text-dim); }
.rpm-line:active { transform: translateY(1px); }
.rpm-line:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.rpm-line-tick {
  width: 9px; height: 9px; border-radius: 99px; flex: none;
  border: 1px solid var(--border, #ddd0b4); background: transparent;
}
.rpm-line.done .rpm-line-tick { background: var(--accent); border-color: var(--accent); }
.rpm-line-name {
  flex: 1; min-width: 0; font-size: 0.9rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rpm-line.done .rpm-line-name { opacity: 0.72; }
.rpm-line-act {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.03em;
  color: var(--accent); flex: none;
}
.rpm-line.done .rpm-line-act { color: var(--text-dim, #6f6151); }

/* View switch: chapters (the reference) or board (the explorer). Its own zone,
   not lumped in with expand/collapse -- those do something, this changes where
   you are. Same slot in both views so it never moves under your thumb. */
.rp-viewsw {
  display: flex; gap: 3px; padding: 3px; margin: 0 auto 14px; width: fit-content;
  background: var(--bg-rp-act-badge); border-radius: 99px;
}
.rp-viewsw button {
  font: inherit; font-size: 0.82rem; font-weight: 600; cursor: pointer;
  padding: 5px 16px; border-radius: 99px; border: 1px solid transparent;
  background: transparent; color: var(--text-dim, #6f6151);
  transition: background .15s, color .15s;
}
.rp-viewsw button:hover { color: var(--text, #2a2016); }
.rp-viewsw button.on {
  background: var(--surface, #fffaf0); color: var(--text, #2a2016);
  border-color: var(--border, #ddd0b4);
  box-shadow: 0 1px 2px var(--shadow-rpc-board-2);
}
.rp-viewsw button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.rpm .rp-viewsw { margin-bottom: 12px; }

/* Board view: your decision, or the absence of one. */
.rpm-chapter {
  text-align: center; font-family: var(--ln-serif, Georgia, serif); font-style: italic;
  font-size: 0.92rem; opacity: 0.7; margin: -4px 0 10px;
}
.rpm-mv-name {
  position: relative; display: block; margin-top: 1px;
  font-family: var(--ln-serif, Georgia, serif); font-style: italic;
  font-size: 0.76rem; opacity: 0.72; color: var(--ln-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* NOTHING EMITS these as of 2026-08-25 — see the .mine note above. */
.rpm-answer { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.rpm-answer-note { font-size: 0.76rem; opacity: 0.6; text-align: center; max-width: 300px; }

/* NOTHING EMITS .rpm-gap* as of 2026-08-27 (Ed: "Think we can remove the red box
   here right?"). It restated the caption directly above it, in red and three
   times the size. Kept as the record of what the class was, like .rpm-answer. */
.rpm-gap {
  max-width: 340px; margin: 0 auto; text-align: center; padding: 14px 16px;
  border: 1px dashed var(--line-rpm-gap); border-radius: 12px;
  background: var(--bg-rpm-gap);
}
.rpm-gap-title {
  font-family: var(--ln-serif, Georgia, serif); font-size: 1.02rem;
  font-weight: 600; color: var(--ink-rpm-gap-title); margin-bottom: 3px;
}
.rpm-gap-note { font-size: 0.82rem; opacity: 0.72; }
.rpm-ask b { font-family: ui-monospace, Menlo, monospace; }

/* Offbeat lines (sidelines, mistakes) tucked behind an obvious pill toggle on
   the chapter detail page, so your main answers lead. Native <details>, no JS. */
.rp-ch-extra { margin: 8px 0 6px; }
.rp-ch-extra > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 0.82rem; font-weight: 600; letter-spacing: 0.01em;
  color: var(--accent2, #6f5636);
  padding: 8px 15px; border-radius: 999px;
  border: 1px solid var(--ln-line, #ddd0b4);
  background: var(--ln-card, #fffaf0);
  box-shadow: 0 1px 2px var(--shadow-summary);
  transition: background .15s, border-color .15s, color .15s, box-shadow .15s;
}
.rp-ch-extra > summary::-webkit-details-marker { display: none; }
.rp-ch-extra > summary:hover {
  color: var(--text, #2a2016); border-color: var(--accent2, #a98d5a);
  box-shadow: 0 2px 6px var(--shadow-rpc-board-2);
}
.rp-ch-extra > summary:focus-visible { outline: 2px solid var(--accent, #3e7818); outline-offset: 2px; }
.rpx-chev {
  width: 0; height: 0; flex: none;
  border-left: 5px solid currentColor; border-top: 4px solid transparent; border-bottom: 4px solid transparent;
  transition: transform .18s ease;
}
.rp-ch-extra[open] > summary .rpx-chev { transform: rotate(90deg); }
.rpx-verb { color: var(--text, #2a2016); }
.rpx-hide { display: none; }
.rp-ch-extra[open] > summary .rpx-show { display: none; }
.rp-ch-extra[open] > summary .rpx-hide { display: inline; }
.rp-ch-extra[open] > summary {
  background: var(--faint, rgba(138,124,102,0.14)); border-color: transparent; box-shadow: none;
}
.rpx-body { margin-top: 4px; padding-left: 4px; border-left: 2px solid var(--ln-line, #e6dac1); }

/* Expand / collapse all — a neat right-aligned pair above the lines list. */
/* NOTHING EMITS these as of 2026-08-25 — the chapter page's Expand all /
   Collapse all pair was removed. */
.rp-chpage-tools { display: flex; justify-content: flex-end; gap: 8px; margin: 4px 0 2px; }
.rp-chpage-tool {
  font: inherit; font-size: 0.76rem; font-weight: 600; cursor: pointer;
  color: var(--text-dim, #6f6151); background: transparent;
  border: 1px solid var(--ln-line, #ddd0b4); border-radius: 999px; padding: 4px 12px;
  transition: background .15s, color .15s, border-color .15s;
}
.rp-chpage-tool:hover { color: var(--text, #2a2016); background: var(--faint, rgba(138,124,102,0.12)); border-color: var(--accent2, #a98d5a); }
.rp-chpage-tool:focus-visible { outline: 2px solid var(--accent, #3e7818); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .rpx-chev { transition: none; } }

/* Chapter page actions: the same tiles the opening hero uses, sitting up under
   "Your answer" instead of stranded below the lines list. */
/* Same two-by-two flow as the opening hero: two tiles per row when they fit, a
   lone tile stretches to fill rather than leaving a gap. Slightly smaller icon
   and label so "Build more lines" / "Continue chapter" fit on one line at
   half-width, keeping the pair balanced (no lop-sided tall tile from wrapping). */
.rp-chpage-acts {
  display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 2px;
}
.rp-chpage-acts .rp-act { flex: 1 1 150px; min-width: 0; padding: 10px 12px; gap: 9px; }
.rp-chpage-acts .rp-act-ic { width: 28px; height: 28px; font-size: 0.95rem; }
.rp-chpage-acts .rp-act-tx { min-width: 0; font-size: 0.82rem; }
.rp-chpage-acts .rp-act-badge { margin-left: 8px; padding: 1px 7px; }
/* "Add another reply" closes the page under the replies it adds to, so it gets
   its own air instead of reading as one more row of the list above it. */
.rp-chpage-acts.rp-chpage-more { margin: 18px 0 4px; }

/* ---- The chapter header is the BOARD'S CAPTION ----
   Ed, 2026-08-28, option 3 of docs/chapter-header-mock.html: "I love 3". The
   move and the answer were loose text on the paper between two cards, and the
   board above them was the only thing on this page not in a card. Now they are
   one object: the position, the move that made it, and what you play back.

   Two labels, "Their move" and "Your answer". Nothing on the old row said which
   move was whose — a stranger read "2...Nc6 → 3.Bc4" and had to work it out.
   2026-08-25 removed a lone "Your answer" label for good reason ("the arrow says
   what the chip is"); it is the PAIR that carries the meaning, so re-adding one
   without the other would be going backwards.

   The moves are plain mono, not chips: with a label above each, a chip on one
   side and bare text on the other made them look like different kinds of thing.
   Colour is what separates them now — ink for theirs, green for yours. */
.rp-chpage-boardcard {
  background: var(--ln-card); margin-bottom: 12px;
  border: 1px solid var(--ln-border-soft); border-radius: 16px;
  box-shadow: var(--ln-shadow-sm); overflow: hidden;
}
/* The card carries the frame now, so the board inside it must not draw a second
   one — a rounded border inside a rounded border reads as a board pasted on. */
.rp-chpage-boardcard .rp-chpage-board { border: 0; border-radius: 0; box-shadow: none; }
/* SIDE BY SIDE AT EVERY WIDTH, phone included (Ed, 2026-08-28: "I want it side
   by side"). The two blocks each take half the row and NEVER wrap below one
   another — `flex: 1 1 0` plus `min-width: 0` is what makes that safe, because a
   long name then wraps inside its own half instead of shoving the answer onto a
   line of its own. Left to size themselves the halves overflowed and the whole
   answer dropped, which measured 131px on a phone against the 86px the old loose
   row took. Top-aligned so the two eyebrows sit on one line however many lines
   the names below them run to. */
.rp-chpage-cap {
  display: flex; align-items: flex-start; gap: 8px; flex-wrap: nowrap;
  padding: 9px 12px 10px; border-top: 1px solid var(--ln-border-soft);
}
.rp-chpage-capcol { display: flex; flex-direction: column; min-width: 0; }
.rp-chpage-cap > .rp-chpage-capcol { flex: 1 1 0; }
.rp-chpage-capeye {
  font-size: 0.6rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.1em; color: var(--ln-faint); white-space: nowrap;
}
.rp-chpage-cap .rp-chpage-move { font-size: 1.15rem; margin: 0; line-height: 1.2; }
.rp-chpage-cap .rp-chpage-tag { font-size: 0.8rem; margin: 0; line-height: 1.3; }
/* Sits on the MOVE's line, not the block's middle: 15px clears the eyebrow above
   it, and the rest centres it on the 22px move. Centring it in the block instead
   drifts whenever one name wraps and the other doesn't. */
.rp-chpage-arrow {
  flex: none; color: var(--ln-faint); font-size: 1rem; line-height: 1; margin-top: 18px;
}
/* Half the row, same as their move, with Change hard right inside it so Change
   sits at the caption's right edge without leaving the tap target. */
/* nowrap is load-bearing: `.rpc-answer` sets flex-wrap: wrap, so Change fell to a
   line of its own below the answer instead of sitting beside it. It rides on the
   eyebrow's line now, which reads as a heading for the half it changes. */
.rp-chpage-answer {
  width: auto; flex: 1 1 0; min-width: 0; margin-top: 0;
  flex-direction: row; flex-wrap: nowrap; align-items: flex-start;
  text-align: left; gap: 6px;
  padding: 0; background: none;
  /* Keeps .rpc-answer's own wrapping (Ed, 2026-08-25: "keep wrapping. I don't
     want ellipses instead of words"). */
}
.rp-chpage-answer > .rp-chpage-capcol { flex: 1 1 auto; min-width: 0; }
/* Centred on the answer block rather than pinned to its top eyebrow (Ed,
   2026-08-28: "center the change button"), so it sits beside the move it changes. */
.rp-chpage-answer .rpc-answer-change { margin-left: auto; flex: none; align-self: center; }
/* Matched to their move opposite it, so the two read as one pair. */
.rp-chpage-cap .rpc-move {
  padding: 0; background: none; border: 0; border-radius: 0;
  font-size: 1.15rem; line-height: 1.2;
}
.rp-chpage-cap .rpc-answer-title {
  font-family: var(--ln-serif); font-size: 0.8rem; line-height: 1.3; color: var(--ln-dim);
}
/* A phone has about 360px for the whole pair, so the type steps down a notch to
   keep both halves side by side. Only the words shrink; the layout is the same
   one the laptop gets. */
@media (max-width: 760px) {
  .rp-chpage-cap { gap: 6px; padding: 8px 10px 9px; }
  .rp-chpage-cap .rp-chpage-move, .rp-chpage-cap .rpc-move { font-size: 1.05rem; }
  .rp-chpage-cap .rp-chpage-tag, .rp-chpage-cap .rpc-answer-title { font-size: 0.75rem; }
  .rp-chpage-cap .rpc-answer-change { font-size: 0.6rem; }
  .rp-chpage-arrow { font-size: 0.85rem; margin-top: 16px; }
}

/* ============================================================
   THE ANSWER SCREEN'S OPTION LIST  (2026-08-02)
   Five stacked cards, each carrying a paragraph, put the board out of sight by
   the third option — and every card's colour refers to an arrow drawn on that
   board. So each option collapses to one row, and the tap that selects it is
   the tap that opens its story. Measured on the Caro-Kann as White (5 options,
   430x932): the panel drops from 851px to 553px, the page from 1375px to 1077px,
   and the head row stops wrapping (45px to 23px on the recommended card).
   Scoped to .rp-options-acc, which only the answer screen renders — the story
   walk's decision screen and the two deep-dive pickers are untouched.
   ============================================================ */
.rp-options.rp-options-acc { gap: 8px; }

.rp-options-acc .rp-option {
  padding: 9px 12px;
  background: linear-gradient(180deg, var(--bg-rp-option), var(--bg));
  border: 1px solid var(--ln-border-soft);
  border-radius: 14px;
  box-shadow: var(--ln-shadow-sm), inset 0 1px 0 var(--shadow-ms-tile);
  /* Opening a card scrolls it into view; without this the fixed tab bar sits on
     top of the "Make this my move" button it just revealed. */
  scroll-margin-bottom: calc(62px + env(safe-area-inset-bottom, 0px));
}
.rp-options-acc .rp-option.selected {
  border-color: var(--ln-green);
  background: linear-gradient(180deg, var(--bg-rp-optionselected), var(--bg-rp-optionselected-2));
  box-shadow: var(--ln-shadow-md), inset 0 1px 0 var(--shadow-ms-tile);
}

/* One row, never two. The title takes the slack and ellipsises rather than
   wrapping, so a long variation name can't make a collapsed card grow. */
.rp-options-acc .rp-option-head {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 9px;
  min-width: 0;
}
/* On the answer screen the move wears its own arrow's colour (set inline), so
   card and board are the same thing twice. The other three decision screens
   draw one arrow for the selected move only, with no colour to match, so they
   fall back to this. */
.rp-options-acc .rp-option-san {
  flex: none;
  background: var(--accent2);
  color: var(--ink-rp-danger-btn);
  font-family: var(--ln-ui);
  font-weight: 800;
  font-size: 0.82rem;
  line-height: 1;
  padding: 5px 8px;
  border-radius: 7px;
  box-shadow: inset 0 -1px 0 var(--shadow-rep-info-banner);
}
.rp-options-acc .rp-option-title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 0.88rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Collapsed, a long name is trimmed rather than allowed to wrap the row. Open,
   it wraps and reads in full, so the ellipsis is never the last word on it. */
.rp-options-acc .rp-option.selected .rp-option-title { white-space: normal; }
.rp-options-acc .rp-option-eval { flex: none; margin-left: 0; font-size: 0.76rem; }
/* The caret is a promise that there is more to read, so only a card carrying
   prose gets one. The suggestion cards have a single line of stats and nothing
   to open. */
.rp-options-acc .rp-option.has-story .rp-option-head::after {
  content: '';
  flex: none;
  width: 7px;
  height: 7px;
  border-right: 2px solid var(--ln-faint);
  border-bottom: 2px solid var(--ln-faint);
  transform: translateY(-2px) rotate(45deg);
  transition: transform 0.18s;
}
.rp-options-acc .rp-option.has-story.selected .rp-option-head::after {
  border-color: var(--ln-green-deep);
  transform: translateY(2px) rotate(-135deg);
}

/* Badges and the line count share one quiet line, so neither pushes the other
   onto a row of its own (RECOMMENDED used to wrap under the title). */
.rp-options-acc .rp-option-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 5px;
}
.rp-options-acc .rp-option-badge { margin: 0; font-size: 0.6rem; padding: 2px 7px; }
.rp-options-acc .rp-option-lines { font-size: 0.72rem; color: var(--ln-dim); }

/* The prose is what the tap is for. */
.rp-options-acc .rp-option-story,
.rp-options-acc .rp-option-note { display: none; }
.rp-options-acc .rp-option.selected .rp-option-story,
.rp-options-acc .rp-option.selected .rp-option-note { display: block; }
.rp-options-acc .rp-option.selected .rp-option-story { margin-top: 9px; }
.rp-options-acc .rp-option-note {
  margin-top: 7px;
  font-family: var(--ln-serif);
  font-size: 0.74rem;
  line-height: 1.5;
  color: var(--ln-dim);
}

/* The board used to pin here while the options scrolled under it. Removed
   2026-08-02 at Ed's call: scrolling down should just scroll down. The accordion
   above is what made that fine — the panel went from 851px to 553px, and opening
   a card leaves a single arrow on the board, so there is little left to hold in
   view. Everything the pinning needed went with it: the one-frame overflow-x
   clip that made sticky possible at all, the display:contents that kept the nav
   out of it, and the tightened row gap. */

@media (prefers-reduced-motion: reduce) {
  .rp-options-acc .rp-option.has-story .rp-option-head::after { transition: none; }
}

/* ============================================================
   BREADCRUMB HEADERS  (Repertoire builder + Train, 2026-08-02)
   The old header was a 76px card with a centred two-line title and a Back pill.
   It said the right things in the wrong shape: "Caro-Kann Defense · as White ·
   Black plays 3...c5 · vs 3...c5" is a path, so it's a breadcrumb trail now,
   one line, each step tappable. Scoped to .rep-crumb-bar because the lesson
   player shares .rep-info-banner and keeps its card: its banner is the chapter
   picker, not a location header, and it already has a trail of its own above it.
   ============================================================ */
.rep-info-banner.rep-crumb-bar {
  background: none;
  border: none;
  box-shadow: none;
  min-height: 0;
  padding: 2px 2px 4px;
  gap: 0;
}
.rep-crumb-bar .rep-banner-center { flex: 1; text-align: left; min-width: 0; }
.rep-crumb-bar .book-crumbs { margin: 0; }
/* The trail replaced Back, so the pill only survives on the setup walk, which
   drops the banner (and with it the trail) entirely. Every other screen has to
   put a tappable crumb first or there'd be no way out at all. */
.rep-crumb-bar:not(.rep-info-banner-bare) #rep2-builder-back { display: none; }
/* The detected-opening line under Train's trail keeps its own spacing. */



/* Two per row. The deep dive's prepared replies (Ed, 2026-08-09) and, since
   2026-09-15, the line builder's own move choices ("Can the cards be half the
   column instead of the whole line?"). Same reason both times: those cards carry a
   move and a number or two and nothing else, so a full-width card is mostly empty
   and four or six of them run off the screen. The CHAPTER ANSWER screen keeps one
   per row — its cards hold a name as well as an eval and a line count, and open to
   show prose. Overrides .rp-options' column flex, so it has to come after it, and
   after .rp-options-acc, which it shares a specificity with. */
.rp-options.rp-options-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  /* Grid's default stretch is wanted here: at half width "played in 12% of
     games" runs to two lines while a card with no percentage has one, and
     without it the two cards in a row end up different heights. */
}
/* A grid item's default min-width is its CONTENT, so one long name pushes its column
   past 1fr and the row runs off the screen — which is exactly what happened on the
   chapter answer's first build (Ed, 2026-09-15: "it shouldn't get cut off no matter
   what"). Everything that can hold a long word needs the floor. */
.rp-options-pair .rp-option,
.rp-options-pair .rp-option-head { min-width: 0; }

/* THE MOVE LINE, THEN THE NAME UNDER IT (Ed, 2026-09-15: "move the name directly
   under the move line and keep the eval tied to the move line so the name can go
   under it too"). Side by side, the chip and a wrapping name fight over ~110px and
   the eval ends up centred against three lines of text. The DOM order is chip, name,
   eval, so `order` puts the eval back on the move line and the name takes a row of
   its own. */
.rp-options-pair .rp-option-head { flex-wrap: wrap; }
.rp-options-pair .rp-option-eval { order: 1; margin-left: auto; }
.rp-options-pair .rp-option-title {
  order: 2;
  flex: 1 0 100%;
  /* Wrapping rather than trimmed: at half width an ellipsis eats most of the name,
     and the name is what you are choosing between. */
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}
/* One line, at any card width (Ed, 2026-09-15). "Choose this move" over two lines
   made the open card taller than its neighbour for no reason. */
.rp-options-pair .rp-choose-btn { white-space: nowrap; padding-left: 6px; padding-right: 6px; }

/* ============================================================
   TRAIN SETUP — the opening card (2026-08-11)
   One card is one opening, both sides. The board is the top of the card
   instead of the whole card, and the body underneath carries the side, the
   name and the counts. Shared by the main grid and by Customize; `sel` and
   the tick are the only things Customize adds. See
   docs/train-tab-redesign-plan.md.
   ============================================================ */
.ms-sec-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; max-width: 820px; margin: 0 auto 10px;
}
.ms-sec-head + .ms-oc-grid, .ms-sec-head + .ms-mode-row { margin-top: 0; }
.ms-sec-title {
  font-family: var(--ln-serif); font-size: 1.22rem; font-weight: 700;
  margin: 0; color: var(--ln-ink);
}
.ms-sec-link {
  font: inherit; font-family: var(--ln-ui); font-size: 0.8rem; font-weight: 650;
  color: var(--ln-dim); background: none; border: 0; padding: 4px 2px;
  cursor: pointer; text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: var(--ln-border-soft);
}
.ms-sec-link:hover { color: var(--ln-green-deep); }
.ms-sec-head + .ms-mode-row { margin-bottom: 18px; }

/* 190px, four across on a laptop (Ed's pick, 2026-08-11). The 820px cap and
   the centring are the page's own, unchanged. */
.ms-oc-grid { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); }

.ms-oc-card {
  position: relative; display: flex; flex-direction: column; gap: 0;
  padding: 0; text-align: left; font: inherit; overflow: hidden; cursor: pointer;
  background: var(--ln-card); border: 1px solid var(--ln-border-soft);
  border-radius: 16px; box-shadow: var(--ln-shadow-sm); color: var(--ln-ink);
  transition: transform .13s ease, border-color .13s ease, box-shadow .13s ease;
}
.ms-oc-card:hover { transform: translateY(-3px); border-color: var(--ln-green); box-shadow: var(--ln-shadow-md); }
.ms-oc-card:focus-visible { outline: 2px solid var(--ln-green); outline-offset: 2px; }
.ms-oc-card.sel { border-color: var(--ln-green); box-shadow: 0 0 0 2px var(--ln-green), var(--ln-shadow-sm); }
.ms-oc-card .ms-oc-board { border-bottom: 1px solid var(--ln-border-soft); border-radius: 0; }
.ms-oc-board img, .ms-oc-board svg { display: block; width: 100%; height: auto; }
.ms-oc-body { display: flex; flex-direction: column; gap: 5px; padding: 9px 11px 11px; flex: 1; }
.ms-oc-top { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.ms-oc-eyebrow {
  font-size: 0.63rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ln-dim);
}
.ms-oc-name { font-family: var(--ln-serif); font-weight: 700; font-size: 0.95rem; line-height: 1.18; }
.ms-oc-foot { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; margin-top: auto; }

/* A lighter chip inside these cards. The shared .rp-chip tint measured 3.62:1
   here, below the app's own 4.0 house level; on a lighter ground the same ink
   clears it. Scoped, so .rp-chip everywhere else is untouched. */
.ms-oc-card .rp-chip { background: var(--bg-rp-act-badge-2); }

/* The star sits in the body now, not floating over the board. */
.ms-oc-card .ms-oc-star {
  position: static; width: auto; height: auto; background: none; box-shadow: none;
  font-size: 1rem; line-height: 1; color: var(--ln-faint); border: 0;
  padding: 0 2px; cursor: pointer;
}
.ms-oc-card .ms-oc-star.on { color: var(--ln-amber-ink); }

/* Train's empty states: a board and the tap that fixes them, in place of a
   sentence with nothing to act on. Same card furniture as the openings home's
   start card, one tab over. */
.ms-emptycard {
  max-width: 380px; margin: 16px auto 0;
  background: var(--ln-card); border: 1px solid var(--ln-border-soft);
  border-radius: 18px; box-shadow: var(--ln-shadow-sm); overflow: hidden;
}
.ms-emptycard-board { max-width: 240px; margin: 16px auto 0; }
.ms-emptycard-board img, .ms-emptycard-board svg { display: block; width: 100%; height: auto; }
.ms-emptycard-body { display: flex; flex-direction: column; gap: 11px; padding: 13px 18px 18px; }
.ms-emptycard-line {
  margin: 0; text-align: center; line-height: 1.5;
  font-size: 0.86rem; color: var(--ln-dim);
}
.ms-emptycard-body .btn { width: 100%; justify-content: center; }

/* "Train everything" is a bar above the grid, not a card in it. */
.ms-train-all {
  display: flex; align-items: center; gap: 13px; width: 100%; text-align: left;
  max-width: 820px; margin: 0 auto 12px; padding: 14px 16px;
  border-radius: 16px; cursor: pointer; font: inherit;
  background: linear-gradient(180deg, var(--bg-rp-prep-segready), var(--ln-green));
  border: 1px solid var(--ln-green-deep); color: var(--ink-btn-primary);
  box-shadow: 0 3px 12px var(--shadow-btn-primary), inset 0 1px 0 var(--shadow-btn-primary-2);
  transition: transform .13s ease, box-shadow .16s ease;
}
.ms-train-all:hover { transform: translateY(-2px); box-shadow: 0 8px 20px var(--shadow-btn-primary), inset 0 1px 0 var(--shadow-btn-primary-2); }
.ms-train-all:active { transform: translateY(0); }
.ms-train-all:focus-visible { outline: 2px solid var(--ln-green-deep); outline-offset: 2px; }
.ms-train-all-ic { font-size: 1.45rem; line-height: 1; }
.ms-train-all-tx { flex: 1; min-width: 0; }
.ms-train-all-tx b { display: block; font-family: var(--ln-serif); font-size: 1.08rem; font-weight: 700; }
.ms-train-all-tx span { font-size: 0.78rem; opacity: 0.9; }
.ms-train-all-go { font-size: 1.15rem; opacity: 0.9; }

/* Customize: the tick rides on the board's corner so the card stays one target. */
.ms-oc-pick {
  position: absolute; top: 8px; right: 8px; width: 25px; height: 25px;
  border-radius: 50%; border: 1.5px solid var(--ln-border);
  background: var(--bg-rp-act-badge-2); box-shadow: 0 1px 3px var(--shadow-header);
  display: flex; align-items: center; justify-content: center;
  font-size: 0.82rem; color: transparent;
}
.ms-oc-card.sel .ms-oc-pick { background: var(--ln-green); border-color: var(--ln-green-deep); color: var(--ink-rp-danger-btn); }

/* Side buttons, only on an opening with lines on both sides. */
.ms-sides { display: flex; gap: 5px; width: 100%; }
.ms-sidebtn {
  flex: 1; min-width: 0; font-family: var(--ln-ui); font-size: 0.68rem; font-weight: 700;
  padding: 5px 4px; border-radius: 9px; cursor: pointer; text-align: center;
  border: 1px solid var(--ln-border-soft); background: var(--bg-button-3);
  color: var(--ln-dim); display: flex; align-items: center; justify-content: center; gap: 4px;
}
.ms-sidebtn small { font-weight: 400; font-size: 0.62rem; opacity: 0.75; }
.ms-sidebtn:hover { border-color: var(--ln-green); color: var(--ln-ink); }
.ms-sidebtn.on {
  background: var(--ln-green); border-color: var(--ln-green-deep); color: var(--ink-btn-primary);
}
.ms-sidebtn.on:hover { color: var(--ink-btn-primary); }

/* The tally + Start, outside the scrolling pane. */
#master-startbar:empty { display: none; }
#master-startbar {
  flex: 0 0 auto; padding: 10px 36px 12px;
  border-top: 1px solid var(--ln-border-soft); background: var(--bg);
}
#master-startbar .ms-startbar { position: static; margin: 0 auto; }
/* On a phone the PAGE scrolls, not the pane, so a bar merely placed after the
   pane lands below the fold — measured at top 1158 in a 932 viewport. Pin it
   above the tab bar instead, and give the pane room so the last cards are not
   hidden behind it. */
@media (max-width: 760px) {
  #master-startbar:not(:empty) {
    position: fixed; left: 0; right: 0; z-index: 90;
    bottom: calc(54px + env(safe-area-inset-bottom, 0px));
    padding: 10px 14px 12px;
  }
  #master-view:has(#master-startbar:not(:empty)) #master-setup { padding-bottom: 150px; }
}

/* ── DOORS (step 6) ──────────────────────────────────────────────────────────
   The openings list is grouped under your first move: a wide card for the door,
   then the openings you have built inside it. Same surface, border and radius as
   an opening tile, laid out as a row so the group reads as a heading with its
   contents under it rather than as another tile. */
.rp-door-group { margin-bottom: 22px; }
.rp-door {
  display: flex; overflow: hidden; cursor: pointer;
  border: 1px solid var(--ln-border-soft); border-radius: 18px;
  background: var(--ln-card); box-shadow: var(--ln-shadow-sm);
  margin-bottom: 12px;
}
.rp-door:hover { border-color: var(--ln-green); box-shadow: var(--ln-shadow-md); }
.rp-door-board { flex: none; width: 112px; border-right: 1px solid var(--ln-border-soft); }
.rp-door-board img, .rp-door-board svg { display: block; width: 100%; height: auto; }
.rp-door-tx {
  padding: 11px 14px; min-width: 0;
  display: flex; flex-direction: column; justify-content: center; gap: 2px;
}
.rp-door-kicker {
  font-size: 0.62rem; font-weight: 800; letter-spacing: 0.11em;
  text-transform: uppercase; color: var(--ln-green-deep);
}
.rp-door-move { font-family: var(--ln-serif); font-size: 1.3rem; font-weight: 700; line-height: 1.1; }
.rp-door-sub { font-size: 0.75rem; color: var(--ln-dim); line-height: 1.4; }
.rp-door-go { font-size: 0.77rem; font-weight: 700; color: var(--ln-green); margin-top: 3px; }
/* What is inside a door, on the openings home: the replies people play here, as
   one row that scrolls sideways (Ed, 2026-09-05: "all the other chapters should be
   under the e4 chapter as scrollable chapters"). It scrolls rather than wrapping
   because 1.e4 has eleven of them, and three rows of tiles under every door would
   be most of the screen — worse as Black, which has one door per first move White
   plays at you. The tile is the ordinary opening tile, given a fixed width so the
   row can scroll and so a long name cannot stretch one card wider than the rest. */
.rp-door-strip {
  display: flex; gap: 10px; overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch;
  padding: 2px 2px 8px; margin: 0 -2px 4px;
  scrollbar-width: thin;
}
.rp-door-strip .rp-door-reply {
  flex: 0 0 150px; scroll-snap-align: start; padding: 10px 12px; gap: 6px;
}
.rp-door-strip .rp-open-tile-name { font-size: 0.9rem; }
.rp-door-strip .rp-open-tile-status { font-size: 0.7rem; }
.rp-door-strip .rp-open-tile-board { margin: -2px 0 0; }
/* A first move you face and have no answer to: still a door, plainly empty. */
.rp-door.is-empty { border-style: dashed; }
.rp-door.is-empty .rp-door-move, .rp-door.is-empty .rp-door-kicker { color: var(--ln-dim); }
/* The trail above a door's page: every door you came through, outermost first.
   It stops at the last position that is still a door, because an opening with
   its own page is a destination rather than a step. */
.rp-door-trail { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; margin: 0 0 10px; }
.rp-door-crumb {
  border: 0; background: none; cursor: pointer; padding: 2px 0;
  font-family: var(--ln-ui); font-size: 0.78rem; font-weight: 600; color: var(--ln-green);
}
.rp-door-crumb:hover { text-decoration: underline; }
.rp-door-sep { color: var(--ln-dim); font-size: 0.78rem; }

/* ══════════════════════════════════════════════════════════════════════════
   THE HOME: START, THEN OPENINGS (2026-09-05, design 1 + build 1 + change 1).
   One slim top row, then one of: the day-one card, or the moves card + your
   openings as covers + the next reply to meet. The moves board is the same
   section in another state. The page holds no list of moves.
   ══════════════════════════════════════════════════════════════════════════ */
.rp-hm-top { max-width: 760px; margin: 0 auto 12px; display: flex; align-items: center; gap: 9px; padding: 0 2px; }
.rp-hm-ic {
  width: 32px; height: 32px; flex: none; border-radius: 50%; border: 1px solid var(--ln-border-soft);
  background: var(--bg-btn-ghost); display: grid; place-items: center; color: var(--ln-dim);
  font: inherit; font-size: 0.95rem; cursor: pointer; padding: 0;
}
.rp-hm-ic.is-blank { visibility: hidden; }
.rp-hm-ic:hover { border-color: var(--ln-green); color: var(--ln-ink); }
.rp-hm-who { flex: 1; min-width: 0; font-family: var(--ln-serif); font-size: 1.15rem; font-weight: 700; color: var(--ln-ink); }
.rp-hm-eb { font-size: 0.62rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.09em; color: var(--ln-dim); }
.rp-hm-eb.is-green { color: var(--ln-green-deep); }
.rp-hm-sec { max-width: 760px; margin: 16px auto 8px; padding: 0 2px; font-size: 0.62rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.09em; color: var(--ln-dim); }
.rp-hm-note { font-size: 0.78rem; color: var(--ln-dim); line-height: 1.45; }
.rp-hm-note.is-center { text-align: center; margin: 10px auto 0; max-width: 760px; }
.rp-hm-link { background: none; border: 0; padding: 0; font: inherit; font-size: inherit; color: var(--ln-green-deep); font-weight: 700; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.rp-hm-btn { padding: 8px 14px; font-size: 0.82rem; border-radius: 999px; }
.rp-hm-btn.is-wide { width: 100%; }
/* The day-one card is gone (2026-09-05): the first move goes to the app's own
   answer screen, which already has the arrows and the option cards. */
/* The moves card: your first move, the door to the board. */
.rp-hm-moves {
  max-width: 760px; margin: 0 auto 12px; width: 100%; display: flex; align-items: center; gap: 11px;
  padding: 8px 12px 8px 10px; border-radius: 14px; background: var(--ln-green-tint);
  border: 1px solid rgba(62, 120, 24, 0.3); font: inherit; color: var(--ln-ink); text-align: left; cursor: pointer;
}
.rp-hm-moves:hover { border-color: var(--ln-green); }
.rp-hm-moves-board { flex: 0 0 58px; width: 58px; border-radius: 5px; overflow: hidden; }
.rp-hm-moves-board img, .rp-hm-moves-board svg { display: block; width: 100%; height: auto; }
.rp-hm-moves-tx { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.rp-hm-moves-mv { font-family: ui-monospace, Menlo, monospace; font-weight: 700; font-size: 1.1rem; }
.rp-hm-moves-sub { font-size: 0.74rem; color: var(--ln-dim); }
.rp-hm-chev { color: var(--ln-green-deep); font-size: 1.3rem; flex: none; }
/* THE CONTINUE SHELF (Ed, 2026-09-08, option 5 of docs/openings-home-mock.html).
   It scrolls SIDEWAYS on purpose: that is what makes it a shelf rather than a
   second grid competing with the one below, and about three and a half tiles show
   at 430px so it reads as having more in it without needing to be dragged. The
   edge span is a scroll-end spacer -- padding-right on a flex scroller is ignored
   at the end of the content in several engines. */
.rp-hm-shelf {
  max-width: 760px; margin: 0 auto; display: flex; gap: 9px;
  overflow-x: auto; -webkit-overflow-scrolling: touch; padding-bottom: 4px;
  scrollbar-width: none;
}
.rp-hm-shelf::-webkit-scrollbar { display: none; }
.rp-hm-shelf-edge { flex: 0 0 2px; }
.rp-hm-tile {
  flex: 0 0 108px; display: flex; flex-direction: column; gap: 4px; padding: 7px;
  text-align: left; font: inherit; cursor: pointer; color: var(--ln-ink);
  background: var(--ln-card); border: 1px solid var(--ln-green); border-radius: 12px;
  box-shadow: var(--ln-shadow-sm);
  transition: transform .13s ease, box-shadow .13s ease;
}
.rp-hm-tile:hover { transform: translateY(-2px); box-shadow: var(--ln-shadow-md); }
.rp-hm-tile-board { display: block; }
.rp-hm-tile-board img, .rp-hm-tile-board svg { display: block; width: 100%; height: auto; border-radius: 4px; }
.rp-hm-tile-nm { font-family: var(--ln-serif); font-weight: 700; font-size: 0.76rem; line-height: 1.15; }
.rp-hm-tile-when { font-size: 0.6rem; color: var(--ln-faint); }
@media (min-width: 761px) { .rp-hm-tile { flex-basis: 132px; } }

/* Covers. THREE TO A ROW (Ed, 2026-09-08, option 3 of the same mockup): the list
   is a ranking now, so seeing more of it at once is the point, and a card carries
   two short facts rather than three. */
.rp-hm-grid { max-width: 760px; margin: 0 auto; display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
@media (min-width: 761px) { .rp-hm-grid { grid-template-columns: repeat(4, 1fr); gap: 11px; } }
.rp-hm-cover {
  display: flex; flex-direction: column; padding: 0; text-align: left; font: inherit; cursor: pointer; color: var(--ln-ink);
  background: var(--ln-card); border: 1px solid var(--ln-border-soft); border-radius: 11px; overflow: hidden; box-shadow: var(--ln-shadow-sm);
  transition: transform .13s ease, border-color .13s ease, box-shadow .13s ease;
}
.rp-hm-cover:hover { transform: translateY(-3px); border-color: var(--ln-green); box-shadow: var(--ln-shadow-md); }
.rp-hm-cover-board { display: block; padding: 7px 7px 0; }
.rp-hm-cover-board img, .rp-hm-cover-board svg { display: block; width: 100%; height: auto; border-radius: 4px; }
.rp-hm-cover-tx { display: flex; flex-direction: column; gap: 3px; padding: 6px 8px 9px; }
.rp-hm-cover-nm { font-family: var(--ln-serif); font-weight: 700; font-size: 0.79rem; line-height: 1.15; }
.rp-hm-cover-sub { font-size: 0.64rem; color: var(--ln-dim); }
.rp-hm-cover-sub b { color: var(--ln-green-deep); }
@media (min-width: 761px) {
  .rp-hm-cover-nm { font-size: 0.92rem; }
  .rp-hm-cover-sub { font-size: 0.72rem; }
}
/* The next reply to meet. */
.rp-hm-nextwrap { max-width: 760px; margin: 12px auto 0; }
.rp-hm-next { background: var(--ln-card); border: 1px solid var(--ln-green); box-shadow: 0 0 0 1px var(--ln-green), var(--ln-shadow-sm); border-radius: 16px; overflow: hidden; }
.rp-hm-next-main { display: flex; align-items: stretch; }
.rp-hm-next-board { flex: 0 0 42%; max-width: 42%; }
.rp-hm-next-board img, .rp-hm-next-board svg { display: block; width: 100%; height: 100%; object-fit: cover; }
.rp-hm-next-tx { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: center; gap: 4px; padding: 12px 13px; }
.rp-hm-next-mv { font-family: ui-monospace, Menlo, monospace; font-weight: 700; font-size: 1.25rem; }
.rp-hm-next-pc { font-family: inherit; font-weight: 400; font-size: 0.76rem; color: var(--ln-dim); }
.rp-hm-next-nm { font-family: var(--ln-serif); font-weight: 700; font-size: 1.02rem; }
.rp-hm-next-tx .rp-hm-btn { align-self: flex-start; margin-top: 6px; }
.rp-hm-next-foot {
  display: block; width: 100%; text-align: left; padding: 8px 13px; border: 0; border-top: 1px solid var(--ln-border-soft);
  background: none; font: inherit; font-size: 0.76rem; font-weight: 700; color: var(--ln-green-deep); cursor: pointer;
}
.rp-hm-add {
  width: 100%; display: grid; place-items: center; gap: 4px; padding: 16px; border-radius: 16px; cursor: pointer;
  border: 1px dashed rgba(62, 120, 24, 0.5); background: none; font: inherit; color: var(--ln-green-deep);
}
.rp-hm-add-plus { font-size: 1.6rem; line-height: 1; }
.rp-hm-add-tx { font-family: var(--ln-serif); font-weight: 700; font-size: 0.95rem; }
/* "HOW YOU START": ONE CARD PER MOVE (Ed, 2026-09-07, option 4 of
   docs/how-you-start-mock.html). It was four rows of small text with four identical
   "Change" links, on boards too small to read. There is exactly one thing to do on this
   screen, so the whole card does it, and the opening your move makes is a green badge:
   the page reads as your openings rather than as four settings. */
.rp-hm-ocards { max-width: 760px; margin: 0 auto; display: flex; flex-direction: column; gap: 10px; }
.rp-hm-ocard {
  display: grid; grid-template-columns: 96px 1fr; gap: 12px; align-items: center;
  width: 100%; padding: 10px; text-align: left; font: inherit; color: var(--ln-ink); cursor: pointer;
  background: var(--ln-card); border: 1px solid var(--ln-border-soft); border-radius: 16px;
  box-shadow: var(--ln-shadow-sm);
  transition: transform .13s ease, border-color .13s ease, box-shadow .13s ease;
}
.rp-hm-ocard:hover { transform: translateY(-2px); border-color: var(--ln-green); box-shadow: var(--ln-shadow-md); }
.rp-hm-ocard-bd { display: block; border-radius: 8px; overflow: hidden; }
.rp-hm-ocard-bd img, .rp-hm-ocard-bd svg { display: block; width: 100%; height: auto; }
.rp-hm-ocard-tx { min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
.rp-hm-ocard-flow { display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font-family: ui-monospace, Menlo, monospace; font-size: 0.95rem; }
.rp-hm-ocard-them { color: var(--ln-dim); }
.rp-hm-ocard-arr { color: var(--ln-faint); }
.rp-hm-ocard-you { font-weight: 700; color: var(--ln-green-deep); }
.rp-hm-ocard-badge { font-family: var(--ln-serif); font-weight: 700; font-size: 0.84rem;
  background: var(--ln-green-tint); color: var(--ln-green-deep); border-radius: 8px; padding: 3px 9px; }
.rp-hm-ocard-foot { font-size: 0.72rem; color: var(--ln-faint); }
/* On a laptop the card is 760px wide, so a 96px board floats in a lot of empty card. */
@media (min-width: 761px) {
  .rp-hm-ocard { grid-template-columns: 128px 1fr; gap: 16px; padding: 12px; }
}
/* "Replies to prepare": what is LEFT and how often it comes up, which is a
   different question from changing a move and gets its own screen (Ed, 2026-09-05,
   option C of docs/choose-reply-mock.html). */
/* THE TILE FROM THE OPENINGS LIST, ONE LEVEL IN (Ed, 2026-09-05, option B of
   docs/prepare-restyle-mock.html). Each reply is the position it makes, its name
   and its share, two across, so the whole list fits one screen. It replaced a
   column of wide rows that each carried their own green Prepare button, seven
   times over, which made nothing on the screen more worth doing than anything
   else. The tile IS the button now. */
/* On a laptop the card is 760px wide, so 42% of it is a 320px board towering over
   three lines of text. A fixed width keeps the card the height of its words. */
@media (min-width: 761px) {
  .rp-hm-next-board { flex: 0 0 200px; max-width: 200px; }
}

/* THE FIRST MOVE HAS NO NUMBERS (Ed, 2026-09-05). The eval bar reads "White edge"
   before anyone has moved, and the four moves sit within a tenth of a pawn of each
   other, so it separates nothing. Scoped to that one question by rpRenderSection. */
.rp-firstmove #rw-evalwrap { display: none; }
.rp-question-big {
  display: block; font-family: var(--ln-serif); font-size: 1.35rem; font-weight: 700;
  line-height: 1.2; color: var(--ln-ink);
}

/* ── THE FIRST MOVE IS A LIST, NOT FOUR BOXES ────────────────────────────────
   Ed, 2026-09-14, picking treatment 2 of docs/first-move-mock.html: "Let's do 2."

   Everywhere else the answer screen's cards are boxes because they are things to
   compare — a score, a covered count, a story to open. This question has none of
   that by its own rule (no numbers, fixed order), so four boxes were four frames
   drawn around four words. One panel with hairlines between the rows says the same
   thing with less furniture, and the colour that ties a row to its arrow moves from
   a chip to a rail, which leaves the move itself free to be written the way the
   rest of the app writes a move: 1. e4.

   Scoped to .rp-firstmove, set by rpRenderSection on #rep2-builder, so every other
   answer screen in the app is untouched. Three classes deep on purpose: each rule
   here has to outrank the .rp-options-acc pair it replaces. */
.rp-firstmove .rp-options.rp-options-acc { gap: 0; }
.rp-firstmove .rp-options-acc .rp-option {
  position: relative;
  padding: 12px 4px 12px 13px;
  background: none;
  border: none;
  border-bottom: 1px solid var(--ln-border-soft);
  border-radius: 0;
  box-shadow: none;
}
.rp-firstmove .rp-options-acc .rp-option::before {
  content: '';
  position: absolute; left: 0; top: 6px; bottom: 6px;
  width: 4px; border-radius: 0 3px 3px 0;
  background: var(--rp-opt-dot, var(--ln-green));
}
.rp-firstmove .rp-options-acc .rp-option.selected {
  background: var(--ln-green-tint);
  box-shadow: none;
}
/* The move, in its arrow's colour, on a fixed measure so the names line up under
   each other. Wide enough for "1. Nf3" without wrapping. */
.rp-firstmove .rp-options-acc .rp-option-san {
  flex: none;
  width: 58px;
  padding: 0;
  background: none;
  box-shadow: none;
  color: var(--rp-opt-dot, var(--ln-green));
  font-family: var(--ln-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 0.92rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.rp-firstmove .rp-options-acc .rp-option-title { font-size: 0.95rem; }
/* The line or two about the move lines up under the opening's NAME, not under the
   rail, so an open row reads as one block rather than two columns. 58px of move
   plus the head row's 9px gap. */
.rp-firstmove .rp-options-acc .rp-option-story {
  margin-left: 67px;
  font-size: 0.82rem;
  color: var(--ln-dim);
}
/* Every row opens, so every row gets the caret — the .has-story rule above draws
   one only for a card carrying prose, and these carry none. */
.rp-firstmove .rp-options-acc .rp-option .rp-option-head::after {
  content: '';
  flex: none;
  width: 7px; height: 7px;
  border-right: 2px solid var(--ln-faint);
  border-bottom: 2px solid var(--ln-faint);
  transform: translateY(-2px) rotate(45deg);
  transition: transform 0.18s;
}
.rp-firstmove .rp-options-acc .rp-option.selected .rp-option-head::after {
  border-color: var(--ln-green-deep);
  transform: translateY(2px) rotate(-135deg);
}
/* The way in to Bird's, Larsen's and the Polish: the foot of the same list, in the
   list's own rhythm, quiet enough that it never competes with the four moves above
   it. A word and a caret, which is all a disclosure is. */
.rp-fm-more {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  width: 100%;
  margin: 0;
  padding: 13px 0 3px;
  background: none; border: none; cursor: pointer;
  font-family: var(--ln-ui); font-size: 0.82rem; font-weight: 700;
  color: var(--ln-green-deep);
}
.rp-fm-more::after {
  content: '';
  width: 6px; height: 6px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
}
.rp-fm-more.is-open::after { transform: translateY(1px) rotate(-135deg); }
@media (prefers-reduced-motion: reduce) {
  .rp-firstmove .rp-options-acc .rp-option .rp-option-head::after { transition: none; }
}

/* ── THE DOOR SCREEN (work item 7, step 5) ────────────────────────────────────
   A shelf of archways, one per route into the opening. Ed picked it from four
   treatments in docs/doors-mock.html, 2026-09-12: "I kind of like 2."

   The brief was that it must not look like a chapter list, and this is the
   furthest thing on the page from one: a chapter page is a vertical column of
   reply rows, and this is a horizontal row of tall wooden arches.

   THE ARCH IS A FRAME AROUND A SQUARE BOARD, NOT A CLIP OVER IT. The first cut
   curved the board itself and ate the eighth rank off every one of them, which
   loses the pieces the position is about. So the arch carries a lintel above a
   board that stays square. */
/* THE DOORS FILL THE WIDTH (Ed, 2026-09-12: "make the doors bigger so they fit to
   the page"), AND THEY SIT NEXT TO EACH OTHER ("They should be next to each other").
   They were a fixed 176px in a sideways scroller, which left a third of a phone
   screen empty on the commonest case of two routes.

   FLEX, NOT GRID, and that is not a style preference. `repeat(auto-fit, minmax(160px,
   250px))` counts its tracks off the track's MAXIMUM when that is a definite length,
   so a 398px phone fits one 250px track and stacked the doors one per row on Ed's
   phone. Flex wrapping goes off the flex-basis instead, so two 160px bases fit and
   each grows to fill half the row. */
.rp-doors {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 12px; padding: 2px 2px 14px;
}
.rp-door {
  /* Half a row, so a third route left alone on the second row is the same size as
     the two above it rather than growing to fill the width by itself. */
  flex: 1 1 160px; max-width: calc(50% - 6px);
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  text-align: center; cursor: pointer; font: inherit; color: inherit;
  background: linear-gradient(180deg, var(--bg-door-top), var(--ln-card2));
  border: 1px solid var(--ln-border);
  /* A PLAIN CARD, NOT AN ARCHWAY (Ed, 2026-09-12: "The door shape isn't working.
     Make it regular"). The arch was his pick from the mockup and it did not survive
     contact with his phone, where a doorway the height of half the screen reads as
     one enormous tile rather than as a row of choices. The route's colour moves onto
     a dot beside the move, which is how a reply is marked everywhere else. */
  border-radius: 18px;
  padding: 12px 12px 14px; box-shadow: var(--ln-shadow-sm);
  transition: transform 0.14s ease, box-shadow 0.14s ease, border-color 0.14s ease;
}
.rp-door:hover { transform: translateY(-3px); box-shadow: var(--ln-shadow-md); border-color: var(--tone); }
.rp-door:active { transform: translateY(-1px); }
.rp-door-board {
  display: block; width: 100%;
  border: 2px solid var(--dark-sq); border-radius: 6px; overflow: hidden;
}
.rp-door-board .mini-board { display: block; width: 100%; height: auto; border-radius: 0; }
.rp-door-pct {
  font-family: var(--ln-serif); font-size: 2rem; font-weight: 700;
  color: var(--tone); line-height: 1.1; margin-top: 10px;
}
.rp-door-pct i { font-size: 1rem; font-style: normal; opacity: 0.7; }
.rp-door-move {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  font-family: 'Roboto Mono', 'Courier New', monospace; font-size: 1.02rem;
  font-weight: 700; color: var(--ln-ink); margin-top: 1px; padding-bottom: 2px;
}
.rp-door-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--tone); flex: 0 0 10px; }
/* A bigger board deserves a bigger number under it. */
@media (min-width: 761px) {
  .rp-doors { gap: 16px; }
  .rp-door { flex: 1 1 190px; max-width: 250px; }
  .rp-door-pct { font-size: 2.3rem; }
}

/* ============================================================
   THEIR REPLY, ON THE SCREEN (Ed, 2026-09-16)
   The "Add a line" walk used to name the opponent's reply in a sentence and
   hide every alternative behind "Choose a different move for White". The card
   below is what replaced it: the reply on offer as a wide card, the other
   moves as sections split evenly beneath it, and the two bars that close it.
   Every move is written in its own arrow's colour, set inline from
   RP_OPT_COLORS[i] — the same index that drew the arrow, so a move on the card
   and a move on the board are the same thing twice.
   Built by rpAltCardHtml / rpAltBarsInto in repertoire.js.
   ============================================================ */
.rp-alt-eyebrow {
  font-family: var(--ln-ui); font-size: 0.68rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.07em; color: var(--accent2);
}

/* The reply on offer. The whole card is the target: there is no smaller button
   inside it, which is the rule the "How you start" cards already follow.
   It has no round dot and no chevron (Ed, 2026-09-16) — the badge already
   carries the colour and the card has always been the tap target — so there is
   nothing left to align against and its move centres like the sections below. */
.rp-alt-lead {
  display: flex; align-items: center; justify-content: center; gap: 12px;
  width: 100%; margin-top: 10px;
  padding: 12px 14px; border-radius: 16px; cursor: pointer; text-align: center;
  font: inherit; color: var(--ln-ink);
  border: 1px solid var(--ln-green);
  background: linear-gradient(180deg, var(--bg-rp-alt-lead), var(--bg-rp-alt));
  box-shadow: var(--ln-shadow-sm);
  transition: border-color .14s, box-shadow .14s, transform .12s;
}
.rp-alt-lead:hover { box-shadow: var(--ln-shadow-md); transform: translateY(-1px); }
.rp-alt-lead:active { transform: translateY(0); }
.rp-alt-lead:focus-visible { outline: 2px solid var(--ln-green); outline-offset: 2px; }
.rp-alt-leadtx { min-width: 0; }
/* The same badge the sections wear, one size up. inline-block, so the centring
   on the card above it applies to the badge and not just to its text. */
.rp-alt-big {
  display: inline-block; font-family: var(--ln-ui); font-weight: 800;
  font-size: 1.22rem; line-height: 1; color: var(--ink-rp-alt-badge);
  padding: 7px 12px; border-radius: 9px;
  box-shadow: inset 0 -1px 0 var(--shadow-rep-info-banner);
}
.rp-alt-sub { display: block; font-size: 0.75rem; color: var(--ln-dim); margin-top: 6px; }

/* The other moves. One .rp-alt-row per row of sections, with n1/n2/n3 saying how
   many share it; rpAltSplit decides. Evenly split rather than sized to their own
   words (Ed, 2026-09-16), so the row is never part empty and a section is never
   narrower than the count says. */
.rp-alt-row { display: grid; gap: 8px; margin-top: 8px; }
.rp-alt-row.n1 { grid-template-columns: 1fr; }
.rp-alt-row.n2 { grid-template-columns: 1fr 1fr; }
.rp-alt-row.n3 { grid-template-columns: 1fr 1fr 1fr; }
.rp-alt-mv {
  min-width: 0; cursor: pointer; font: inherit; color: var(--ln-ink);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; min-height: 58px; padding: 9px 8px;
  border: 1px solid var(--ln-border-soft); border-radius: 13px;
  background: linear-gradient(180deg, var(--bg-rp-option), var(--bg));
  box-shadow: var(--ln-shadow-sm), inset 0 1px 0 var(--shadow-ms-tile);
  transition: border-color .14s, box-shadow .14s, transform .12s;
}
.rp-alt-mv:hover { border-color: var(--ln-green); box-shadow: var(--ln-shadow-md); transform: translateY(-1px); }
.rp-alt-mv:active { transform: translateY(0); }
.rp-alt-mv:focus-visible { outline: 2px solid var(--ln-green); outline-offset: 2px; }
/* The move IS the colour, rather than carrying a dot beside it: at a third of
   the card there is no room for both, and the badge is the component the answer
   screen already uses for a move. */
.rp-alt-san {
  font-family: var(--ln-ui); font-weight: 800; font-size: 0.85rem; line-height: 1;
  color: var(--ink-rp-alt-badge); padding: 5px 9px; border-radius: 7px;
  box-shadow: inset 0 -1px 0 var(--shadow-rep-info-banner);
}
.rp-alt-pct {
  font-family: 'Roboto Mono', 'Courier New', monospace;
  font-size: 0.74rem; font-weight: 700; color: var(--ln-dim); line-height: 1;
}

/* The two bars. Appended after the move list and Analyze, so they close the card
   rather than sitting in the middle of it. The hairline is the bars' own, which is
   why it rides on the block instead of being a separate element. */
.rp-alt-bars {
  margin-top: 13px; padding-top: 13px;
  border-top: 1px solid var(--ln-border-soft);
}
.rp-alt-save, .rp-alt-back {
  display: flex; align-items: center; justify-content: center; gap: 9px; width: 100%;
  padding: 11px 14px; border-radius: 13px; cursor: pointer;
  font-family: var(--ln-ui); font-weight: 700; font-size: 0.88rem;
  transition: border-color .14s, box-shadow .14s, transform .12s;
}
.rp-alt-save {
  color: var(--ln-green-deep); border: 1px solid var(--line-rp-alt-save);
  background: var(--bg-rp-alt-save);
}
.rp-alt-back {
  margin-top: 8px; font-weight: 600; font-size: 0.84rem; color: var(--ln-dim);
  border: 1px solid var(--ln-border-soft); background: var(--bg-rp-alt-back);
}
/* On the choose-your-move screen back is the only bar, so it has nothing to sit
   under and the gap that separates it from Save and finish is dead space. */
.rp-alt-bars > .rp-alt-back:first-child { margin-top: 0; }
.rp-alt-save:hover, .rp-alt-back:hover { box-shadow: var(--ln-shadow-md); transform: translateY(-1px); }
.rp-alt-save:active, .rp-alt-back:active { transform: translateY(0); }
.rp-alt-save:focus-visible, .rp-alt-back:focus-visible { outline: 2px solid var(--ln-green); outline-offset: 2px; }

/* ============================================================
   THE TWO TABS ON THE BUILD-A-REPLY SCREEN (Ed, 2026-09-17)
   docs/prepare-tabs-mock.html, five looks, he took the inset track: the track is
   pressed into the panel and the open tab is a raised cream card sitting in it.
   Neither tab carries a count. Built by rpNavTabsHtml in repertoire.js; the card
   under them is rpNavCardHtml, which wears the .rp-alt-* classes above.
   ============================================================ */
.rp-navtabs {
  display: grid; grid-template-columns: 1fr 1fr; gap: 4px; padding: 4px;
  border-radius: 14px; background: var(--bg-rp-navtabs);
  box-shadow: inset 0 1px 2px var(--shadow-rp-navtabs);
}
.rp-navtab {
  display: flex; align-items: center; justify-content: center;
  padding: 9px 6px; border: 0; border-radius: 11px; cursor: pointer;
  font-family: var(--ln-serif); font-size: 0.92rem; font-weight: 700;
  color: var(--ln-dim); background: none;
  transition: color .14s, background .14s, box-shadow .14s;
}
.rp-navtab.is-on {
  color: var(--ln-ink); background: var(--bg-rp-alt);
  box-shadow: var(--ln-shadow-sm), inset 0 1px 0 var(--shadow-ms-tile);
}
.rp-navtab:focus-visible { outline: 2px solid var(--ln-green); outline-offset: 2px; }

/* THE END OF A LINE YOU BUILT (Ed, 2026-09-19, option 4 of
   docs/end-of-line-tabs-mock.html, wording set 3 of
   docs/end-of-line-words-mock.html). The Prepared tab with nothing left in it.
   The same band as .rp-lhdr above, and deliberately not that class: .rp-lhdr is
   welded to the top of the PANEL and this sits under the tabs, inside the tab it
   belongs to. Edge to edge, so it undoes the panel's own padding, which is 16px
   and 10px on a phone (styles.css) exactly as .rp-lbody does. Written by
   rpNavEndHtml. */
.rp-navend {
  display: flex; align-items: center; justify-content: center;
  min-height: 52px; margin: 12px -16px 0; padding: 11px 14px;
  background: linear-gradient(180deg, var(--bg-rp-linedone), var(--bg-disabled));
  border-top: 1px solid var(--line-rp-linedone);
  border-bottom: 1px solid var(--line-rp-linedone);
  color: var(--ln-green-deep);
  font-family: var(--ln-serif); font-weight: 700; font-size: 1rem; line-height: 1.2;
}
@media (max-width: 760px) { .rp-navend { margin: 12px -10px 0; } }

/* The one line of the old paragraph still doing work: playing your own move is
   the only thing on this screen nothing else hints at. Something new only. */
.rp-nav-board {
  font-family: var(--ln-serif); font-size: 0.8rem; color: var(--ln-dim);
  margin-top: 11px; text-align: center;
}

/* ============================================================
   YOUR OWN MOVE, IN THE SAME SHAPE (Ed, 2026-09-16)
   Built by rpAnsCardHtml, which reuses every class above — the wide card, the
   rows, the sections and the badges are the same components the opponent's
   reply uses. Only what is different lives here: the green line that says the
   move is yours, the ★ beside it before you have answered, the name a few
   openings carry, and the two numbers on a section.
   docs/your-move-card-plan.md has the eight rounds behind it.
   ============================================================ */

/* Once you have answered, this is the ONLY thing marking your move (Ed's pick
   from four treatments in round 5): no badge, no ring. It sits above the move
   rather than beside it, so the badge stays centred on the card. */
.rp-ans-you {
  display: block; font-family: var(--ln-ui); font-size: 0.64rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: 0.09em;
  color: var(--ln-green-deep); margin-bottom: 6px;
}
/* The star rides beside the move on the same line, Ed's own suggestion and his
   pick from round 2. It costs the badge 12px of centring, measured, and that was
   accepted knowingly. */
.rp-ans-leadrow { display: flex; align-items: center; justify-content: center; gap: 9px; }
.rp-ans-star {
  flex: none; font-size: 1.05rem; line-height: 1;
  color: var(--bg-rp-option-badgerecommend);
}
/* The hand-written name from REP_STORIES, where an opening has one. On a section
   it sits between the move and the numbers; on the wide card it is a size up. */
.rp-ans-nm {
  display: block; font-family: var(--ln-serif); font-weight: 700; font-size: 0.7rem;
  line-height: 1.15; color: var(--accent2); text-align: center;
}
.rp-ans-nm.is-big { font-size: 0.95rem; margin-top: 6px; }
/* Both numbers, every time (Ed): the score left, the engine right, a hairline
   between. A move with no score takes the whole width rather than leaving half
   the row empty — that is what is-one is for. */
.rp-ans-split { display: flex; align-items: stretch; width: 100%; }
.rp-ans-split > span {
  flex: 1 1 0; min-width: 0; text-align: center; line-height: 1.1;
  font-family: 'Roboto Mono', 'Courier New', monospace;
  font-size: 0.74rem; font-weight: 700; color: var(--ln-dim);
}
.rp-ans-split > span + span { border-left: 1px solid var(--ln-border-soft); }
.rp-ans-split.is-one > span + span { border-left: 0; }

/* ============================================================
   CONFIRMING A MOVE YOU PLAYED YOURSELF (Ed, 2026-09-16)
   Option 4 of docs/confirm-move-mock.html: no card at all. The move is named on
   the button you press, so nothing repeats it, and the panel behind the two bars
   draws no surface — which is what .rp-builder-panel.is-bare is for. The move
   list and Analyze keep their place underneath.
   Built by rpRenderWalk's custom-played branch and rpPanelBare.
   ============================================================ */
.rp-builder-panel.is-bare {
  background: none; border: 0; box-shadow: none; padding: 0;
}
.rp-conf { display: block; }
/* The one solid-green bar in the tab. Everything else here is a quiet pill, and
   this is the single thing the screen is asking you to do. */
.rp-conf-play {
  display: flex; align-items: center; justify-content: center; gap: 9px; width: 100%;
  padding: 14px; border-radius: 13px; cursor: pointer; border: 1px solid var(--ln-green);
  font-family: var(--ln-ui); font-weight: 800; font-size: 0.97rem;
  color: var(--ink-rp-danger-btn); background: var(--ln-green);
  box-shadow: var(--ln-shadow-sm);
  transition: border-color .14s, box-shadow .14s, transform .12s;
}
.rp-conf-play:hover { box-shadow: var(--ln-shadow-md); transform: translateY(-1px); }
.rp-conf-play:active { transform: translateY(0); }
.rp-conf-play:focus-visible { outline: 2px solid var(--ln-green-deep); outline-offset: 2px; }
/* The one place the app says a move of yours is bad. It sits BETWEEN the two bars
   so it reads as the reason to consider the second one. */
.rp-conf-warn {
  margin: 11px 0; text-align: center; font-family: var(--ln-serif); font-style: italic;
  font-size: 0.85rem; line-height: 1.5; color: var(--ink-rp-ch-warn);
}
.rp-conf > .rp-alt-back { margin-top: 11px; }
