  /* Breakpoints — use these two, don't invent new ones.
     760px: phone layout (page scroll, stacked boards, swipe rows).
     480px: small-phone tightening. Ed's iPhone 17 Pro Max is 440px wide,
     so anything meant for his phone must sit at 480, never 430. */

  /* Text steps up on bigger screens (Ed, 2026-08-07). Type was sized for a
     phone held at arm's length and never grew for a monitor at desk distance.
     Nothing sets a root size, so everything inherits the browser's 16px.
     This is safe to do from the root because rem in this app means TEXT and
     nothing else: of 345 rem declarations, 340 are font-size and the other 5
     are the font shorthand. No padding, margin or width uses it, so raising
     the root scales the words and moves no layout. Boards are sized in px and
     don't follow, which is deliberate — they have their own rules.
     The phone is untouched: neither query applies below 760px.
     Two steps, and the second sits at 1600 deliberately: Ed's 14" laptop is
     1512 wide, so a 1440 threshold would have given it the same size as the
     24" desktop. A small screen at laptop distance wants the smaller step. */
  @media (min-width: 760px)  { html { font-size: 17px; } }
  @media (min-width: 1600px) { html { font-size: 18px; } }

  :root {
    --bg: #e8dfc0;
    --surface: #d8cc9e;
    --surface2: #c8b880;
    --accent: #3e7818;
    --accent2: #7a5228;
    --light-sq: #f0dab0;
    --dark-sq: #6a4018;
    --highlight: rgba(80, 180, 60, 0.7);
    --last-move: rgba(60, 160, 40, 0.45);
    --text: #1e1008;
    --text-dim: #7a5830;
    --correct: #2e7820;
    --wrong: #a02818;
    --border: #a08848;
    --bk-sq: clamp(56px, min(
      calc((100vw - 510px) / 8),
      calc((100vh - 290px) / 8)
    ), 88px);
  }


  /* ============================================================
     THE PALETTE (generated by tools/tokenise-css.js, stage 2 of
     docs/theme-system-plan.md). One token per role and value, so this
     pass changed no pixel. Names describe the JOB, never the colour, so
     a second theme only has to redefine this block.
     ============================================================ */
  :root {

    /* ---- From the injected stylesheets in account.js and auth.js ---- */
    --ink-acct-danger: #a3352f;
    --ink-msg-error: #b02020;
    --bg-toggle-track: #b9ac86;
    --bg-avatar-light: #efe6cf;
    --bg-avatar-dark: #5a4326;
    --bg-seg-rest: rgba(255,250,235,0.55);
    --bg-inset-panel: rgba(255,250,235,0.5);
    --line-account-badge: rgba(255,255,255,0.7);
    --shadow-auth-card: rgba(60,42,12,0.22);
    --shadow-badge-hover: rgba(0,0,0,0.32);

    /* ---- Text (35) ---- */
    --ink-rp-danger-btn: #ffffff;        /* 20x */
    --ink-btn-primary: #f4fbe8;          /* 6x */
    --ink-eval-bar-numat-white: #4a2c10; /* 5x */
    --ink-h1: #d8eaa0;                   /* 3x */
    --ink-rp-walk-rewind: #b05c10;       /* 3x */
    --ink-eval-bar-numeq: #8a6a3f;       /* 3x */
    --ink-rp-chipis-progress: #8a5a10;   /* 3x */
    --ink-rp-conflict-block: #b23b3b;    /* 2x */
    --ink-fable-ch-step: #e8d9a0;        /* 2x */
    --ink-rp-side-white: #2a1a0a;        /* 2x */
    --ink-rp-chipis-attn: #9a3b1b;       /* 2x */
    --ink-btag-attack: #b83010;          /* 1x */
    --ink-btag-solid: #1e5aa0;           /* 1x */
    --ink-btag-theory: #6a28a0;          /* 1x */
    --ink-btag-access: #147840;          /* 1x */
    --ink-btag-dynamic: #906008;         /* 1x */
    --ink-bottom-tab: rgba(255,255,255,0.6); /* 1x */
    --ink-hover: rgba(255,255,255,0.85); /* 1x */
    --ink-bottom-tabactive: #eaffc4;     /* 1x */
    --ink-rp-ch-warn: #9a5d12;           /* 1x */
    --ink-rp-conflict-warn: #9a6a00;     /* 1x */
    --ink-rp-deep-alt: rgba(0,0,0,0.18); /* 1x */
    --ink-rep-practice-line-name: rgba(216,234,160,0.72); /* 1x */
    --ink-player-color-badgewhite: #333333; /* 1x */
    --ink-player-color-badgeblack: #f0d9b5; /* 1x */
    --ink-btn-primary-2: #eef8d0;        /* 1x */
    --ink-ms-tag-w: #5a3d16;             /* 1x */
    --ink-ms-oc-star: #b9902a;           /* 1x */
    --ink-ms-oc-staron: #e6a817;         /* 1x */
    --ink-span: rgba(255,255,255,0.82);  /* 1x */
    --ink-ep-modal-danger: #fdecec;      /* 1x */
    --ink-rp-side-black: #f6ecd4;        /* 1x */
    --ink-epb-cd: rgba(106,64,24,0.7);   /* 1x */
    --ink-epb-cd-2: rgba(240,218,176,0.75); /* 1x */
    --ink-rpm-gap-title: #8d3a2a;        /* 1x */

    /* ---- Surfaces and fills (136) ---- */
    --bg-rp-prep-segready: #4d8b23;      /* 5x */
    --bg-rpc-movegreen: rgba(62,120,24,0.14); /* 4x */
    --bg-header: #7a5228;                /* 3x */
    --bg-rp-ch-badgewarn: #b8791b;       /* 3x */
    --bg-rp-deep-primary: rgba(62,120,24,0.07); /* 3x */
    --bg-active-move: rgba(62,120,24,0.22); /* 3x */
    --bg-eval-bar: #3a230e;              /* 3x */
    --bg-btn-ghost: rgba(255,250,235,0.6); /* 3x */
    --bg-ms-rrowdone: rgba(62,120,24,0.1); /* 3x */
    --bg-body: rgba(100,70,10,0.03);     /* 2x */
    --bg-header-2: #5e3e1a;              /* 2x */
    --bg-rp-danger-btn: #b23b3b;         /* 2x */
    --bg-rp-conflict-block: rgba(178,59,59,0.08); /* 2x */
    --bg-hover: rgba(0,0,0,0.05);        /* 2x */
    --bg-hover-2: rgba(62,120,24,0.16);  /* 2x */
    --bg-hover-3: #d0c088;               /* 2x */
    --bg-ep-mv-active: rgba(62,120,24,0.18); /* 2x */
    --bg-rp-chapter-cardrpclearned: rgba(62,120,24,0.055); /* 2x */
    --bg-rpc-move: rgba(0,0,0,0.055);    /* 2x */
    --bg-rpc-answer: rgba(62,120,24,0.08); /* 2x */
    --bg-disabled: rgba(62,120,24,0.09); /* 2x */
    --bg-hover-4: rgba(255,250,235,0.95); /* 2x */
    --bg-button: rgba(255,250,235,0.7);  /* 2x */
    --bg-button-2: rgba(216,204,158,0.6); /* 2x */
    --bg-hover-5: rgba(184,121,27,0.22); /* 2x */
    --bg-rp-chip: rgba(138,124,102,0.12); /* 2x */
    --bg-rp-chipis-progress: rgba(184,121,27,0.1); /* 2x */
    --bg-rp-act: rgba(255,255,255,0);    /* 2x */
    --bg-rp-act-badge: rgba(138,124,102,0.14); /* 2x */
    --bg-rpm-hole: rgba(141,127,106,0.07); /* 2x */
    --bg-after: rgba(240,218,176,0.55);  /* 2x */
    --bg-body-2: rgba(80,130,30,0.1);    /* 1x */
    --bg-body-3: rgba(100,70,20,0.08);   /* 1x */
    --bg-btag-attack: rgba(180,50,20,0.14); /* 1x */
    --bg-btag-solid: rgba(30,90,160,0.13); /* 1x */
    --bg-btag-theory: rgba(110,40,160,0.13); /* 1x */
    --bg-btag-access: rgba(20,130,70,0.13); /* 1x */
    --bg-btag-dynamic: rgba(160,100,10,0.14); /* 1x */
    --bg-hover-6: rgba(0,0,0,0.03);      /* 1x */
    --bg-bottom-nav: #2e6018;            /* 1x */
    --bg-bottom-nav-2: #1e4810;          /* 1x */
    --bg-before: rgba(168,232,96,0.16);  /* 1x */
    --bg-hover-7: #9a3030;               /* 1x */
    --bg-hover-8: rgba(255,255,255,0.06); /* 1x */
    --bg-rp-conflict-ok: rgba(74,124,58,0.08); /* 1x */
    --bg-rp-conflict-warn: rgba(230,143,0,0.1); /* 1x */
    --bg-rp-deep-ic: rgba(62,120,24,0.15); /* 1x */
    --bg-rp-option-badgerecommend: #15781b; /* 1x */
    --bg-hover-9: rgba(255,255,255,0.1); /* 1x */
    --bg-rep-move-history: #c8ba88;      /* 1x */
    --bg-player-color-badgewhite: #f0d9b5; /* 1x */
    --bg-player-color-badgeblack: #333333; /* 1x */
    --bg-after-2: rgba(0,0,0,0.25);      /* 1x */
    --bg-hover-10: rgba(0,0,0,0.07);     /* 1x */
    --bg-btn-primary: #4a8c20;           /* 1x */
    --bg-btn-primary-2: #336010;         /* 1x */
    --bg-hover-11: #58a828;              /* 1x */
    --bg-btn-secondary: #c8b880;         /* 1x */
    --bg-btn-secondary-2: #b8a868;       /* 1x */
    --bg-hover-12: #c0b070;              /* 1x */
    --bg-webkit-scrollbar-track: #d0c090; /* 1x */
    --bg-hover-13: #7a6030;              /* 1x */
    --bg-after-3: rgba(0,0,0,0.3);       /* 1x */
    --bg-after-4: rgba(255,255,255,0.32); /* 1x */
    --bg-disabled-2: rgba(255,255,255,0.16); /* 1x */
    --bg-hover-14: rgba(255,255,255,0.12); /* 1x */
    --bg-fable-ch-progress: rgba(0,0,0,0.28); /* 1x */
    --bg-span: #e8d9a0;                  /* 1x */
    --bg-fable-hint: rgba(120,80,20,0.08); /* 1x */
    --bg-ms-tag-b: #4a3016;              /* 1x */
    --bg-ms-oc-star: rgba(255,255,255,0.9); /* 1x */
    --bg-after-5: #ffffff;               /* 1x */
    --bg-ms-score-track: rgba(0,0,0,0.1); /* 1x */
    --bg-master-feedback: rgba(40,28,12,0.92); /* 1x */
    --bg-master-feedbackgood: rgba(36,98,28,0.94); /* 1x */
    --bg-master-feedbackbad: rgba(150,36,22,0.94); /* 1x */
    --bg-master-feedbacktrap: rgba(150,84,12,0.94); /* 1x */
    --bg-master-choice: rgba(0,0,0,0.04); /* 1x */
    --bg-rpc-caret: rgba(160,136,72,0.16); /* 1x */
    --bg-rp-think-track: rgba(160,136,72,0.3); /* 1x */
    --bg-rp-prep-segready-2: #38700f;    /* 1x */
    --bg-btn-secondary-3: #d8c88c;       /* 1x */
    --bg-disabled-3: #dccc8e;            /* 1x */
    --bg-btn-ghost-2: rgba(216,204,158,0.5); /* 1x */
    --bg-ms-tile: rgba(255,250,235,0.9); /* 1x */
    --bg-ms-tile-2: rgba(216,204,158,0.72); /* 1x */
    --bg-rp-linedone: rgba(62,120,24,0.17); /* 1x */
    --bg-button-3: rgba(255,250,235,0.58); /* 1x */
    --bg-hover-15: rgba(255,250,235,0.85); /* 1x */
    /* The Explore board's path row. SOLID on purpose, where a panel like this
       would normally be a wash: the row fades out at both ends to show it
       scrolls, and a fade has to end in the exact colour behind it. The page
       carries two large radial tints, so a translucent row would leave the fade
       a shade off at the top of the page and a different shade lower down —
       the same reason the pinned board was reverted on 2026-08-02. */
    --bg-rpm-path: #f3ecd6;
    --bg-rpm-path-0: rgba(243,236,214,0);
    /* How often the opponent plays a move, painted behind its row on the Explore
       board. A warm neutral ON PURPOSE, never green: green on that board already
       means "you have a reply saved for this", and one colour cannot carry two
       meanings. Low alpha so it reads over both the green row and the tan one. */
    --bg-rpm-mv-fill: rgba(120,92,44,0.16);
    --bg-ms-scrim: rgba(28,18,6,0.58);   /* 1x */
    --bg-ms-report: #efe7cd;             /* 1x */
    --bg-ms-report-2: #ddd0a6;           /* 1x */
    --bg-ms-rstat: rgba(255,250,235,0.66); /* 1x */
    --bg-ms-rrow: rgba(255,250,235,0.62); /* 1x */
    --bg-ep-modal-danger: #c24a4a;       /* 1x */
    --bg-ep-modal-danger-2: #a83232;     /* 1x */
    --bg-disabled-4: #b23f3f;            /* 1x */
    --bg-disabled-5: #8f2929;            /* 1x */
    --bg-ep-modal-overlay: rgba(30,16,8,0.46); /* 1x */
    --bg-hover-16: rgba(60,42,12,0.07);  /* 1x */
    --bg-fable-ch-progress-2: rgba(0,0,0,0.16); /* 1x */
    --bg-fb-recess: rgba(106,64,24,0.07); /* 1x */
    --bg-book-hl: rgba(60,160,40,0.22);  /* 1x */
    --bg-after-6: rgba(0,0,0,0.22);      /* 1x */
    --bg-rp-side-white: #f2e7c4;         /* 1x */
    --bg-rp-side-white-2: #e4d4a4;       /* 1x */
    --bg-rp-side-black: #533516;         /* 1x */
    --bg-ms-side-both: #efe4c0;          /* 1x */
    --bg-ms-side-both-2: #ded0a0;        /* 1x */
    --bg-wt-overlay: rgba(30,16,8,0.55); /* 1x */
    --bg-wt-dot: rgba(0,0,0,0.18);       /* 1x */
    --bg-rp-train-band-go: rgba(255,250,240,0.6); /* 1x */
    --bg-rp-train-band-go-2: rgba(255,252,246,0.95); /* 1x */
    --bg-rp-chipis-attn: rgba(176,64,24,0.1); /* 1x */
    --bg-rp-act-2: rgba(255,255,255,0.45); /* 1x */
    --bg-rp-act-3: #fffaf0;              /* 1x */
    --bg-rp-actis-train: rgba(255,255,255,0.35); /* 1x */
    --bg-rp-act-ic: rgba(184,121,27,0.34); /* 1x */
    --bg-rp-act-ic-2: rgba(184,121,27,0.2); /* 1x */
    --bg-rp-act-badge-2: rgba(255,250,240,0.75); /* 1x */
    --bg-rpm-sum-bar: rgba(106,64,24,0.14); /* 1x */
    --bg-hover-17: rgba(141,127,106,0.16); /* 1x */
    --bg-rp-chcard-chipis-attn: rgba(176,64,24,0.12); /* 1x */
    --bg-hover-18: rgba(62,120,24,0.05); /* 1x */
    --bg-rp-train-pill-n: rgba(255,250,240,0.7); /* 1x */
    --bg-rpc-move-2: rgba(106,64,24,0.07); /* 1x */
    --bg-after-7: rgba(106,64,24,0.2);   /* 1x */
    --bg-after-8: rgba(106,64,24,0.05);  /* 1x */
    --bg-after-9: rgba(58,35,14,0.55);   /* 1x */
    --bg-before-2: rgba(230,200,60,0.3); /* 1x */
    --bg-after-10: rgba(62,120,24,0.45); /* 1x */
    --bg-after-11: rgba(58,35,14,0.6);   /* 1x */
    --bg-rpm-chipsome: rgba(184,121,27,0.07); /* 1x */
    --bg-rpm-gap: rgba(168,67,44,0.05);  /* 1x */
    --bg-rp-option: #f4ecd6;             /* 1x */
    --bg-rp-optionselected: #eff3e1;     /* 1x */
    --bg-rp-optionselected-2: #e3ecd1;   /* 1x */

    /* ---- Borders, outlines, rules (55) ---- */
    --line-rp-warn: rgba(184,121,27,0.4); /* 5x */
    --line-rp-chcardis-done: rgba(62,120,24,0.5); /* 4x */
    --line-hover: rgba(184,121,27,0.7);  /* 3x */
    --line-rp-deep-primary: rgba(62,120,24,0.28); /* 2x */
    --line-after: rgba(0,0,0,0.25);      /* 2x */
    --line-squaredrag-over: rgba(62,120,24,0.7); /* 2x */
    --line-rpc-leader: rgba(160,136,72,0.6); /* 2x */
    --line-btn-ghost: rgba(160,136,72,0.55); /* 2x */
    --line-rp-linedone: rgba(62,120,24,0.34); /* 2x */
    --line-ms-rrowdone: rgba(62,120,24,0.3); /* 2x */
    --line-ep-modal-danger: #7f2727;     /* 2x */
    --line-rp-train-band: rgba(184,121,27,0.45); /* 2x */
    --line-rp-train-band-go: rgba(184,121,27,0.5); /* 2x */
    --line-rp-chipis-done: rgba(62,120,24,0.4); /* 2x */
    --line-rp-chmine: rgba(62,120,24,0.30);
    --line-rpm-hole: rgba(141,127,106,0.7); /* 2x */
    --line-header: #4a2e10;              /* 1x */
    --line-btag-attack: rgba(180,50,20,0.28); /* 1x */
    --line-btag-solid: rgba(30,90,160,0.25); /* 1x */
    --line-btag-theory: rgba(110,40,160,0.25); /* 1x */
    --line-btag-access: rgba(20,130,70,0.25); /* 1x */
    --line-btag-dynamic: rgba(160,100,10,0.27); /* 1x */
    --line-bottom-nav: #3a7820;          /* 1x */
    --line-rp-rep-confirm: #b23b3b;      /* 1x */
    --line-rp-ch-badgewarn: #b8791b;     /* 1x */
    --line-rp-conflict-ok: rgba(74,124,58,0.35); /* 1x */
    --line-rp-conflict-warn: rgba(230,143,0,0.45); /* 1x */
    --line-rp-conflict-block: rgba(178,59,59,0.4); /* 1x */
    --line-rp-walk-intro: rgba(0,0,0,0.1); /* 1x */
    --line-rep-practice-back: rgba(240,232,208,0.35); /* 1x */
    --line-player-color-badgeblack: #555555; /* 1x */
    --line-btn-primary: #336010;         /* 1x */
    --line-ms-tag-w: #d8bd85;            /* 1x */
    --line-ms-tag-b: #3a2410;            /* 1x */
    --line-ms-oc-cardfav: #e6a817;       /* 1x */
    --line-rp-ch-var: rgba(160,136,72,0.5); /* 1x */
    --line-rpc-count: rgba(160,136,72,0.4); /* 1x */
    --line-rpc-actions: rgba(160,136,72,0.35); /* 1x */
    --line-ms-tile: rgba(160,136,72,0.62); /* 1x */
    --line-ms-foot: rgba(120,92,44,0.18); /* 1x */
    --line-button: rgba(160,136,72,0.52); /* 1x */
    --line-ms-report: rgba(120,92,44,0.55); /* 1x */
    --line-ms-rx: rgba(120,92,44,0.34);  /* 1x */
    --line-ms-rstat: rgba(160,136,72,0.42); /* 1x */
    --line-rp-side-black: rgba(60,40,16,0.6); /* 1x */
    --line-focus-visible: #8fbf5a;       /* 1x */
    --line-ms-side-both: rgba(150,126,66,0.55); /* 1x */
    --line-rp-repcardis-confirm: rgba(178,59,59,0.5); /* 1x */
    --line-rp-chipis-attn: rgba(176,64,24,0.4); /* 1x */
    --line-rp-viewbtnis-explore: rgba(62,120,24,0.35); /* 1x */
    --line-rpc-move: rgba(106,64,24,0.14); /* 1x */
    --line-rpc-move-2: rgba(62,120,24,0.22); /* 1x */
    --line-rp-chapter-cardrpcrpc-next: rgba(62,120,24,0.45); /* 1x */
    --line-rpm-offbook: rgba(184,121,27,0.42); /* 0x — the off-book card is a solid line-end card now (2026-08-25) */
    --line-rpm-chipmine: rgba(62,120,24,0.75); /* 1x */
    --line-rpm-gap: rgba(168,67,44,0.5); /* 1x */

    /* ---- Shadows, glows and filters (53) ---- */
    --shadow-img: #f4ead2;               /* 12x */
    --shadow-bottom-nav: rgba(0,0,0,0.3); /* 7x */
    --shadow-rpc-board: rgba(58,40,20,0.14); /* 4x */
    --shadow-hover: rgba(0,0,0,0.1);     /* 3x */
    --shadow-rep-info-banner: rgba(0,0,0,0.2); /* 3x */
    --shadow-disabled: rgba(255,255,255,0.2); /* 3x */
    --shadow-btn-secondary: rgba(255,255,255,0.35); /* 3x */
    --shadow-ms-tile: rgba(255,255,255,0.5); /* 3x */
    --shadow-rpc-board-2: rgba(58,40,20,0.1); /* 3x */
    --shadow-rpm-board: rgba(58,40,20,0.16); /* 3x */
    --shadow-header: rgba(0,0,0,0.25);   /* 2x */
    --shadow-hover-2: rgba(0,0,0,0.12);  /* 2x */
    --shadow-rep-move-history: rgba(0,0,0,0.15); /* 2x */
    --shadow-fable-hint-sq: rgba(62,120,24,0.85); /* 2x */
    --shadow-master-feedback: rgba(0,0,0,0.28); /* 2x */
    --shadow-btn-primary: rgba(44,90,16,0.3); /* 2x */
    --shadow-btn-primary-2: rgba(255,255,255,0.18); /* 2x */
    --shadow-disabled-2: rgba(44,90,16,0.32); /* 2x */
    --shadow-disabled-3: rgba(255,255,255,0.4); /* 2x */
    --shadow-focus: rgba(62,120,24,0.25); /* 2x */
    --shadow-bottom-nav-2: #1e4810;      /* 1x */
    --shadow-rp-deep-primary: rgba(40,28,12,0.06); /* 1x */
    --shadow-hover-3: rgba(40,28,12,0.13); /* 1x */
    --shadow-piece-img: rgba(0,0,0,0.4); /* 1x */
    --shadow-hover-4: rgba(60,40,10,0.25); /* 1x */
    --shadow-fable-ch-menu: rgba(0,0,0,0.35); /* 1x */
    --shadow-fable-mark: rgba(160,40,24,0.65); /* 1x */
    --shadow-50: rgba(62,120,24,0.35);   /* 1x */
    --shadow-ms-oc-star: rgba(0,0,0,0.22); /* 1x */
    --shadow-ms-startbar: rgba(0,0,0,0.18); /* 1x */
    --shadow-0: rgba(170,30,20,0.55);    /* 1x */
    --shadow-100: rgba(170,30,20,0);     /* 1x */
    --shadow-0-2: rgba(40,120,30,0.55);  /* 1x */
    --shadow-100-2: rgba(40,120,30,0);   /* 1x */
    --shadow-disabled-4: rgba(60,42,12,0.16); /* 1x */
    --shadow-btn-ghost: rgba(60,42,12,0.1); /* 1x */
    --shadow-book-keep-btnactive: rgba(62,120,24,0.22); /* 1x */
    --shadow-disabled-5: rgba(60,42,12,0.17); /* 1x */
    --shadow-disabled-6: rgba(255,255,255,0.45); /* 1x */
    --shadow-disabled-7: rgba(60,42,12,0.12); /* 1x */
    --shadow-ms-tile-2: rgba(60,42,12,0.13); /* 1x */
    --shadow-hover-5: rgba(60,42,12,0.2); /* 1x */
    --shadow-ms-report: rgba(20,12,4,0.45); /* 1x */
    --shadow-ep-modal-danger: rgba(120,30,30,0.3); /* 1x */
    --shadow-disabled-8: rgba(120,30,30,0.32); /* 1x */
    --shadow-ep-modal: rgba(30,16,8,0.42); /* 1x */
    --shadow-fable-ch-menu-2: rgba(60,42,12,0.28); /* 1x */
    --shadow-ms-startbar-2: rgba(60,42,12,0.18); /* 1x */
    --shadow-rp-act: rgba(58,40,20,0.05); /* 1x */
    --shadow-rp-chapter-cardrpcrpc-next: rgba(58,40,20,0.07); /* 1x */
    --shadow-rp-chapter-cardrpcrpc-next-2: rgba(58,40,20,0.11); /* 1x */
    --shadow-before: rgba(62,120,24,0.75); /* 1x */
    --shadow-summary: rgba(58,40,20,0.06); /* 1x */

    /* ---- The door screen (work item 7, step 5) ---- */
    /* The wood of the archway around each route's board. Not board colours: the
       board itself stays out of the palette by design (see CLAUDE.md), and these
       are the frame around it. */
    --bg-door-top: #ded1a3;
    --bg-door-frame: #7d4c1c;
    --bg-door-frame2: #5a3612;
    --shadow-door-inset: rgba(0,0,0,0.25);
    --shadow-door-key: rgba(0,0,0,0.4);

    /* ---- Their reply, on the "Add a line" screen (Ed, 2026-09-16) ---- */
    /* The card's own surface, the green wash on the reply being offered, the ink
       on every move badge, and the two bars that close it. The badge FILLS are
       NOT here: each one is set inline from RP_OPT_COLORS so it matches its own
       arrow, and those stay out of the palette with the rest of the board's
       colours. */
    --bg-rp-alt: #fffaf0;
    --bg-rp-alt-lead: rgba(62,120,24,0.14);
    --ink-rp-alt-badge: #ffffff;
    --bg-rp-alt-save: rgba(62,120,24,0.08);
    --line-rp-alt-save: rgba(62,120,24,0.45);
    --bg-rp-alt-back: rgba(255,250,235,0.5);
    /* the build-a-reply tabs: the track they sit in, pressed into the panel */
    --bg-rp-navtabs: rgba(100,70,10,0.10);
    --shadow-rp-navtabs: rgba(60,42,12,0.10);
  }

  * { box-sizing: border-box; margin: 0; padding: 0; touch-action: manipulation; }

  body {
    font-family: 'Segoe UI', system-ui, sans-serif;
    background: var(--bg);
    background-image:
      radial-gradient(ellipse at 10% 10%, var(--bg-body-2) 0%, transparent 50%),
      radial-gradient(ellipse at 90% 90%, var(--bg-body-3) 0%, transparent 50%),
      repeating-linear-gradient(
        98deg,
        transparent 0px,
        transparent 20px,
        var(--bg-body) 20px,
        var(--bg-body) 22px
      );
    color: var(--text);
    min-height: 100vh;
    min-height: 100dvh;
    /* A DEFINITE height, not just a minimum: min-height alone leaves body's
       height auto, so it grows to its content and every flex:1 pane below
       inherits that instead of the viewport — tall content then slid under the
       fixed nav rather than scrolling in its own pane. Mobile resets this to
       auto (see the max-width:760px block), where the page scrolls by design. */
    height: 100vh;
    height: 100dvh;
    display: flex;
    flex-direction: column;
  }

  header {
    background: linear-gradient(180deg, var(--bg-header) 0%, var(--bg-header-2) 100%);
    border-bottom: 2px solid var(--line-header);
    box-shadow: 0 2px 8px var(--shadow-header);
    padding: 12px 24px;
    display: flex;
    align-items: center;
    gap: 20px;
  }

  header h1 {
    font-size: 1.2rem;
    font-weight: 700;
    white-space: nowrap;
    color: var(--ink-h1);
    letter-spacing: 1px;
  }

  .main {
    display: flex;
    flex: 1;
    /* A flex child defaults to min-height:auto and refuses to shrink below its
       content, so .main grew past the viewport and tall content slid behind the
       fixed bottom nav instead of scrolling in its own pane. Mobile sets this
       back to auto, where the whole page scrolls by design. */
    min-height: 0;
    overflow: hidden;
  }

  /* ===== OPENING BOOK ===== */
  #book-view { display:none; flex:1; overflow:hidden; }
  .book-body { display:flex; flex:1; overflow:hidden; height:100%; }
  .book-content { flex:1; overflow-y:auto; padding:32px 40px; }
  .book-article { max-width:700px; margin:0 auto; }
  .book-opening-title { font-size:1.55rem; font-weight:700; color:var(--accent2); margin-bottom:4px; }
  .book-eco { font-size:0.75rem; color:var(--text-dim); font-family:monospace; margin-bottom:16px; }
  .book-intro-text {
    font-size:0.92rem; color:var(--text); line-height:1.75;
    margin-bottom:28px; padding:14px 16px;
    background:var(--surface); border-left:3px solid var(--accent2);
    border-radius:0 6px 6px 0;
  }
  .book-section-divider {
    font-size:0.88rem; font-weight:700; color:var(--text);
    margin-bottom:14px; padding-bottom:6px; border-bottom:1px solid var(--border);
  }
  .book-var-group { margin-bottom:22px; }
  .book-var-name {
    font-size:0.8rem; font-weight:700; text-transform:uppercase;
    letter-spacing:.4px; color:var(--accent2); margin-bottom:8px;
  }
  .book-line-card {
    margin-bottom:8px; padding:11px 14px;
    background:var(--surface); border:1px solid var(--border); border-radius:6px;
  }
  .book-line-card-name { font-size:0.85rem; font-weight:600; color:var(--text); margin-bottom:4px; }
  .book-line-card-desc { font-size:0.8rem; color:var(--text-dim); line-height:1.55; }
  .book-tags { display:flex; flex-wrap:wrap; gap:5px; margin-bottom:20px; }
  .book-tag {
    display:inline-block; padding:3px 10px; border-radius:10px;
    font-size:0.72rem; font-weight:700; letter-spacing:.2px;
  }
  .btag-attack  { background:var(--bg-btag-attack);  color:var(--ink-btag-attack); border:1px solid var(--line-btag-attack); }
  .btag-solid   { background:var(--bg-btag-solid);  color:var(--ink-btag-solid); border:1px solid var(--line-btag-solid); }
  .btag-theory  { background:var(--bg-btag-theory); color:var(--ink-btag-theory); border:1px solid var(--line-btag-theory); }
  .btag-access  { background:var(--bg-btag-access);  color:var(--ink-btag-access); border:1px solid var(--line-btag-access); }
  .btag-dynamic { background:var(--bg-btag-dynamic); color:var(--ink-btag-dynamic); border:1px solid var(--line-btag-dynamic); }
  .book-var-card {
    margin-bottom:10px; padding:13px 16px;
    background:var(--surface); border:1px solid var(--border); border-radius:8px;
    cursor:pointer; transition:border-color .15s, box-shadow .15s;
  }
  .book-var-card:hover { border-color:var(--accent2); box-shadow:0 2px 8px var(--shadow-hover); }
  .book-var-card-name { font-size:0.92rem; font-weight:700; color:var(--text); margin-bottom:5px; display:flex; align-items:center; gap:10px; }
  .book-var-count { font-size:0.7rem; font-weight:600; color:var(--text-dim); background:var(--surface2); border:1px solid var(--border); border-radius:8px; padding:1px 7px; }
  .book-var-card-desc { font-size:0.8rem; color:var(--text-dim); line-height:1.55; }
  .book-line-card-click { cursor:pointer; transition:border-color .15s, box-shadow .15s; }
  .book-line-card-click:hover { border-color:var(--accent2); box-shadow:0 2px 8px var(--shadow-hover); }
  .book-line-arrow { font-size:0.75rem; color:var(--text-dim); margin-left:6px; }
  /* Variation accordion on the opening page: a tap-to-expand header per variation
     with its lines revealed inline (see openBookOpening). */
  .book-var-item {
    margin-bottom:10px; background:var(--surface);
    border:1px solid var(--border); border-radius:8px; overflow:hidden;
  }
  .book-var-head {
    display:flex; align-items:flex-start; justify-content:space-between; gap:12px;
    padding:13px 16px; cursor:pointer; user-select:none; transition:background .12s;
  }
  .book-var-head:hover { background:var(--bg-hover-6); }
  .book-var-head-text { flex:1; min-width:0; }
  .book-var-caret {
    flex-shrink:0; color:var(--text-dim); font-size:0.8rem; margin-top:3px;
    transition:transform .2s ease;
  }
  .book-var-item.open .book-var-caret { transform:rotate(90deg); }
  .book-var-lines { display:none; padding:8px 14px 6px; border-top:1px solid var(--border); }
  .book-var-item.open .book-var-lines { display:block; }
  /* The move-named variations sit under one disclosure below the named ones.
     It reuses the chapter page's .rp-ch-extra pill so the two read as the same
     control; only the body needs undoing, because that rule's left rule and
     inset are sized for narrow chapter rows, not full-width variation cards. */
  /* The opening page is the longest scroll in the app (the Ruy Lopez runs to 30
     variation cards), so its trail stays at the top rather than disappearing
     with the header (Ed, 2026-08-25: "we have so long to scroll"). Same
     treatment as the focus-mode bar: a deliberate surface, because the page
     background carries two large radial tints and a see-through strip shows a
     seam. No safe-area inset here, unlike focus mode, because the header is
     still above it. */
  .book-crumbs.book-op-crumbs {
    position:sticky; top:0; z-index:60;
    background:var(--surface); border-bottom:1px solid var(--border);
    box-shadow:0 2px 6px var(--shadow-hover);
    margin-left:-100vw; margin-right:-100vw;
    padding-left:100vw; padding-right:100vw;
    padding-top:10px; padding-bottom:10px; margin-bottom:14px;
  }
  .book-var-more { margin-top:18px; }
  .book-var-more > .rpx-body { margin-top:14px; padding-left:0; border-left:0; }
  .book-movelist {
    font-family:'Roboto Mono','Courier New',monospace; font-size:0.88rem;
    line-height:2; color:var(--text); padding:12px 16px;
    background:var(--surface); border:1px solid var(--border); border-radius:6px;
  }
  .book-move-num { color:var(--text-dim); }
  .book-move { font-weight:600; }
  .book-line-inner { display:flex; flex-direction:column; width:100%; max-width:860px; padding:16px 0; gap:10px; }
  .book-board-center { display:flex; gap:14px; align-items:flex-start; justify-content:center; }
  .book-board-col { display:flex; flex-direction:column; gap:8px; align-items:center; flex:0 1 480px; min-width:0; }
  #book-board-wrap { width:100%; }
  .book-pos-label { font-size:0.82rem; color:var(--text-dim); text-align:center; min-height:1.1em; }
  .bk-arrow-labels { display:flex; flex-direction:column; gap:5px; max-width:580px; width:100%; box-sizing:border-box; padding-top:6px; }
  .bk-arrow-label { display:flex; align-items:center; gap:8px; font-size:0.8rem; color:var(--text-dim); line-height:1.4; }
  .bk-arrow-dot { width:10px; height:10px; border-radius:50%; flex-shrink:0; }
  .book-back-btn {
    background:none; border:1px solid var(--border); border-radius:5px;
    padding:5px 12px; cursor:pointer; font-size:0.8rem; color:var(--text-dim);
    transition:all .1s;
  }
  .book-back-btn:hover { border-color:var(--accent2); color:var(--accent2); }

  /* ===== BOTTOM TAB BAR ===== */
  .bottom-nav {
    display: flex;
    position: fixed;
    bottom: 0; left: 0; right: 0;
    z-index: 100;
    background: linear-gradient(180deg, var(--bg-bottom-nav) 0%, var(--bg-bottom-nav-2) 100%);
    border-top: 2px solid var(--line-bottom-nav);
    /* The second shadow is a blur-free block of the nav's own darkest colour,
       painted 60px BELOW the bar. iOS standalone quirk: on the very first paint
       of a launch this fixed bar lands about a home-indicator's height above the
       true screen bottom (bottom:0 resolves against a viewport iOS hasn't yet
       extended into the safe area), so page background shows through under it
       until you scroll once. The block fills that strip in the bar's colour.
       A shadow never affects layout, hit-testing, or scroll height, and once
       iOS settles the bar at the bottom the block is simply off-screen. */
    box-shadow: 0 -2px 12px var(--shadow-bottom-nav), 0 60px 0 0 var(--shadow-bottom-nav-2);
    /* 54px of tappable nav PLUS room for the iPhone home indicator below it
       (box-sizing is border-box, so the inset must be added to the height, not
       just padded in, or it eats into the icons/labels). */
    height: calc(54px + env(safe-area-inset-bottom, 0px));
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  /* Keep content from sliding under the fixed nav (+ home indicator) */
  body { padding-bottom: calc(54px + env(safe-area-inset-bottom, 0px)); }

  /* The Openings Book is hidden. The class is stamped on <html> in index.html's
     head, before the body parses, so the button is never painted and then taken
     away. The button stays in the markup rather than being cut out of it, so
     flipping window.EP_BOOK_HIDDEN brings the whole tab back in one edit. The
     remaining two tabs are flex:1 each, so they split the bar between them with
     no other rule needed. */
  .ep-no-book #tab-book { display: none !important; }

  .bottom-tab {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    cursor: pointer;
    border: none;
    background: transparent;
    color: var(--ink-bottom-tab);
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.5px;
    transition: color 0.18s;
    position: relative;
    padding: 0;
  }

  .bottom-tab .tab-icon {
    font-size: 1.2rem;
    line-height: 1;
    transition: transform 0.18s;
  }

  .bottom-tab:hover {
    color: var(--ink-hover);
  }

  .bottom-tab.active {
    color: var(--ink-bottom-tabactive);
  }

  .bottom-tab.active .tab-icon {
    transform: translateY(-1px) scale(1.08);
  }

  /* Soft pill highlight behind the active tab instead of a floating line */
  .bottom-tab.active::before {
    content: '';
    position: absolute;
    top: 2px; bottom: 4px;
    left: 12px; right: 12px;
    background: var(--bg-before);
    border-radius: 12px;
    z-index: -1;
  }

  /* ===== REPERTOIRE TAB ===== */
  #repertoire-view {
    display: none;
    flex: 1;
    overflow: hidden;
    flex-direction: column;
  }
  #rep2-home, #rep2-chapters, #rep2-reps, #rep2-color { flex:1; overflow-y:auto; padding:28px 36px 60px; }
  .rp-home-top { max-width:760px; margin:0 auto 6px; display:flex; align-items:center; justify-content:space-between; gap:10px; }
  .rp-current-rep { font-family:Georgia,serif; font-weight:700; color:var(--accent2); }
  .rp-color-pick-card { max-width:760px; margin:0 auto 14px; display:flex; align-items:center; gap:16px; cursor:pointer; transition:border-color .12s, box-shadow .12s; }
  .rp-color-pick-card:hover { border-color:var(--accent); box-shadow:0 0 0 1px var(--accent); }
  .rp-color-pick-icon { font-size:2.2rem; line-height:1; }
  .rp-color-pick-go { font-size:1.5rem; color:var(--accent2); }
  .rp-rep-card { display:flex; align-items:center; justify-content:space-between; gap:14px; }
  .rp-rep-main { flex:1; cursor:pointer; min-width:0; }
  .rp-rep-actions { display:flex; gap:8px; flex-wrap:wrap; flex-shrink:0; }
  .rp-rep-editing { gap:10px; }
  .rp-rep-editing .rp-create-input { flex:1; }
  .rp-rep-confirm { border-color:var(--line-rp-rep-confirm); }
  .rp-danger-btn { background:var(--bg-rp-danger-btn); color:var(--ink-rp-danger-btn); }
  .rp-danger-btn:hover { background:var(--bg-hover-7); }
  .rp-create-label { display:block; font-size:0.8rem; font-weight:700; color:var(--accent2); margin-bottom:6px; }
  .rp-create-input { width:100%; box-sizing:border-box; padding:10px 12px; border:1px solid var(--border); border-radius:8px; font-size:1rem; background:var(--bg); color:var(--text); }
  .rp-ch-actions { display:flex; gap:10px; justify-content:center; flex-wrap:wrap; margin-top:14px; }
  .rp-chapter-card { max-width:760px; margin:0 auto 14px; background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:14px 18px; }
  .rp-chapter-card.learned { border-color:var(--accent); }
  .rp-chapter-head { display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:8px; }
  .rp-chapter-name { font-family:Georgia,serif; font-size:1.05rem; font-weight:700; color:var(--accent2); }
  .rp-ch-badge { font-size:0.7rem; font-weight:700; text-transform:uppercase; color:var(--text-dim); background:var(--bg); border:1px solid var(--border); border-radius:4px; padding:2px 8px; white-space:nowrap; }
  .rp-ch-badge.learned { color:var(--ink-rp-danger-btn); background:var(--accent); border-color:var(--accent); }
  .rp-ch-badge.warn { color:var(--ink-rp-danger-btn); background:var(--bg-rp-ch-badgewarn); border-color:var(--line-rp-ch-badgewarn); }
  .rp-ch-badge.dim { opacity:0.7; }
  .rp-ch-warn { color:var(--ink-rp-ch-warn); }
  .rp-ch-lines { display:flex; flex-direction:column; gap:3px; }
  .rp-ch-line { font-size:0.82rem; color:var(--text-dim); padding:2px 0; }
  .rp-ch-line.walked { color:var(--text); }
  .rp-ch-line.clickable { cursor:pointer; border-radius:4px; padding:2px 6px; margin:0 -6px; transition:background 0.12s; }
  .rp-ch-line.clickable:hover { background:var(--bg-hover-8); }
  .rp-ch-review { float:right; color:var(--text-dim); font-size:0.74rem; opacity:0; transition:opacity 0.12s; }
  .rp-ch-line.clickable:hover .rp-ch-review { opacity:1; }
  .rp-ch-tick { display:inline-block; width:16px; color:var(--accent); }
  .rp-ch-reply { font-size:0.88rem; color:var(--text); margin:-2px 0 8px; }
  .rp-ch-reply.dim { color:var(--text-dim); margin-bottom:0; }
  .rp-progress { height:6px; background:var(--bg); border:1px solid var(--border); border-radius:4px; overflow:hidden; }
  .rp-progress-fill { height:100%; background:var(--accent); border-radius:4px; transition:width 0.3s; }
  .rp-progress-note { font-size:0.72rem; color:var(--text-dim); margin:4px 0 10px; }
  .rp-prep { max-width:760px; margin:0 auto 14px; background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:12px 18px; }
  .rp-prep-head { font-size:0.9rem; color:var(--text); margin-bottom:8px; }
  .rp-prep-bar { display:flex; gap:4px; }
  .rp-prep-seg { flex:1; height:10px; border-radius:3px; background:var(--bg); border:1px solid var(--border); }
  .rp-prep-seg.ready { background:var(--accent); border-color:var(--accent); }
  .rp-prep-seg.partial { background:repeating-linear-gradient(45deg, var(--accent), var(--accent) 3px, var(--bg) 3px, var(--bg) 6px); }
  .rp-ch-var { border-top:1px solid var(--border); padding:7px 0 5px; }
  .rp-ch-var-head { display:flex; justify-content:space-between; gap:10px; font-size:0.85rem; font-weight:700; color:var(--accent2); margin-bottom:3px; }
  .rp-ch-var-count { font-weight:400; font-size:0.75rem; color:var(--text-dim); white-space:nowrap; }
  /* While the app looks something up: one moving hairline, no card, no words
     (Ed, 2026-09-17). It replaces the italic sentence every waiting screen used
     to carry. The panel is bare underneath it, so this sits on the paper. */
  .rp-think { height:3px; border-radius:2px; overflow:hidden; position:relative;
    background:var(--bg-rp-think-track); }
  .rp-think i { position:absolute; top:0; bottom:0; width:38%; border-radius:2px;
    background:var(--accent); animation: rpThinkRun 1.25s ease-in-out infinite; }
  @keyframes rpThinkRun { 0% { left:-38%; } 100% { left:100%; } }
  .rp-opening-card { max-width:760px; margin:0 auto 20px; background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:18px 20px; }
  .rp-opening-head { display:flex; align-items:center; justify-content:space-between; gap:10px; }
  .rp-opening-name { font-family:Georgia,serif; font-size:1.3rem; font-weight:700; color:var(--accent2); }
  .rp-opening-intro { font-size:0.85rem; color:var(--text-dim); line-height:1.45; margin:8px 0 14px; }
  .rp-card-status { font-size:0.78rem; color:var(--text-dim); margin:-4px 0 14px; }
  .rp-card-done { color:var(--accent); font-weight:600; }
  .rp-card-actions { display:flex; gap:8px; flex-wrap:wrap; }
  /* Repertoire openings home: search + compact alphabetical grid */
  .rp-open-search { display:block; width:100%; max-width:760px; box-sizing:border-box; margin:0 auto 16px; padding:11px 14px; font-size:0.95rem; border:1px solid var(--border); border-radius:10px; background:var(--surface); color:var(--text); }
  .rp-open-search::placeholder { color:var(--text-dim); }
  .rp-open-search:focus { outline:none; border-color:var(--accent); }
  .rp-open-section { max-width:760px; margin:0 auto 22px; }
  .rp-open-section-head { font-family:-apple-system,sans-serif; font-size:0.72rem; font-weight:700; text-transform:uppercase; letter-spacing:0.06em; color:var(--accent2); margin:0 2px 10px; display:flex; align-items:center; gap:8px; }
  .rp-open-section-count { font-weight:700; color:var(--text-dim); background:var(--surface2); border:1px solid var(--border); border-radius:999px; padding:0 8px; font-size:0.7rem; }
  .rp-open-grid { max-width:760px; margin:0 auto; display:grid; grid-template-columns:repeat(auto-fill, minmax(210px, 1fr)); gap:12px; }
  .rp-open-tile { display:flex; flex-direction:column; gap:8px; background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:14px 16px; cursor:pointer; transition:transform .12s ease, border-color .12s ease, box-shadow .12s ease; }
  .rp-open-tile:hover { transform:translateY(-2px); border-color:var(--accent2); box-shadow:0 4px 14px var(--shadow-hover-2); }
  .rp-open-tile.done { border-color:var(--accent); }
  .rp-open-tile-name { font-family:Georgia,serif; font-size:1.05rem; font-weight:700; color:var(--accent2); line-height:1.25; }
  .rp-open-tile-status { font-size:0.76rem; color:var(--text-dim); }
  .rp-open-tile-status.done, .rp-open-tile-status.ready { color:var(--accent); font-weight:600; }
  /* The tile's progress bar went on 2026-08-30, with the same missing total the
     chapter cards' bar was measuring. See rpRenderHome. */
  .rp-custom-badge { display:inline-block; font-family:-apple-system,sans-serif; font-size:0.6rem; font-weight:700; text-transform:uppercase; letter-spacing:0.04em; color:var(--accent2); background:var(--surface2); border:1px solid var(--border); border-radius:6px; padding:1px 5px; vertical-align:middle; }
  .rp-conflict { font-size:0.85rem; line-height:1.4; border-radius:10px; padding:10px 12px; margin:10px 0; border:1px solid var(--border); }
  .rp-conflict-ok { color:var(--accent); background:var(--bg-rp-conflict-ok); border-color:var(--line-rp-conflict-ok); }
  .rp-conflict-warn { color:var(--ink-rp-conflict-warn); background:var(--bg-rp-conflict-warn); border-color:var(--line-rp-conflict-warn); }
  .rp-conflict-block { color:var(--ink-rp-conflict-block); background:var(--bg-rp-conflict-block); border-color:var(--line-rp-conflict-block); }
  @media (max-width:480px) { .rp-open-grid { grid-template-columns:repeat(auto-fill, minmax(150px, 1fr)); gap:10px; } }
  .rp-color-row { display:flex; align-items:center; gap:14px; padding:12px 0; border-top:1px solid var(--border); }
  .rp-color-icon { font-size:1.6rem; }
  .rp-color-info { flex:1; display:flex; flex-direction:column; gap:2px; }
  .rp-status { font-size:0.78rem; color:var(--text-dim); }
  .rp-type-badge { display:inline-block; font-family:-apple-system,sans-serif; font-size:0.62rem; font-weight:700; text-transform:uppercase; letter-spacing:0.04em; padding:2px 7px; border-radius:999px; vertical-align:middle; margin-left:6px; }
  .rp-type-main { background:var(--accent); color:var(--ink-rp-danger-btn); }
  .rp-type-supp { background:var(--border); color:var(--text-dim); }
  .rp-cov { max-width:760px; margin:0 auto 18px; background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:16px 18px; }
  .rp-cov-head { font-family:Georgia,serif; font-size:1.15rem; font-weight:700; color:var(--accent2); margin-bottom:10px; }
  .rp-cov-head .rp-status { font-weight:400; }
  .rp-cov-empty { font-size:0.9rem; color:var(--text-dim); line-height:1.5; margin:0; }
  .rp-cov-bar { height:14px; border-radius:999px; background:var(--border); overflow:hidden; }
  .rp-cov-bar-fill { height:100%; background:var(--accent); border-radius:999px; transition:width .3s; }
  .rp-cov-pct { font-size:0.88rem; color:var(--text); margin:7px 0 4px; }
  .rp-cov-conflict { font-size:0.82rem; color:var(--ink-rp-conflict-block); background:var(--bg-rp-conflict-block); border-radius:8px; padding:8px 10px; margin:10px 0 4px; }
  .rp-cov-section-title { font-size:0.78rem; font-weight:700; text-transform:uppercase; letter-spacing:0.03em; color:var(--text-dim); margin:14px 0 8px; }
  .rp-cov-gap { display:flex; align-items:center; gap:10px; flex-wrap:wrap; padding:9px 0; border-top:1px solid var(--border); }
  .rp-cov-gap-info { flex:1; min-width:180px; display:flex; flex-direction:column; gap:2px; }
  .rp-cov-gap-cands { display:flex; gap:6px; flex-wrap:wrap; align-items:center; }
  .rp-cov-allgood { font-size:0.9rem; color:var(--accent); font-weight:600; margin-top:10px; }
  .rp-cov-more { font-size:0.8rem; color:var(--text-dim); text-align:center; margin-top:8px; }
  .rp-sug-covered-head { margin-top:14px; }
  .rp-color-actions { display:flex; gap:8px; }
  .rp-small-btn { font-size:0.78rem; padding:6px 14px; }
  .rp-more-btn { font-size:0.72rem; padding:3px 10px; margin-top:5px; }
  .rp-deep-modes { display:flex; flex-direction:column; gap:12px; margin-bottom:14px; }
  .rp-deep-mode { display:flex; flex-direction:column; align-items:flex-start; gap:4px; text-align:left; padding:14px 16px; white-space:normal; height:auto; line-height:1.4; }
  .rp-deep-mode b { font-size:0.95rem; }
  .rp-deep-mode span { font-size:0.8rem; font-weight:400; opacity:0.88; }
  /* "Go deeper" chooser: one refined primary card (soft green wash, branch icon
     in a chip, chevron) plus a quiet underlined text link for the alt path. */
  .rp-deep-choose { display:flex; flex-direction:column; gap:12px; }
  .rp-deep-primary { display:flex; align-items:center; gap:14px; width:100%; text-align:left;
    padding:15px 16px; border-radius:16px; cursor:pointer; font-family:inherit; color:var(--text);
    background:var(--bg-rp-deep-primary); border:1px solid var(--line-rp-deep-primary);
    box-shadow:0 2px 10px var(--shadow-rp-deep-primary);
    transition:transform .12s ease, box-shadow .12s ease, border-color .12s ease; }
  .rp-deep-primary:hover { transform:translateY(-1px); box-shadow:0 6px 16px var(--shadow-hover-3);
    border-color:var(--accent); }
  .rp-deep-ic { flex:none; width:42px; height:42px; border-radius:50%;
    display:flex; align-items:center; justify-content:center;
    background:var(--bg-rp-deep-ic); color:var(--accent); }
  .rp-deep-txt { flex:1; min-width:0; display:flex; flex-direction:column; gap:3px; }
  .rp-deep-txt b { font-family:Georgia,serif; font-size:1rem; line-height:1.25; }
  .rp-deep-txt span { font-size:0.8rem; color:var(--text-dim); line-height:1.4; }
  .rp-deep-chev { flex:none; color:var(--accent); font-size:1.15rem; opacity:0.75; }
  .rp-deep-alt { display:block; width:100%; text-align:center; background:none; border:none;
    cursor:pointer; font-family:inherit; font-size:0.85rem; color:var(--text-dim);
    padding:6px 4px; text-decoration:underline; text-underline-offset:3px;
    text-decoration-color:var(--ink-rp-deep-alt); transition:color .12s ease; }
  .rp-deep-alt:hover { color:var(--accent); }
  .rp-coming { max-width:760px; margin:0 auto; text-align:center; font-size:0.8rem; color:var(--text-dim); opacity:0.8; }
  .rp-builder-panel { width:400px; max-height:540px; overflow-y:auto; background:var(--surface); border:1px solid var(--border); border-radius:10px; padding:16px; }
  .rp-question { font-family:Georgia,serif; font-size:0.98rem; line-height:1.55; margin-bottom:14px; }
  .rp-options { display:flex; flex-direction:column; gap:10px; }
  .rp-option { border:2px solid var(--border); border-radius:10px; padding:10px 12px; cursor:pointer; background:var(--bg); transition:border-color 0.12s; }
  .rp-option:hover { border-color:var(--accent2); }
  .rp-option.selected { border-color:var(--accent); background:var(--surface2); }
  .rp-option.current { box-shadow:0 0 0 1px var(--accent); }
  .rp-option-head { display:flex; align-items:baseline; gap:8px; flex-wrap:wrap; }
  .rp-option-san { font-weight:800; font-size:1.05rem; color:var(--accent2); }
  .rp-option-title { font-family:Georgia,serif; font-weight:700; font-size:0.92rem; }
  .rp-option-badge { font-size:0.65rem; font-weight:700; text-transform:uppercase; color:var(--ink-rp-danger-btn); background:var(--accent); border-radius:4px; padding:2px 6px; margin-left:auto; }
  /* Matches RP_OPT_COLORS[0], the recommended card's arrow (Ed, 2026-08-06). */
  .rp-option-badge.recommend { background:var(--bg-rp-option-badgerecommend); margin-left:0; }
  .rp-option-badge.lastpick { background:var(--accent); margin-left:0; }
  .rp-option-eval { font-family:monospace; font-size:0.8rem; font-weight:700; color:var(--text-dim); margin-left:auto; }
  .rp-option-eval:empty::before { content:'…'; opacity:0.5; }
  .rp-option-story { font-family:Georgia,serif; font-size:0.85rem; line-height:1.5; color:var(--text); margin-top:6px; }
  .rp-option-meta { font-size:0.72rem; color:var(--text-dim); margin-top:6px; }
  .rp-choose-btn { margin-top:10px; width:100%; }
  .rp-review-actions { margin-top:14px; display:flex; justify-content:center; }
  .rp-fork-list { display:flex; flex-direction:column; gap:18px; }
  .rp-fork-group { display:flex; flex-direction:column; gap:8px; }
  .rp-fork-group-head { font-size:0.72rem; font-weight:700; text-transform:uppercase; letter-spacing:.4px; color:var(--accent2); padding-bottom:5px; border-bottom:1px solid var(--border); }
  .rp-fork-card { background:var(--bg); border:1px solid var(--border); border-radius:8px; overflow:hidden; }
  .rp-fork-card.open { border-color:var(--accent2); }
  .rp-fork-card.missing { border-color:var(--wrong); }
  .rp-fork-head { display:flex; align-items:center; gap:8px; padding:9px 12px; cursor:pointer; transition:background 0.12s; }
  .rp-fork-head:hover { background:var(--bg-hover); }
  .rp-fork-ctx { font-size:0.76rem; color:var(--text-dim); white-space:nowrap; }
  .rp-fork-num { font-weight:700; color:var(--accent2); }
  .rp-fork-pick { font-size:0.82rem; color:var(--text); margin-left:auto; text-align:right; }
  .rp-fork-pick.none { color:var(--wrong); font-style:italic; }
  .rp-fork-caret { font-size:0.7rem; color:var(--text-dim); width:12px; text-align:center; }
  .rp-fork-body { padding:4px 12px 12px; }
  .rp-fork-hint { font-size:0.74rem; color:var(--text-dim); line-height:1.45; margin:2px 0 10px; }
  .rp-fork-actions { margin-top:8px; }
  #rep2-msg { padding:14px 16px; min-height:88px; }
  #rep2-history { flex:1; overflow-y:auto; padding:8px 14px; border-top:1px solid var(--border); }
  /* The Repertoire builder board is now the responsive .epb board (see app-theme.css);
     no fixed --bk-sq sizing. Give its column a basis so the board fills it cleanly
     alongside the side panel, and let the board wrap stretch. */
  #rp-build-board-wrap { width:100%; }
  .master-board-row .fable-board-col { flex:0 1 500px; min-width:0; width:100%; max-width:520px; }
  .rp-walk-caption { font-family:Georgia,serif; font-size:0.98rem; line-height:1.6; }
  .rp-walk-flavor { margin-top:8px; font-size:0.85rem; color:var(--accent2); font-style:italic; }
  .rp-walk-rewind { color:var(--ink-rp-walk-rewind); }
  .rp-walk-intro { margin-bottom:10px; padding-bottom:10px; border-bottom:1px solid var(--line-rp-walk-intro); }
  .rp-walk-lineend-title { font-family:Georgia,serif; font-weight:700; font-size:1.05rem; color:var(--correct); margin-bottom:4px; }
  .rp-walk-lineend-count { font-size:0.82rem; color:var(--text-dim); margin-bottom:8px; }
  /* The move list sits under the walk's notes, with a hairline separating them.
     :first-child drops the rule when the move list IS the panel — the analysis
     board has no notes above it (Ed, 2026-08-09), and a separator with nothing
     on one side of it is just a stray line. */
  .rp-builder-panel .ep-moves { display:block; margin-top:14px; padding-top:12px;
    border-top:1px solid var(--border); }
  .rp-builder-panel .ep-moves:first-child { margin-top:0; padding-top:0; border-top:none; }
  /* Analyze, under the move list and sharing its hairline (Ed, 2026-09-15: "move
     the analyze button to the bottom... they won't be used as often here", then
     "can we do that everywhere else possible too?"). Same pill as .ep-moves-sum on
     purpose, so the foot of the card reads as one quiet row of two rather than a
     button. Every screen in the Repertoire tab that offers Analyze uses it now;
     rpAnalyzeFootHtml is the one builder. Top level, not inside a media query, so
     it is the same at every width. */
  .rp-ans-foot { text-align:center; margin-top:10px; }
  .rp-ans-analyze { cursor:pointer;
    display:inline-flex; align-items:center; gap:7px;
    font-family:Georgia,serif; font-size:0.72rem; font-weight:700;
    text-transform:uppercase; letter-spacing:0.7px; color:var(--text-dim);
    padding:6px 15px; border-radius:999px; border:1px solid var(--border);
    background:var(--surface); transition:color .12s ease, border-color .12s ease; }
  .rp-ans-analyze:hover { color:var(--text); border-color:var(--text-dim); }

  /* ===== REP PRACTICE SUB-VIEW ===== */
  .rep-practice-back {
    background: none; border: 1px solid var(--line-rep-practice-back);
    color: var(--ink-h1); font-size: 0.85rem; padding: 5px 14px;
    border-radius: 4px; cursor: pointer;
  }
  .rep-practice-back:hover { background: var(--bg-hover-9); }
  .rep-info-banner {
    width: 100%;
    background: linear-gradient(135deg, var(--bg-header) 0%, var(--bg-header-2) 100%);
    border-radius: 8px;
    padding: 8px 12px;
    /* Fixed height so the board doesn't jump when the title text changes
       length (e.g. switching into Analysis mode). Content stays centered. */
    min-height: 76px; box-sizing: border-box;
    display: flex; align-items: center; gap: 10px;
    box-shadow: 0 2px 8px var(--shadow-rep-info-banner);
  }
  .rep-banner-center {
    flex: 1; text-align: center;
  }
  .rep-practice-bar-title {
    color: var(--ink-h1); font-weight: 700; font-size: 0.95rem; line-height: 1.3;
  }
  .rep-practice-line-name {
    color: var(--ink-rep-practice-line-name); font-size: 0.78rem; margin-top: 1px;
  }
  .rep-move-history-header {
    padding: 7px 10px; font-size: 0.75rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.6px; flex-shrink: 0;
    color: var(--text-dim); background: var(--surface2);
    border: 1px solid var(--border); border-bottom: none;
    border-radius: 6px 6px 0 0;
  }
  .rep-move-history {
    flex: 1; overflow-y: auto; padding: 4px 2px; min-height: 0;
    background: linear-gradient(135deg, var(--surface) 0%, var(--bg-rep-move-history) 100%);
    border: 1px solid var(--border); border-radius: 0 0 6px 6px;
    font-family: 'Roboto Mono', 'Courier New', monospace; font-size: 0.82rem;
    box-shadow: inset 0 1px 4px var(--shadow-rep-move-history);
  }
  .rep-move-history .move-row { grid-template-columns: 26px 1fr 1fr; padding: 2px 6px; }

  /* ===== Line-study side panel + nav controls (shared: Book, Repertoire, Fable) ===== */
  .study-nav { display: flex; gap: 8px; align-items: center; }
  /* Walk nav: Chapters pinned left, Analyze pinned right, Prev/Next centered so
     the button you keep clicking stays put. Three flex zones + space-between. */
  #rep2-walk-nav { justify-content: space-between; }
  #rep2-walk-nav .study-nav-side, #rep2-walk-nav .study-nav-mid {
    display: inline-flex; gap: 8px; align-items: center; flex-wrap: wrap; }
  #rep2-walk-nav .study-nav-mid { justify-content: center; }

  .player-color-badge {
    display: inline-block;
    padding: 2px 10px;
    border-radius: 10px;
    font-size: 0.75rem;
    font-weight: 600;
    margin-top: 6px;
  }

  .player-color-badge.white { background: var(--bg-player-color-badgewhite); color: var(--ink-player-color-badgewhite); }
  .player-color-badge.black { background: var(--bg-player-color-badgeblack); color: var(--ink-player-color-badgeblack); border: 1px solid var(--line-player-color-badgeblack); }

  .board-wrapper {
    position: relative;
  }

  .board-container {
    display: grid;
    grid-template-columns: 24px 1fr;
    grid-template-rows: 1fr 24px;
    gap: 3px;
  }

  .board-ranks {
    display: flex;
    flex-direction: column;
    justify-content: space-around;
    align-items: center;
    font-size: 0.75rem;
    color: var(--text-dim);
    grid-row: 1;
    grid-column: 1;
  }

  .board-files {
    display: flex;
    justify-content: space-around;
    font-size: 0.75rem;
    color: var(--text-dim);
    grid-row: 2;
    grid-column: 2;
  }

  #board {
    grid-row: 1;
    grid-column: 2;
    display: grid;
    grid-template-columns: repeat(8, 80px);
    grid-template-rows: repeat(8, 80px);
    border: 2px solid var(--border);
    user-select: none;
  }

  .square {
    width: 80px;
    height: 80px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    position: relative;
    transition: filter 0.1s;
  }

  .square.light { background: var(--light-sq); }
  .square.dark { background: var(--dark-sq); }
  .square.selected { background: var(--highlight) !important; }
  .square.last-move { background: var(--last-move) !important; }
  .square.legal-target::after {
    content: '';
    position: absolute;
    width: 30%;
    height: 30%;
    border-radius: 50%;
    background: var(--bg-after-2);
  }
  .square.legal-capture::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 0;
    background: transparent;
    border: 4px solid var(--line-after);
    border-radius: 2px;
  }
  .square:hover { filter: brightness(1.15); }
  .square.dragging { opacity: 0.45; }
  .square.drag-over { filter: brightness(1.35) !important; outline: 3px solid var(--line-squaredrag-over); outline-offset: -3px; }
  .square[draggable="true"] { cursor: grab; }

  .piece-img {
    width: 76px;
    height: 76px;
    pointer-events: none;
    display: block;
    filter: drop-shadow(0 1px 3px var(--shadow-piece-img));
    -webkit-touch-callout: none;
    -webkit-user-drag: none;
  }

  .move-row {
    display: grid;
    grid-template-columns: 30px 1fr 1fr;
    padding: 3px 8px;
    border-radius: 4px;
    line-height: 1.7;
    transition: background 0.1s;
  }

  .move-row:hover { background: var(--bg-hover-10); }

  .move-num { color: var(--text-dim); font-size: 0.8rem; }

  .move-w, .move-b {
    color: var(--text);
    padding: 0 4px;
    border-radius: 3px;
    cursor: default;
  }

  .active-move {
    background: var(--bg-active-move);
    color: var(--accent);
    font-weight: 700;
  }

  /* Book study board: each move is clickable to jump there; upcoming (unplayed)
     line moves are dimmed. */
  .book-move { cursor: pointer; }
  .book-move:hover { background: var(--bg-hover-2); color: var(--accent); }
  .move-future { opacity: 0.4; }
  .move-future:hover { opacity: 1; }

  .btn {
    padding: 8px 16px;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 0.85rem;
    font-weight: 600;
    transition: all 0.15s;
    position: relative;
  }

  .btn .kbd {
    font-size: 0.65rem;
    opacity: 0.6;
    margin-left: 5px;
    font-weight: 400;
    font-family: monospace;
  }

  .btn-primary { background: linear-gradient(180deg, var(--bg-btn-primary) 0%, var(--bg-btn-primary-2) 100%); color: var(--ink-btn-primary-2); border: 1px solid var(--line-btn-primary); box-shadow: 0 2px 4px var(--shadow-rep-info-banner); }
  .btn-primary:hover { background: linear-gradient(180deg, var(--bg-hover-11) 0%, var(--accent) 100%); }
  .btn-secondary { background: linear-gradient(180deg, var(--bg-btn-secondary) 0%, var(--bg-btn-secondary-2) 100%); color: var(--text); border: 1px solid var(--border); box-shadow: 0 1px 3px var(--shadow-rep-move-history); }
  .btn-secondary:hover { background: linear-gradient(180deg, var(--bg-hover-3) 0%, var(--bg-hover-12) 100%); }
  .btn-ghost { background: transparent; border: 1px solid var(--border); color: var(--text-dim); }
  .btn-ghost:hover { border-color: var(--accent); color: var(--text); }
  .btn:disabled { opacity: 0.4; cursor: not-allowed; }

  /* ===== SCROLLBAR ===== */
  ::-webkit-scrollbar { width: 6px; }
  ::-webkit-scrollbar-track { background: var(--bg-webkit-scrollbar-track); }
  ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
  ::-webkit-scrollbar-thumb:hover { background: var(--bg-hover-13); }

  /* ECO badge */
  .eco-badge {
    display: inline-block;
    padding: 1px 6px;
    border-radius: 3px;
    font-size: 0.7rem;
    background: var(--surface2);
    color: var(--text-dim);
    font-family: monospace;
    margin-left: 6px;
    vertical-align: middle;
  }

  .board-with-eval {
    display: flex;
    gap: 10px;
    align-items: flex-start;
  }

  .eval-bar-wrap {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding-top: 1px;
  }

  .eval-bar {
    width: 20px;
    height: 640px;
    border-radius: 4px;
    overflow: hidden;
    border: 1px solid var(--border);
    background: var(--bg-eval-bar);            /* Black's side — board dark-square family */
    position: relative;
    box-shadow: inset 0 1px 3px var(--shadow-bottom-nav);
  }

  /* Faint "dead even" line across the midpoint (drawn over the fill).
     Its colour follows whatever sits under it: dark on the cream fill when
     White leads, light on the dark track when Black leads. */
  .eval-bar::after {
    content: '';
    position: absolute;
    left: 0; right: 0; top: 50%;
    height: 1px;
    background: var(--bg-after-3);
    pointer-events: none;
  }
  .eval-bar.lead-black::after { background: var(--bg-after-4); }

  .eval-bar-fill {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    background: var(--light-sq);           /* White's side — board light-square colour */
    transition: height 0.35s cubic-bezier(0.4, 0, 0.2, 1);
  }

  /* While the engine is still calculating, the fill breathes. */
  .eval-bar-fill.thinking { animation: evalPulse 1.1s ease-in-out infinite; }
  @keyframes evalPulse { 0%,100% { opacity: 0.4; } 50% { opacity: 0.85; } }

  /* Board flipped to Black's side → mirror the bar so Black sits at the bottom
     (same as Lichess/Chess.com). Purely visual; fill stays White's-perspective. */
  .eval-bar.eval-bar-flipped { transform: scaleY(-1); }

  /* The score, rendered on the bar at the leading side's end (Lichess-style). */
  .eval-bar-num {
    position: absolute;
    left: 0; right: 0;
    text-align: center;
    font-family: monospace;
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: -0.5px;
    pointer-events: none;
  }
  .eval-bar-num.at-white { bottom: 3px; color: var(--ink-eval-bar-numat-white); }   /* on the cream fill */
  .eval-bar-num.at-black { top: 3px;    color: var(--light-sq); }   /* on the dark track */
  .eval-bar-num.eq       { color: var(--ink-eval-bar-numeq); }
  /* Keep the number upright when the whole bar is flipped for Black. */
  .eval-bar.eval-bar-flipped .eval-bar-num { transform: scaleY(-1); }

  .eval-score {
    font-size: 0.66rem;
    font-weight: 600;
    color: var(--text-dim);
    text-align: center;
    max-width: 58px;
    line-height: 1.2;
  }

  /* ===== FABLE TAB ===== */
  .fable-search-wrap { max-width:1060px; margin:0 auto 24px; }
  .fable-search { width:100%; padding:9px 14px; border-radius:8px; border:1px solid var(--border); background:var(--surface); color:var(--text); font-size:0.95rem; outline:none; box-sizing:border-box; }
  .fable-search:focus { border-color:var(--accent); }
  .fable-no-results { max-width:1060px; margin:0 auto; text-align:center; color:var(--text-dim); font-style:italic; padding:20px 0; display:none; }
  .fable-section { max-width:1060px; margin:0 auto 30px; }
  .fable-section-title { font-family:Georgia,serif; font-size:1.2rem; color:var(--accent2); border-bottom:2px solid var(--border); padding-bottom:4px; margin-bottom:2px; }
  .fable-section-blurb { font-size:0.82rem; color:var(--text-dim); margin-bottom:12px; }
  .fable-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(230px,1fr)); gap:12px; }
  .fable-card { background:var(--surface); border:1px solid var(--border); border-radius:10px; padding:12px 14px; cursor:pointer; transition:transform 0.12s, box-shadow 0.12s; display:flex; flex-direction:column; gap:5px; }
  .fable-card:hover { transform:translateY(-2px); box-shadow:0 6px 16px var(--shadow-hover-4); background:var(--surface2); }
  .fable-card-name { font-family:Georgia,serif; font-size:1.02rem; font-weight:700; color:var(--text); }
  .fable-card-sub { font-size:0.78rem; color:var(--text-dim); line-height:1.35; flex:1; }
  .fable-card-meta { font-size:0.72rem; color:var(--text-dim); opacity:0.8; }
  #fable-player { display:none; flex:1; flex-direction:column; overflow:hidden; align-items:center; }
  .fable-player-inner { display:flex; flex-direction:column; flex:1; overflow:hidden; width:100%; max-width:1260px; padding:10px 16px 0; }
  .fable-board-col { display:flex; flex-direction:column; align-items:center; gap:10px; }
  /* The Learn board is the responsive .epb board now (see app-theme.css). */
  #fable-board-wrap { width:100%; margin:0 auto; }

  /* ---- The lesson card (option D, docs/lesson-screen-redesign-plan.md) ----
     ONE grid, two sets of areas. The source order is the phone's: the writing
     above the board, so the words are the first thing on the screen. The laptop
     re-flows the SAME elements into rail | board | writing, which is why the
     chapter name and the chapter list each exist exactly once. */
  /* The card fills the space it is given and NEVER page-scrolls (Ed,
     2026-08-13: "There should be no scrolling available for the big screen, the
     only scrolling that should exist is in the text area"). Only .fb-text has a
     scrollbar; head, action, board and foot are all flex:none, which is what
     keeps the board and the buttons still. */
  .fb-page {
    display:grid; width:100%; min-width:0; min-height:0;
    grid-template-areas: "head" "hair" "text" "board" "foot";
    grid-template-rows: auto auto minmax(0, 1fr) auto auto;
    background:var(--surface); border:1px solid var(--border);
    border-radius:18px; overflow:hidden;
  }
  /* Stacked: the two wrappers get out of the way so every child keeps its own
     grid area. They only become real boxes in the rail layout below. */
  .fb-side, .fb-mid { display:contents; }
  .fb-hair { grid-area:hair; height:3px; background:var(--bg-fable-ch-progress); }
  .fb-hair span { display:block; height:100%; width:0; background:var(--accent); transition:width 0.3s ease; }
  .fb-head { grid-area:head; display:flex; align-items:center; gap:4px; padding:6px 6px 4px; }
  .fb-head .fable-ch-current { flex:1 1 auto; min-width:0; max-width:none; }
  .fb-text { grid-area:text; min-width:0; min-height:0; overflow-y:auto; overscroll-behavior:contain; -webkit-overflow-scrolling:touch; }
  /* The action lives inside the board slot, above the board, in BOTH layouts,
     so there is one of it and it is always the thing directly above the board. */
  /* Set by fbTextCue while there is more writing below the fold. */
  .fb-text.fb-more {
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 34px), transparent);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 34px), transparent);
  }
  .fb-act { padding:0 12px 8px; }
  .fb-act .fable-challenge-prompt,
  .fb-act .fable-challenge-result,
  .fb-act .fable-hint { margin-top:0; margin-bottom:6px; padding:9px 13px; }
  .fb-act .fable-challenge-btns { margin-bottom:0; }
  /* A recess, so the card reads as one object with the board set into it
     rather than a card sitting behind a board. */
  .fb-boardslot { grid-area:board; padding:8px; background:var(--bg-fb-recess); min-width:0; }
  .fb-boardslot #fable-board-wrap { margin:0 auto; }
  .fb-foot { grid-area:foot; padding:9px 12px; border-top:1px solid var(--border); }
  /* The primary's label grows to "Next chapter" and "Finish", so the labelled
     buttons are tightened and never break mid-label. If a narrow phone still
     cannot hold the row it wraps to a second centred line rather than squash. */
  #fable-player .fb-foot .study-nav { width:100%; justify-content:space-between; gap:8px; row-gap:8px; }
  .fb-foot .btn { white-space:nowrap; }
  .fb-foot .btn:not(.btn-icon) { padding-left:14px; padding-right:14px; }
  .fb-tools { display:flex; align-items:center; gap:8px; flex:none; }
  /* Chapters run to 46 steps, so this is a counter and not a row of dots. */
  .fb-count { font-size:0.82rem; color:var(--text-dim); flex:none; font-variant-numeric:tabular-nums; }
  .fable-ch-step { flex:none; width:26px; height:26px; border:none; border-radius:6px; background:transparent; color:var(--ink-fable-ch-step); font-size:1.5rem; line-height:1; cursor:pointer; display:flex; align-items:center; justify-content:center; transition:background 0.12s; }
  .fable-ch-step:hover:not(:disabled) { background:var(--bg-disabled-2); }
  .fable-ch-step:disabled { opacity:0.28; cursor:default; }
  .fable-ch-current { flex:none; max-width:340px; border:none; border-radius:6px; background:transparent; color:var(--ink-fable-ch-step); text-align:center; cursor:pointer; padding:2px 10px; display:flex; flex-direction:column; align-items:center; gap:1px; transition:background 0.12s; }
  .fable-ch-current:hover { background:var(--bg-hover-14); }
  .fable-ch-eyebrow { font-size:0.62rem; letter-spacing:1.2px; text-transform:uppercase; opacity:0.7; display:flex; align-items:center; justify-content:center; gap:5px; }
  .fable-ch-eyebrow .chev { font-size:0.7rem; transition:transform 0.15s; }
  .fable-ch-current.open .chev { transform:rotate(180deg); }
  .fb-head .fable-chapter-name { font-family:Georgia,serif; font-size:0.92rem; font-weight:700; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:100%; }
  .fable-ch-menu { display:none; position:fixed; z-index:60; width:300px; max-height:62vh; overflow-y:auto; padding:6px; background:var(--surface); border:1px solid var(--border); border-radius:10px; box-shadow:0 12px 30px var(--shadow-fable-ch-menu); }
  .fable-ch-item { display:flex; gap:10px; align-items:baseline; width:100%; text-align:left; border:none; background:none; cursor:pointer; padding:10px 12px; border-radius:8px; color:var(--text); }
  .fable-ch-item:hover { background:var(--surface2); }
  .fable-ch-item.current { background:var(--accent); color:var(--ink-rp-danger-btn); }
  .fable-ch-num { font-size:0.78rem; opacity:0.55; min-width:18px; font-variant-numeric:tabular-nums; }
  .fable-ch-item.current .fable-ch-num { opacity:0.9; }

  /* ---- The lesson card on a laptop ----
     Same elements, re-flowed: the chapter list stops being a dropdown and
     becomes a permanent rail, so the ‹ › steppers and the dropdown chevron
     have nothing left to do and go. fableApplyChapterMenu knows about this
     mode and stops writing inline position styles. */
  @media (min-width: 761px) {
    /* The card is shorter than the window, so the spare height is split between
       above and below it — one part above to three below, which sits it halfway
       between the middle of the window and the top (Ed, 2026-08-14: "centered
       isn't perfect for viewing"). Two flexible spacers rather than a margin,
       because the spare height depends on the window. */
    .fable-player-inner { padding-bottom:10px; }
    .fable-player-inner::before { content:''; flex:1 1 0; min-height:0; }
    .fable-player-inner::after { content:''; flex:3 1 0; min-height:0; }
    .fb-page { flex:none; }
    /* Big enough to read from a laptop's distance. The lesson's trail only — the
       other tabs' trails are untouched. */
    #fable-crumbs .book-crumbs { font-size:1rem; gap:5px 8px; }
    /* Three columns, ONE row. The writing is a column of its own and so is the
       board, which is what lets the board sit at the TOP of its column with the
       button row directly under it (Ed, 2026-08-14: the board at the foot of a
       tall column looked stranded). The card is as tall as the board column and
       stops there — it does not run to the bottom of the window. */
    .fb-page {
      grid-template-columns: 236px minmax(0, 1fr) minmax(300px, 380px);
      grid-template-areas: "rail mid side";
      grid-template-rows: minmax(0, auto);
      height: auto; max-height: 100%; min-height: 0;
    }
    .fb-side { grid-area:side; display:flex; flex-direction:column; min-width:0; min-height:0; }
    .fb-mid {
      grid-area:mid; display:flex; flex-direction:column; min-width:0; min-height:0;
      background:var(--bg-fb-recess);
    }
    /* A COLUMN, or the action block lands beside the board as a second flex
       child and both shrink (measured: the board dropped 570 -> 367 on every
       challenge step). */
    .fb-boardslot {
      flex:none; background:transparent; padding-top:0;
      display:flex; flex-direction:column; align-items:center; justify-content:flex-start;
    }
    /* The board is the FIRST thing in the card, flush with its top edge, and the
       action block moved under it (Ed, 2026-08-14: "can you move it to the top").
       On a phone the action stays above the board, where the writing is. */
    .fb-boardslot #fable-board-wrap { width:100%; order:1; }
    /* Its room is held open on every step, whether or not this one has an action,
       so the buttons and the foot of the card never move. Same reserve fbFitBoard
       charges the board for (FB_ACT_ALLOW). */
    .fb-boardslot .fb-act {
      order:2; display:block !important; box-sizing:border-box;
      width:100%; min-height:96px; padding-top:8px; padding-bottom:0;
    }
    .fb-foot { flex:none; }
    /* flex-basis 0, so the writing does not push the card taller than the board
       column: it takes the height the board leaves and scrolls inside it. */
    .fb-text { flex:1 1 0; min-height:0; overflow-y:auto; padding-bottom:14px; }
    .fb-hair { margin:12px 22px 0; border-radius:3px; overflow:hidden; }
    .fb-head { padding:20px 22px 0; }
    .fb-head .fable-ch-step, .fb-head .fable-ch-eyebrow .chev { display:none; }
    .fb-head .fable-ch-current { text-align:left; align-items:flex-start; padding:0; cursor:default; }
    .fb-head .fable-ch-current:hover { background:transparent; }
    .fb-head .fable-ch-eyebrow { justify-content:flex-start; }
    .fb-head .fable-chapter-name { font-size:1.24rem; white-space:normal; }
    .fable-ch-menu {
      grid-area: rail; position:static; display:block; width:auto; max-height:none;
      border:0; border-right:1px solid var(--border); border-radius:0;
      box-shadow:none; background:transparent; padding:14px 8px;
      overflow-y:auto; min-height:0;
    }
  }
  .fable-ch-title { font-family:Georgia,serif; font-size:0.92rem; line-height:1.3; }
  .fable-story-body { padding:16px; }
  .fable-step-text { font-family:Georgia,'Times New Roman',serif; font-size:0.98rem; line-height:1.6; color:var(--text); }
  .fable-analysis-note { font-style:italic; color:var(--text-dim); font-size:0.92rem; }
  .fable-challenge-prompt { margin-top:14px; font-weight:700; color:var(--accent); font-size:0.92rem; }
  .fable-challenge-result.ok { margin-top:14px; font-weight:700; color:var(--correct); }
  .fable-challenge-result.shown { margin-top:14px; font-style:italic; color:var(--text-dim); font-size:0.88rem; }
  .fable-hint { margin-top:10px; font-size:0.88rem; color:var(--accent2); background:var(--bg-fable-hint); border-radius:6px; padding:8px 10px; }
  .fable-challenge-btns { margin-top:12px; display:flex; gap:8px; }
  .fable-small-btn { font-size:0.78rem; padding:5px 12px; }
  .fable-arrow-list { margin-top:14px; display:flex; flex-direction:column; gap:6px; border-top:1px solid var(--border); padding-top:12px; }
  .fable-arrow-item { display:flex; align-items:center; gap:8px; font-size:0.82rem; color:var(--text); }
  .fable-arrow-dot { width:10px; height:10px; border-radius:50%; flex:none; opacity:0.7; }
  .fable-mark { box-shadow: inset 0 0 0 4px var(--shadow-fable-mark); }
  .fable-hint-sq { box-shadow: inset 0 0 0 4px var(--shadow-fable-hint-sq); animation: fableHintPulse 1.1s ease-in-out infinite; }
  @keyframes fableHintPulse { 0%,100%{box-shadow: inset 0 0 0 4px var(--shadow-fable-hint-sq);} 50%{box-shadow: inset 0 0 0 4px var(--shadow-50);} }
  .fable-movelist { max-width: 100%; margin:10px auto 0; display:flex; flex-wrap:wrap; gap:1px 5px; align-items:baseline; font-family:Menlo,Consolas,monospace; font-size:0.8rem; max-height:60px; overflow-y:auto; }
  .fable-mv-num { color:var(--text-dim); margin-left:5px; }
  .fable-mv { color:var(--text); padding:0 3px; border-radius:3px; }
  .fable-mv.current { background:var(--accent); color:var(--ink-rp-danger-btn); }
  #fable-analysis-btn.active { background:var(--accent); color:var(--ink-rp-danger-btn); border-color:var(--accent); }
  @keyframes fableShake { 0%,100%{transform:translateX(0);} 25%{transform:translateX(-6px);} 75%{transform:translateX(6px);} }
  .fable-shake { animation: fableShake 0.35s; }

  /* ===== MASTER TAB ===== */
  #master-view { display:none; flex:1; min-height:0; overflow:hidden; flex-direction:column; }
  #master-setup { flex:1; overflow-y:auto; padding:28px 36px 60px; }
  .master-hero { text-align:center; max-width:620px; margin:0 auto 26px; }
  .master-hero-icon { font-size:2.4rem; }
  .master-hero h2 { font-family:Georgia,'Times New Roman',serif; font-size:2rem; letter-spacing:2px; color:var(--accent2); margin:4px 0 8px; }
  .master-hero p { color:var(--text-dim); font-size:0.95rem; line-height:1.5; }
  .master-pick-label { max-width:760px; margin:0 auto 10px; font-family:Georgia,serif; font-size:1.05rem; color:var(--accent2); border-bottom:2px solid var(--border); padding-bottom:4px; }
  .master-start-btn { font-size:1rem; padding:12px 28px; }
  .master-start-note { font-size:0.82rem; color:var(--text-dim); }

  /* ---- Train session builder ---- */
  .ms-hint { max-width:760px; margin:8px auto 0; font-size:0.8rem; color:var(--text-dim); line-height:1.45; }
  .ms-tag { display:inline-block; font-size:0.72rem; font-weight:700; padding:1px 7px; border-radius:20px; border:1px solid var(--border); }
  .ms-tag-w { background:var(--light-sq); color:var(--ink-ms-tag-w); border-color:var(--line-ms-tag-w); }
  .ms-tag-b { background:var(--bg-ms-tag-b); color:var(--light-sq); border-color:var(--line-ms-tag-b); }
  /* Jump-in opening grid (home) */
  .ms-oc-grid { max-width:820px; margin:0 auto; display:grid; grid-template-columns:repeat(auto-fill,minmax(148px,1fr)); gap:14px; }
  .ms-oc-card { position:relative; background:var(--surface); border:2px solid var(--border); border-radius:14px; padding:10px; display:flex; flex-direction:column; gap:8px; cursor:pointer; transition:transform 0.12s, border-color 0.12s, box-shadow 0.12s; }
  .ms-oc-card.fav { border-color:var(--line-ms-oc-cardfav); }
  .ms-oc-card:hover { transform:translateY(-2px); box-shadow:0 6px 18px var(--shadow-hover-2); }
  .ms-oc-star { position:absolute; top:7px; right:7px; z-index:2; background:var(--bg-ms-oc-star); border:none; border-radius:50%; width:30px; height:30px; font-size:1.05rem; color:var(--ink-ms-oc-star); cursor:pointer; line-height:1; box-shadow:0 1px 3px var(--shadow-ms-oc-star); }
  .ms-oc-star.on { color:var(--ink-ms-oc-staron); }
  .ms-oc-board { border-radius:8px; overflow:hidden; }
  .ms-oc-name { font-family:Georgia,serif; font-weight:700; font-size:0.92rem; line-height:1.2; }
  .ms-oc-chips { display:flex; gap:6px; flex-wrap:wrap; }
  .ms-oc-chip { flex:1; min-width:0; display:flex; align-items:center; justify-content:center; gap:5px; font-size:0.8rem; font-weight:700; padding:7px 6px; border-radius:8px; border:1.5px solid var(--border); background:var(--bg); cursor:pointer; color:var(--text); font-family:Georgia,serif; }
  .ms-oc-chip span { font-size:0.68rem; font-weight:400; color:var(--text-dim); }
  .ms-oc-chip:hover { border-color:var(--accent); }
  .ms-oc-every { align-items:center; justify-content:center; text-align:center; border-style:dashed; cursor:pointer; }
  .ms-oc-every-icon { font-size:2.6rem; color:var(--accent2); display:flex; align-items:center; justify-content:center; aspect-ratio:1; }
  .ms-oc-every-sub { font-size:0.72rem; color:var(--text-dim); }
  /* Customize drill-in */
  .ms-cust-titles h2 { font-family:Georgia,'Times New Roman',serif; font-size:1.45rem; letter-spacing:1px; color:var(--accent2); margin:0; }
  .ms-crumb { font-size:0.7rem; color:var(--text-dim); text-transform:uppercase; letter-spacing:1px; margin-bottom:1px; }
  .ms-fchips { max-width:760px; margin:14px auto 6px; display:flex; gap:8px; }
  .ms-fchip { flex:0 0 auto; padding:7px 16px; border-radius:20px; border:1.5px solid var(--border); background:var(--surface); cursor:pointer; font-size:0.85rem; font-weight:700; color:var(--text); font-family:Georgia,serif; }
  .ms-fchip.on { background:var(--accent); border-color:var(--accent); color:var(--ink-rp-danger-btn); }
  .ms-cards { max-width:760px; margin:6px auto 0; display:flex; flex-direction:column; gap:10px; }
  .ms-card { position:relative; display:flex; align-items:center; gap:10px; background:var(--surface); border:2px solid var(--border); border-radius:14px; padding:8px 10px; transition:border-color 0.12s; }
  .ms-card:hover { border-color:var(--accent); }
  .ms-card-check { flex:0 0 auto; width:22px; height:22px; border:2px solid var(--border); border-radius:6px; cursor:pointer; position:relative; background:var(--bg); }
  .ms-card-check.all, .ms-card-check.some { background:var(--accent); border-color:var(--accent); }
  .ms-card-check.all::after { content:'✓'; position:absolute; top:-1px; left:3px; color:var(--ink-rp-danger-btn); font-size:0.95rem; font-weight:900; }
  .ms-card-check.some::after { content:''; position:absolute; top:8px; left:4px; width:10px; height:2px; background:var(--bg-after-5); }
  .ms-card-main { flex:1; display:flex; align-items:center; gap:12px; cursor:pointer; min-width:0; }
  .ms-card-board { flex:0 0 auto; width:62px; border-radius:8px; overflow:hidden; }
  .ms-card-body { flex:1; min-width:0; }
  .ms-card-name { font-family:Georgia,serif; font-weight:700; font-size:1rem; line-height:1.25; }
  .ms-card-chips { display:flex; gap:6px; flex-wrap:wrap; margin-top:6px; }
  .ms-card-chips .ms-oc-chip { flex:0 0 auto; }
  .ms-oc-chip.on { background:var(--accent); border-color:var(--accent); color:var(--ink-rp-danger-btn); }
  .ms-oc-chip.on span { color:var(--ink-span); }
  .ms-card.sel { border-color:var(--accent); background:var(--surface2); }
  /* Select-all + colour-scope bar */
  .ms-allbar { max-width:760px; margin:4px auto 12px; display:flex; align-items:center; justify-content:space-between; gap:10px 14px; flex-wrap:wrap; }
  .ms-selall { display:flex; align-items:center; gap:8px; background:none; border:none; cursor:pointer; font-family:Georgia,serif; font-weight:700; font-size:0.9rem; color:var(--text); padding:4px 0; }
  .ms-scope { display:flex; border:1.5px solid var(--border); border-radius:20px; overflow:hidden; }
  .ms-scopebtn { background:var(--surface); border:none; border-left:1.5px solid var(--border); cursor:pointer; font-family:Georgia,serif; font-weight:700; font-size:0.8rem; color:var(--text); padding:7px 13px; }
  .ms-scopebtn:first-child { border-left:none; }
  .ms-scopebtn.on { background:var(--accent); color:var(--ink-rp-danger-btn); }
  .ms-mode-row { max-width:760px; margin:0 auto; display:flex; gap:10px; }
  .ms-mode { flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:7px; min-height:92px; padding:10px 6px; border-radius:12px; border:2px solid var(--border); background:var(--surface); cursor:pointer; color:var(--text); font-family:Georgia,serif; text-align:center; }
  .ms-mode-ic { font-size:1.5rem; line-height:1; }
  .ms-mode-lb { font-size:0.8rem; font-weight:700; line-height:1.2; }
  .ms-mode.selected { border-color:var(--accent); background:var(--surface2); box-shadow:0 0 0 1px var(--accent); }
  .ms-cust-btn { display:block; width:100%; max-width:760px; margin:6px auto 24px; padding:16px 20px; border-radius:12px; border:2px solid var(--accent); background:var(--surface2); cursor:pointer; color:var(--text); font-family:Georgia,serif; font-weight:700; font-size:1.05rem; text-align:left; }
  .ms-cust-btn span { display:block; margin-top:3px; font-size:0.78rem; font-weight:400; color:var(--text-dim); }
  .ms-cust-btn:hover { border-color:var(--accent2); }
  .ms-cust-head { max-width:760px; margin:0 auto 4px; display:flex; align-items:center; gap:12px; }
  .ms-cust-head h2 { font-family:Georgia,'Times New Roman',serif; font-size:1.5rem; letter-spacing:1px; color:var(--accent2); margin:0; }
  .ms-back { flex:0 0 auto; width:38px; height:38px; border-radius:50%; border:2px solid var(--border); background:var(--surface); cursor:pointer; color:var(--text); font-size:1.1rem; line-height:1; }
  .ms-back:hover { border-color:var(--accent); }
  .ms-cust-intro { max-width:760px; margin:0 auto 18px; color:var(--text-dim); font-size:0.9rem; line-height:1.5; }
  .ms-startbar { position:sticky; bottom:0; max-width:760px; margin:22px auto 0; display:flex; align-items:center; gap:12px; padding:12px 16px; background:var(--surface2); border:1px solid var(--accent); border-radius:12px; box-shadow:0 4px 16px var(--shadow-ms-startbar); }
  .ms-startbar-info { flex:1; font-family:Georgia,serif; font-weight:700; font-size:0.95rem; }
  .ms-startbar-clear { background:none; border:none; color:var(--text-dim); cursor:pointer; font-size:0.85rem; text-decoration:underline; }
  .ms-startbar-go { font-size:0.95rem; padding:10px 22px; }
  @media (max-width: 760px) {
    .ms-mode-row { gap:6px; }
    .ms-mode { min-height:84px; padding:8px 4px; }
  }
  #master-arena { display:none; flex:1; min-height:0; flex-direction:column; overflow:hidden; align-items:center; }
  .master-arena-inner { display:flex; flex-direction:column; flex:1; min-height:0; overflow:hidden; width:100%; max-width:1100px; padding:10px 16px 0; }
  /* Still the Repertoire builder's row (rep2-builder: board + walk panel).
     The Train tab no longer uses it — see .master-stage below. */
  .master-board-row { display:flex; gap:22px; justify-content:center; align-items:flex-start; flex:1; overflow:hidden; padding-top:8px; }

  /* The Train board sizes itself: no side panel to leave room for any more, so
     drop the 510px horizontal reserve the global --bk-sq keeps, and reserve
     vertically instead for the scorecard/nav/result stacked underneath. Scoped
     to this tab — the global clamp still governs Book/Learn/Repertoire. */
  #master-arena {
    --bk-sq: clamp(56px, min(
      calc((100vw - 120px) / 8),
      calc((100vh - 415px) / 8)
    ), 88px);
  }
  /* min-height:0 or the flex child refuses to shrink below its content and the
     result panel slides behind the fixed nav instead of scrolling. */
  .master-stage { display:flex; flex-direction:column; align-items:center; gap:9px;
    flex:1; min-height:0; overflow-y:auto; overflow-x:hidden; width:100%; padding:8px 0 14px; }
  /* .ms-rail wraps everything that talks about the board. It is display:contents
     here, so its children stay direct participants of this column and the phone
     and stacked-laptop layouts are byte-identical to before it existed. It only
     becomes a real box in the wide-laptop rail below.
     Both selectors are needed: the first covers the board's siblings, the second
     the rail's children, because width on a display:contents box does nothing. */
  .ms-rail { display: contents; }
  .master-stage > *:not(#master-board-wrap),
  .master-stage .ms-rail > * {
    width:100%; max-width:var(--ms-bw);
  }

  /* How far through the line you are. Everything else that lived in this strip
     (the clean/shaky chips, the verdict pill, the line counter) is gone. */
  .ms-score-track { height:3px; border-radius:999px; background:var(--bg-ms-score-track);
    overflow:hidden; flex:none; width:100%; max-width:var(--ms-bw); }
  .ms-score-fill { height:100%; width:0%; background:var(--accent); transition:width 0.3s; }
  /* Recall buttons (Restart line / Skip) sit centred under the board, like the
     line-end row — not left-aligned. */
  #master-nav { justify-content:center; }

  #master-msg:empty { display:none; }
  #master-msg:not(:empty) { background:var(--surface); border:1px solid var(--border);
    border-radius:12px; padding:13px 15px; flex:none; }
  /* Wide under the board, so the result buttons sit in a row, not a stack. */
  .master-stage .master-result-btns { flex-direction:row; flex-wrap:wrap; justify-content:center; }
  .master-stage .rep-move-history { max-height:112px; overflow-y:auto; border-radius:12px;
    border:1px solid var(--border); flex:none; }
  .master-stage .rep-move-history:empty { display:none; }
  #master-msg:empty { padding:0; min-height:0; }
  .master-msg-text { font-family:Georgia,serif; font-size:0.95rem; line-height:1.5; }
  .master-msg-text.good { color:var(--correct); }
  .master-msg-text.bad { color:var(--wrong); }
  .master-msg-text.trap { color:var(--ink-rp-walk-rewind); }
  .master-strikes { letter-spacing:2px; color:var(--wrong); }

  /* Feedback strip directly below the board — always visible on phones where
     the side panel would scroll off-screen, and never covers the pieces */
  .master-feedback {
    display:none; box-sizing:border-box;
    width:calc(var(--bk-sq) * 8 + 20px); max-width:100%;
    margin:10px auto 0; padding:9px 14px; border-radius:8px;
    font-family:Georgia,serif; font-size:0.92rem; line-height:1.35; text-align:center;
    color:var(--ink-rp-danger-btn); background:var(--bg-master-feedback);
    box-shadow:0 2px 10px var(--shadow-master-feedback);
  }
  .master-feedback.show { display:block; animation:msFeedIn 0.22s ease-out; }
  .master-feedback.good { background:var(--bg-master-feedbackgood); }
  .master-feedback.bad  { background:var(--bg-master-feedbackbad); }
  .master-feedback.trap { background:var(--bg-master-feedbacktrap); }
  .master-feedback b { color:var(--ink-rp-danger-btn); }
  .master-feedback .master-strikes { color:var(--ink-rp-danger-btn); }
  @keyframes msFeedIn { from{opacity:0;transform:translateY(6px);} to{opacity:1;transform:translateY(0);} }

  @keyframes sqFlashBad  { 0%{box-shadow:inset 0 0 0 9999px var(--shadow-0);} 100%{box-shadow:inset 0 0 0 9999px var(--shadow-100);} }
  @keyframes sqFlashGood { 0%{box-shadow:inset 0 0 0 9999px var(--shadow-0-2);} 100%{box-shadow:inset 0 0 0 9999px var(--shadow-100-2);} }
  .square.sq-flash-bad  { animation:sqFlashBad 0.6s ease-out; }
  .square.sq-flash-good { animation:sqFlashGood 0.6s ease-out; }
  @media (prefers-reduced-motion: reduce) {
    .master-feedback.show { animation:none; }
    .square.sq-flash-bad, .square.sq-flash-good { animation:none; }
  }
  .master-result-title { font-family:Georgia,serif; font-weight:700; font-size:1.1rem; margin-bottom:6px; }
  .master-result.ok .master-result-title { color:var(--correct); }
  .master-result.shaky .master-result-title { color:var(--ink-rp-walk-rewind); }
  .master-result-note { font-family:Georgia,serif; font-size:0.88rem; line-height:1.5; margin-bottom:12px; }
  .master-result-btns { display:flex; flex-direction:column; gap:8px; }

  /* Line-end result: the buttons stand on their own below the board (no card
     box). .ms-result-acts is the punishment card's row, left as it was. */
  .ms-result { text-align:center; }
  .ms-result-acts { display:flex; align-items:center; justify-content:center; gap:8px; flex-wrap:wrap; }
  #master-msg.ms-bare:not(:empty) { background:none; border:none; box-shadow:none; padding:0; }

  .master-choice { margin-bottom:12px; padding:10px 12px; background:var(--bg-master-choice); border-radius:8px; }
  .master-choice-label { font-family:Georgia,serif; font-size:0.85rem; margin-bottom:8px; }
  .master-choice-btns { display:flex; gap:8px; flex-wrap:wrap; }
  /* Move list (shared: Train, Book, the Repertoire walk). Collapsed by default —
     most of the time you don't need it. A small centred "Move list" pill expands
     to a compact, readable inline move flow, not a board-wide monospace block. */
  .ep-moves { width:auto; max-width:100%; align-self:center; flex:none;
    text-align:center; margin:0 auto; }
  /* id beats the .master-stage full-width rule, so Train's shrinks to content
     and centres instead of stretching board-wide.
     The extra top margin is Ed, 2026-09-17: the column's own 9px gap put this
     pill right under the line-end buttons and it read as part of that row.
     13 takes it to 22, the same separation .ms-foot already uses inside the
     result panel, so the foot of this screen is spaced like its own card. */
  #master-moves { width:auto; max-width:100%; align-self:center; flex:none; text-align:center;
    margin-top:13px; }
  .ep-moves-sum { list-style:none; cursor:pointer; user-select:none;
    display:inline-flex; align-items:center; gap:7px;
    font-family:Georgia,serif; font-size:0.72rem; font-weight:700;
    text-transform:uppercase; letter-spacing:0.7px; color:var(--text-dim);
    padding:6px 15px; border-radius:999px; border:1px solid var(--border);
    background:var(--surface); transition:color .12s ease, border-color .12s ease; }
  .ep-moves-sum::-webkit-details-marker { display:none; }
  .ep-moves-sum:hover { color:var(--text); border-color:var(--text-dim); }
  .ep-moves-chev { font-size:0.62rem; transition:transform .15s ease; }
  .ep-moves[open] .ep-moves-chev { transform:rotate(180deg); }
  .ep-moves-body { display:inline-block; margin-top:10px; padding:11px 16px;
    max-width:440px; max-height:150px; overflow-y:auto; text-align:center;
    border:1px solid var(--border); border-radius:12px; background:var(--surface);
    font-size:0.95rem; line-height:1.9; }
  .ep-moves-body:empty { display:none; }
  .ep-mv-num { color:var(--text-dim); font-weight:700; margin:0 3px 0 9px;
    font-variant-numeric:tabular-nums; }
  .ep-mv-num:first-child { margin-left:0; }
  .ep-mv { font-family:'Roboto Mono','Courier New',monospace; margin:0 2px; white-space:nowrap;
    border-radius:5px; padding:1px 5px; }
  .ep-mv-click { cursor:pointer; }
  .ep-mv-click:hover { background:var(--ln-green-tint); }
  .ep-mv-active { background:var(--bg-ep-mv-active); font-weight:700; }
  /* Moves further down the line than the board has reached: still clickable
     (that's how you skip ahead), just quieter. */
  .ep-mv-future { color:var(--text-dim); opacity:0.6; }

  /* The Train board is the responsive .epb board now (see app-theme.css). Cap it
     to a shared width the whole stacked column tracks, height-aware so board +
     scorecard + nav + result + history all fit the laptop without scrolling. The
     horizontal eval bar rides on top; its height is always reserved so revealing
     it at a line's end never nudges the board. */
  /* 32 -> 16 (Ed, 2026-09-15: "make the board a little bigger in train mode to
     match the repertoire tab"). It was reserving twice the side room the walk
     does, and for nothing: measured at 430x932, BOTH boards sit inside the same
     .master-arena-inner at 8px a side, so the slot is 414px wide on each — the
     walk filled it and the drill capped itself to 398. Matching the reserve
     makes the two boards the same size with no container change and no overflow
     risk, because the 414 was already there. */
  #master-arena { --ms-bw: clamp(280px, min(calc(100vw - 16px), calc(100vh - 420px)), 560px); }
  /* Same raised ceiling as .epb, desktop only. The rule above still governs on
     every phone, so this can only ever apply to a wide, tall screen — which is
     also why the reserve differs from the phone's.
     420 -> 320: this screen is always in focus mode on a laptop (the Train arena
     is a focus door, and Test yourself became one on 2026-08-28), so the header
     and the tab bar are not on screen and the reserve was still paying for them.
     Measured at 1789x1180 with the taller of the two line-end moods on screen:
     158px of the stage went unused under the last button. */
  @media (min-width: 760px) and (min-height: 720px) {
    #master-arena { --ms-bw: clamp(280px, min(calc(100vw - 32px), calc(100vh - 320px)), 1040px); }
  }
  #master-board-wrap { width: 100%; }
  #master-board-wrap .epb { max-width: var(--ms-bw); }

  /* ---- Wide laptop: the drill's panel moves to the right of the board ----
     Ed, 2026-08-28: "board isn't fixed, it's still super small". It was not the
     ceiling this time. Everything on this screen stacks UNDER the board — the
     feedback strip, the buttons, the result tiles, the move list — so the board
     is governed by height on any window that is wider than it is tall, and half
     the width sits empty beside it. Reproduced on his exact window, 1389x761:
     board 441px with 630px of unused page either side.

     Same two-column shape as the walk, the lesson and Explore: board left, the
     panel that talks about it right. The reserve then only has to cover the trail
     card and the padding (140), not a column of buttons (320), so the board is
     governed by width-minus-rail instead and grows with the window.

     No markup change: .master-stage is already a flex column of exactly these
     children, so it becomes a grid, the board spans every row of column 1, and
     everything else falls into column 2 in order.

     Its own breakpoint, deliberately wider than the 760px one above: 621 + 22 +
     400 needs 1043px before the rail is worth having. Below it the stacked
     layout above is unchanged. */
  @media (min-width: 1100px) and (min-height: 720px) {
    /* The container has to be widened WITH the board, or the grid overflows it:
       .master-arena-inner is 1100px above, and at 1789x1180 a 1040px board plus
       the rail wanted 1462px and ran off both sides of the screen. */
    #master-arena .master-arena-inner { max-width: min(1500px, 94vw); }
    #master-arena {
      --ms-bw: clamp(280px, min(calc(94vw - 470px), calc(100vh - 140px)), 1000px);
    }
    #master-arena .master-stage {
      display: grid;
      grid-template-columns: var(--ms-bw) 400px;
      column-gap: 22px; row-gap: 9px;
      align-content: start; justify-content: center;
      width: 100%; max-width: none;
    }
    #master-arena #master-board-wrap { grid-column: 1; grid-row: 1; }
    /* The rail becomes a real box here, and takes the app's card (Ed, 2026-08-28:
       "can we just make this a card with our typical styling instead to keep
       things consistent?") — the same surface, hairline, 18px corners and soft
       shadow the walk's panel and every trail card carry. It hugs its content
       rather than running the height of the board, so it reads as a panel about
       the position and not as an empty column. */
    #master-arena .ms-rail {
      display: flex; flex-direction: column; gap: 9px;
      grid-column: 2; grid-row: 1; align-self: start;
      padding: 16px;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 18px;
      box-shadow: 0 1px 3px var(--shadow-hover);
    }
    #master-arena .ms-rail > * { max-width: none; width: 100%; }
    /* An empty child would still draw a gap in a flex column, and three of these
       are empty most of the time (the feedback strip during a result, the nav row
       at a line end, the result panel during recall). */
    #master-arena .ms-rail > *:empty { display: none; }
    /* The trail sits over both columns now, not just the board. */
    #master-arena #master-crumbs { max-width: calc(var(--ms-bw) + 22px + 400px); }
  }
  /* Eval bar: Analyze only. It used to reserve its space at all times so that
     revealing it at a line's end caused no shake, but it's no longer revealed
     there, so the strip was costing the board height for nothing. Out of flow
     entirely now — the board sits that much higher, and entering Analyze is a
     deliberate mode switch where a shift reads as the mode changing. */
  #master-board-wrap #ms-evalwrap { display: none; }
  #master-board-wrap #ms-evalwrap.ms-eval-shown { display: block; }

  /* ===== MOBILE ===== */
  @media (max-width: 760px) {
    :root {
      --bk-sq: calc((100vw - 108px) / 8);
    }
    /* The scoped Train override would otherwise beat :root here. */
    #master-arena { --bk-sq: calc((100vw - 108px) / 8); }
    .master-stage { overflow:visible; flex:none; height:auto;
      padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 20px); }

    /* THE EVAL BAR KEEPS ITS SLOT ON A PHONE, SO ANALYZE DOES NOT SHOVE THE
       BOARD DOWN (Ed, 2026-09-15: "move the train board down a bit so if I click
       analyze at the end of training the line it doesn't move"). The rule above
       the mobile block takes the bar out of flow entirely, so entering Analyze
       inserts it above the squares — measured at 430x932, the board's box goes
       398 -> 423 and every square drops 25px.
       That out-of-flow rule was a deliberate call ("the strip was costing the
       board height for nothing"), and it still holds on a laptop, where the
       board IS governed by height and a reserved strip would shrink it. On a
       phone it is not: --ms-bw resolves to 414 by WIDTH there (100vh - 420 is
       512, so height never binds), so holding the slot costs the board nothing
       and only moves it down — which is the other half of what he asked for.
       visibility, not display, so the box is held either way. */
    #master-board-wrap #ms-evalwrap { display: block; visibility: hidden; }
    #master-board-wrap #ms-evalwrap.ms-eval-shown { display: block; visibility: visible; }

    /* AND THE STAGE STARTS LOWER (Ed, same message: "there's not so much empty
       space at the bottom"). The drill's column is shorter than the walk's — at
       430x932 it ended 312px above the bottom of the screen where the walk ends
       189px above it — so it sat high with a band of dead paper under it.
       48px is well inside the slack in every state measured, the tightest being
       237px with the move list open and Analyze on, so nothing is pushed off the
       bottom; past that the page simply scrolls, which is the phone's own model.
       Train only: .master-stage is this tab's column (the walk keeps
       .master-board-row), and Test yourself borrows this very screen, so it
       moves with it. */
    #master-arena .master-stage { padding-top: 56px; }

    /* Mobile scrolls the WHOLE PAGE as one document (the nav stays fixed on
       top), instead of trapping each tab's content in a nested scroll box.
       A nested momentum scroller under a fixed bar both floats the bar on iOS
       and squeezes tall content (like the decision cards) into a cramped
       little window. One page scroll fixes both. So on mobile we unwind the
       fixed-viewport flex layout: let every content container grow to its
       content and flow into the page. */
    /* `clip`, not `hidden`. Both stop the page sliding sideways, but `hidden`
       ALSO makes the element a scroll container, and a sticky child then sticks
       inside a box that never scrolls — so it just scrolls away. That silently
       disabled every sticky bar on a phone, the focus-mode trail included; it
       only went unnoticed because those screens barely scroll. `hidden` stays
       first as the fallback for anything too old for `clip` (Safari 16+). */
    html, body { height: auto; min-height: 100dvh; overflow-x: hidden; overflow-x: clip; }
    /* .main is a ROW flex on desktop; as plain block on mobile its child views
       are full-width block boxes that grow to their content — no nested scroll,
       and no content-width sizing that spills off the right edge. */
    .main { display: block; overflow: visible; min-height: auto; }
    /* NOTE: #fable-player and .fable-player-inner are deliberately NOT in this
       list. The lesson is the one screen that keeps the desktop's fixed-height
       model on a phone, because the board and buttons have to stay put and only
       the writing may scroll. Its rules are right below. */
    #book-view, #repertoire-view, #master-view,
    #rep2-builder, #rep2-arena, #master-arena, #book-line-view,
    #rep2-home, #rep2-chapters, #rep2-reps, #rep2-color, #master-setup,
    .master-arena-inner, .book-body, .book-content {
      overflow: visible !important;
      flex: none !important;
      height: auto !important;
      min-height: 0 !important;
      width: 100% !important;
      max-width: 100% !important;
    }

    header { padding: calc(8px + env(safe-area-inset-top, 0px)) 14px 8px; }
    header h1 { font-size: 1rem; }

    /* Board + side-panel rows stack and flow (no inner scroll of their own).
       .master-board-row here is the Repertoire builder's; the Train tab stacks
       by default now and handles itself via .master-stage. */
    .book-board-center,
    .master-board-row {
      flex-direction: column;
      align-items: center;
      justify-content: flex-start;
      width: 100%;
      overflow: visible;
      flex: none;
      height: auto;
      /* breathing room above the fixed nav at the very end of the page */
      padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 20px);
    }

    /* .fable-board-col's flex:0 1 500px sets its WIDTH on the desktop row
       layout above. Once the row flips to column here, that same flex-basis
       reads as a HEIGHT instead, stretching the board+nav column to 500px
       tall and leaving a gap before the text box below it. Drop the basis.
       (Matches the specificity of the two rules being overridden.) */
    .master-board-row .fable-board-col { flex: none; }

    .book-line-inner { width: 100%; max-width: 100%; padding: 16px 8px; }
    .book-board-col { width: 100%; align-items: center; }

    .rp-builder-panel {
      width: 100%;
      max-width: calc(var(--bk-sq) * 8 + 50px);
      max-height: none;
      box-sizing: border-box;
      /* 16px a side until 2026-09-15 (Ed: "widen the big card that they're all in so
         it extends closer to the screen"). With the option cards two to a row, every
         pixel of the panel's own padding comes off both of them. */
      padding: 14px 10px;
    }

    .eval-bar-wrap { width: 34px; }
    /* Caption wraps within the narrow column and may spill a hair without
       widening it, so the board keeps its position. */
    .eval-score { font-size: 0.56rem; white-space: normal; overflow: visible; text-overflow: clip; width: 34px; text-align: center; line-height: 1.15; }

    /* The lesson owns the screen: fixed height, nothing outside .fb-text scrolls.
       svh, not dvh: dvh grows the moment Safari's toolbars slide away, and the
       board would grow with it. The lesson always runs in focus mode (no header,
       no tab bar); the second rule is the safety net for the case where
       epFocusCheck drops out of it. */
    #fable-player {
      height: 100svh; max-height: 100svh; min-height: 0;
      overflow: hidden; flex: none;
    }
    body:not(.ep-focus) #fable-player {
      height: calc(100svh - 52px - 54px - env(safe-area-inset-bottom, 0px));
      max-height: none;
    }
    .fable-player-inner {
      flex: 1; min-height: 0; height: auto; overflow: hidden;
      width: 100%; max-width: 100%;
    }
    .fb-page { flex: 1; min-height: 0; }

    .master-arena-inner { padding: 10px 8px 0; }
    /* Tighter than the arena: every pixel here is board width. */
    .fable-player-inner { padding: 10px 6px 0; }
    #rep2-history, .ep-moves-body { max-height: 180px; }


    .book-body { flex-direction: column; }
    .book-content { padding: 16px 14px; }

    #rep2-home, #rep2-chapters, #rep2-reps, #rep2-color, #master-setup {
      padding: 16px 14px 60px;
    }
    .rp-rep-card { flex-direction: column; align-items: stretch; gap: 10px; }
    .rp-rep-actions { width: 100%; }
    .rp-rep-actions .rp-small-btn { flex: 1; }

    /* Nav buttons wrap instead of overflowing */
    .study-nav { flex-wrap: wrap; justify-content: center; }

    [style*="min-width:80px"] { min-width: 0 !important; }
  }

  /* ============================================================
     REPERTOIRE CHAPTERS — table-of-contents redesign (.rpc)
     Numbered medallion with progress ring, serif titles, move
     chips, mini-board thumbnail, dotted-leader line rows.
     ============================================================ */
  .rp-chapter-card.rpc { position: relative; overflow: hidden; padding: 16px 20px 14px 26px; }
  .rp-chapter-card.rpc::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 6px;
    background: var(--border); opacity: 0.5; }
  .rp-chapter-card.rpc.learned { border-color: var(--accent); background-image: linear-gradient(var(--bg-rp-chapter-cardrpclearned), var(--bg-rp-chapter-cardrpclearned)); }
  .rp-chapter-card.rpc.learned::before { background: var(--accent); opacity: 1; }
  .rp-chapter-card.rpc.attn::before { background: var(--bg-rp-ch-badgewarn); opacity: 1; }
  .rp-chapter-card.rpc.locked { border-style: dashed; box-shadow: none; }
  .rp-chapter-card.rpc.locked::before { opacity: 0.3; }

  .rpc-head { display: flex; gap: 14px; align-items: flex-start; }
  .rpc-medal { position: relative; flex: 0 0 46px; width: 46px; height: 46px; margin-top: 3px; }
  .rpc-medal svg { width: 100%; height: 100%; transform: rotate(-90deg); display: block; }
  .rpc-ring-track { fill: none; stroke: var(--border); stroke-opacity: 0.4; stroke-width: 3.5; }
  .rpc-ring-fill { fill: none; stroke: var(--accent); stroke-width: 3.5; stroke-linecap: round; transition: stroke-dasharray 0.5s ease; }
  .rpc-medal-num { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    font-family: var(--ln-serif, Georgia, serif); font-weight: 700; font-size: 1.12rem; color: var(--accent2); }
  .rpc-medal.done { background: var(--ln-green-tint); border-radius: 50%; }
  .rpc-medal.done .rpc-medal-num { color: var(--accent); font-size: 1.25rem; }
  .rpc.locked .rpc-medal-num { opacity: 0.55; }

  .rpc-titles { flex: 1; min-width: 0; }
  .rpc-eyebrow { display: flex; align-items: center; justify-content: space-between; gap: 6px 10px; flex-wrap: wrap;
    font-size: 0.64rem; font-weight: 700; letter-spacing: 1.6px; text-transform: uppercase;
    color: var(--text-dim); margin-bottom: 3px; }
  .rpc-eyebrow > span:first-child { white-space: nowrap; opacity: 0.85; }
  .rpc-title { font-family: var(--ln-serif, Georgia, serif); font-size: 1.24rem; font-weight: 700;
    color: var(--text); line-height: 1.3; }
  .rpc-tagline { font-family: var(--ln-serif, Georgia, serif); font-style: italic; font-size: 0.95rem;
    color: var(--accent2); margin-top: 1px; }
  .rpc-locked-note { font-size: 0.84rem; color: var(--text-dim); margin-top: 6px; }

  .rpc-move { display: inline-block; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-style: normal; font-size: 0.76em; font-weight: 700; color: var(--accent2);
    background: var(--bg-rpc-move); border: 1px solid var(--ln-border-soft); border-radius: 8px;
    padding: 1px 8px; vertical-align: 2px; white-space: nowrap; }
  .rpc-move.green { color: var(--ln-green-deep); background: var(--bg-rpc-movegreen); border-color: transparent; vertical-align: 0; font-size: 0.85em; }

  .rpc-board { flex: 0 0 92px; width: 92px; border-radius: 9px; overflow: hidden;
    border: 1px solid var(--border); box-shadow: var(--ln-shadow-sm, 0 1px 3px rgba(60,42,12,0.12)); }
  .rpc.locked .rpc-board { opacity: 0.55; filter: saturate(0.5); }

  .rpc-answer { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 11px;
    padding: 7px 12px; background: var(--bg-rpc-answer); border-radius: 10px;
    font-size: 0.86rem; color: var(--text); }
  .rpc-answer-label { font-size: 0.62rem; font-weight: 700; letter-spacing: 1.3px; text-transform: uppercase;
    color: var(--ln-green-deep); opacity: 0.85; }
  .rpc-answer-title { font-family: var(--ln-serif, Georgia, serif); font-style: italic; }

  .rpc-meta { font-size: 0.74rem; color: var(--text-dim); margin: 9px 0 4px; }
  .rpc-covered-pill { display: inline-block; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.3px;
    color: var(--ink-rp-danger-btn); background: var(--accent); border: 1px solid var(--accent); border-radius: 999px;
    padding: 2px 10px; white-space: nowrap; }

  .rpc .rp-ch-var, .rp-chpage .rp-ch-var { border-top: 1px dashed var(--line-rp-ch-var); padding: 9px 0 7px; }
  .rpc .rp-ch-var-head, .rp-chpage .rp-ch-var-head { align-items: center; margin-bottom: 4px; }
  .rpc-count { font-size: 0.67rem; font-weight: 700; color: var(--text-dim); background: var(--bg-hover);
    border: 1px solid var(--line-rpc-count); border-radius: 999px; padding: 1px 9px; white-space: nowrap; }
  .rpc-count.done { color: var(--ink-rp-danger-btn); background: var(--accent); border-color: var(--accent); }

  .rpc .rp-ch-line, .rp-chpage .rp-ch-line { display: flex; align-items: center; gap: 8px; font-size: 0.84rem; padding: 4px 6px; margin: 0; }
  .rpc .rp-ch-line.clickable, .rp-chpage .rp-ch-line.clickable { border-radius: 8px; }
  .rpc .rp-ch-line.clickable:hover, .rp-chpage .rp-ch-line.clickable:hover { background: var(--bg-rpc-answer); }
  .rpc-tick { flex: 0 0 15px; width: 15px; height: 15px; border-radius: 50%;
    border: 1.5px solid var(--border); box-sizing: border-box; position: relative; }
  .rpc-tick.done { border-color: var(--accent); background: var(--accent); }
  .rpc-tick.done::after { content: '✓'; position: absolute; inset: 0; display: flex; align-items: center;
    justify-content: center; color: var(--ink-rp-danger-btn); font-size: 9px; font-weight: 700; line-height: 1; }
  .rpc-line-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .rpc-leader { flex: 1; min-width: 14px; border-bottom: 1px dotted var(--line-rpc-leader);
    transform: translateY(-3px); }
  .rpc-line-act { font-size: 0.66rem; font-weight: 700; letter-spacing: 0.6px; text-transform: uppercase;
    color: var(--text-dim); opacity: 0.75; white-space: nowrap; }
  .rpc-line-act.go { color: var(--ln-green-deep); opacity: 0.9; }
  .rpc .rp-ch-line.clickable:hover .rpc-line-act,
  .rp-chpage .rp-ch-line.clickable:hover .rpc-line-act { opacity: 1; color: var(--ln-green-deep); }

  .rpc-actions { justify-content: flex-end; border-top: 1px solid var(--line-rpc-actions);
    padding-top: 12px; margin-top: 12px; }

  /* Collapsible chapters: header toggles the body open/closed */
  .rpc-toggle { cursor: pointer; }
  .rpc-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 10px; }
  .rpc-caret { flex: 0 0 30px; align-self: center; width: 30px; height: 30px; position: relative;
    border-radius: 50%; background: var(--bg-rpc-caret); border: 1px solid var(--ln-border-soft);
    transition: transform 0.2s ease, background 0.15s ease, border-color 0.15s ease; }
  .rpc-caret::before { content: '\25BE'; position: absolute; inset: 0; display: flex;
    align-items: center; justify-content: center; font-size: 0.92rem; line-height: 1; color: var(--dark-sq); }
  .rpc-toggle:hover .rpc-caret { background: var(--bg-ep-mv-active); border-color: var(--accent); }
  .rpc.collapsed .rpc-caret { transform: rotate(-90deg); }
  .rpc.collapsed .rpc-body { display: none; }

  /* Expand-all / collapse-all toolbar above the first chapter */
  .rp-ch-toolbar { display: flex; align-items: center; justify-content: space-between;
    gap: 10px; max-width: 760px; margin: 4px auto 10px; flex-wrap: wrap; }
  .rp-ch-toolbar-label { font-size: 0.74rem; font-weight: 700; letter-spacing: 0.4px;
    text-transform: uppercase; color: var(--text-dim); }
  .rp-ch-toolbar-btns { display: flex; gap: 8px; }
  .rp-tiny-btn { font-size: 0.72rem; padding: 6px 14px; }

  /* Preparedness banner: chunkier, friendlier segments */
  .rp-prep-bar { gap: 5px; }
  .rp-prep-seg { height: 13px; border-radius: 6px; }
  .rp-prep-seg.ready { background: linear-gradient(180deg, var(--bg-rp-prep-segready), var(--bg-rp-prep-segready-2)); border-color: var(--accent); }
  .rp-prep-head b { color: var(--ln-green-deep); }

  @media (max-width: 760px) {
    .rp-chapter-card.rpc { padding: 14px 14px 12px 19px; }
    .rpc-head { gap: 11px; }
    .rpc-medal { flex-basis: 40px; width: 40px; height: 40px; }
    .rpc-medal-num { font-size: 1rem; }
    .rpc-board { flex-basis: 76px; width: 76px; }
    .rpc-title { font-size: 1.1rem; }
    .rpc-tagline { font-size: 0.9rem; }
    .rpc-actions { justify-content: center; }
  }

  /* Stop iOS from zooming the page when a text field is tapped. Mobile Safari
     auto-zooms any input whose font is under 16px, so on touch devices bump the
     search bars and text inputs to 16px. Desktop keeps its smaller sizes. */
  @media (pointer: coarse) {
    .rp-open-search,
    .fable-search,
    .rp-create-input,
    input[type="text"],
    input[type="search"],
    textarea {
      font-size: 16px;
    }
  }

  /* ===== FOCUS MODE =====
     At the board, the app furniture goes away (Ed, 2026-08-08). The bottom tab
     bar and the En Passant header are hidden on the six screens where you are
     actually playing, and the breadcrumb becomes a slim bar pinned to the top —
     the one way out, so it must never scroll away. Toggled by epFocus() in
     app.js via `body.ep-focus`. See docs/focus-mode-plan.md. */
  body.ep-focus > header { display: none; }
  body.ep-focus .bottom-nav { display: none; }
  /* The nav's 54px reservation goes with it; keep the home-indicator inset. */
  body.ep-focus { padding-bottom: env(safe-area-inset-bottom, 0px); }

  /* The trail, pinned. Sticky rather than fixed on purpose: it stays IN flow,
     so nothing below it has to be re-padded per screen and nothing jumps when
     focus turns on.
     It is a deliberate surface, not a transparent strip. The page background
     carries two large radial tints, so a flat colour behind a see-through bar
     shows a seam — the exact reason pinning the board was reverted on
     2026-08-02. A bar that is visibly its own thing has no seam to match, and
     it is the shape of the app Ed is copying: back arrow, name, nothing else. */
  body.ep-focus #rep2-builder-crumbs,
  body.ep-focus #master-crumbs,
  body.ep-focus #book-line-crumbs,
  body.ep-focus #fable-crumbs {
    position: sticky;
    top: 0;
    z-index: 90;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    box-shadow: 0 2px 6px var(--shadow-hover);
    /* Full-bleed: the hosts sit inside padded columns, so pull out to the edges
       and put the padding back on the inside. */
    margin-left: -100vw; margin-right: -100vw;
    padding-left: 100vw; padding-right: 100vw;
    padding-top: calc(env(safe-area-inset-top, 0px) + 8px);
    padding-bottom: 8px;
    margin-bottom: 4px;
  }
  /* THE INSTALLED APP NEEDS MORE ROOM ABOVE THE TRAIL THAN THE RULE ABOVE GIVES
     IT (Ed, 2026-09-15, from a Test yourself screenshot on his home-screen app:
     "the top is kind of cut off and fading away ... This doesn't happen in
     safari"). In Safari the URL bar does the clearing and the trail lands
     correctly, which is why only the installed app shows it — so this is scoped
     to the installed app and Safari is untouched.
     max() rather than the raw inset, deliberately: the app declares
     apple-mobile-web-app-status-bar-style black-translucent with viewport-fit
     cover, so the page starts at the very top of the screen and the inset is the
     only thing holding the trail clear of the clock. A 0 there — which iOS
     standalone has long been unreliable about reporting — puts the trail under
     the status bar, which is exactly what his screenshot shows. The floor means
     it reads correctly whether or not the inset arrives. */
  @media (display-mode: standalone) {
    body.ep-focus #rep2-builder-crumbs,
    body.ep-focus #master-crumbs,
    body.ep-focus #book-line-crumbs,
    body.ep-focus #fable-crumbs {
      /* 20 -> 34 (Ed, 2026-09-15, from the drill in the installed app: "make the
         breadcrumbs just a little bit lower"). Clear of the status bar at 20;
         this is the nudge past clear, by his eye. */
      padding-top: calc(max(env(safe-area-inset-top, 0px), 52px) + 34px);
    }
    /* The En Passant banner gets the same treatment (Ed, 2026-09-15: "can you do
       the same for the top banner"). It is the trail's opposite number — the
       header shows on every screen focus mode does NOT hide — and it had the
       identical rule, 8px plus the inset and nothing else, so it was tight
       against the status bar in the installed app for the same reason. Same
       floor and same nudge, so the two read the same however you get there.
       Later in the file than the phone rule it overrides, which is what makes it
       win at equal specificity. */
    header { padding-top: calc(max(env(safe-area-inset-top, 0px), 52px) + 34px); }
  }
  /* Close the gap the header used to fill (Ed, 2026-08-08: "there's some space
     for the eval bar to move up a little bit"). Measured at 375x812 in focus:
     10px above the bar (.master-arena-inner's padding-top) and 24px below it
     (12 crumb margin + 4 banner padding + 8 board-row padding). Now 0 above and
     12 below, so the board sits 22px higher. Focus mode only — off it, the
     header is there and this spacing is what separates them. */
  body.ep-focus .master-arena-inner,
  body.ep-focus .book-line-inner,
  body.ep-focus .fable-player-inner { padding-top: 0; }
  body.ep-focus .rep-crumb-bar { padding-bottom: 0; }
  body.ep-focus #rep2-builder-crumbs .book-crumbs,
  body.ep-focus #master-crumbs .book-crumbs,
  body.ep-focus #book-line-crumbs .book-crumbs,
  body.ep-focus #fable-crumbs .book-crumbs { margin-bottom: 0; }

  /* The lesson's and the drill's trail on a laptop: a card, not a full-bleed
     strip. The strip is right on a phone, where it is pinned to the top of the
     screen and is the one way out. On a laptop nothing scrolls and the screen
     sits in from the edges, so a bar running edge to edge above it read as a
     leftover (Ed, 2026-08-14). It takes the same treatment as what it sits over:
     the same width, surface, border and 18px corners.
     After the focus block on purpose — same specificity, so it has to be later
     to win. These two only; the other two trails keep the strip. */
  @media (min-width: 761px) {
    body.ep-focus #fable-crumbs,
    body.ep-focus #master-crumbs {
      padding: 9px 18px;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 18px;
      box-shadow: 0 1px 3px var(--shadow-hover);
      margin-bottom: 10px;
    }
    /* The lesson card runs the full width of its column; the drill's board does
       not, so its trail is capped to the board's own width and centred over it. */
    body.ep-focus #fable-crumbs { margin-left: 0; margin-right: 0; }
    /* Floored at 360px: on a short window the board shrinks with the height, and
       the trail followed it down until "Training › Vienna Game · White" wrapped
       onto two lines. Below that width it stops matching the board. */
    body.ep-focus #master-crumbs {
      margin-left: auto; margin-right: auto;
      max-width: max(360px, var(--ms-bw));
    }
    #master-crumbs .book-crumbs { font-size: 1rem; gap: 5px 8px; }
    /* The drill sat hard against the top of the window with the spare height all
       below it. Split it one part above to three below, the same as the lesson,
       and keep the trail right above the board rather than stranded at the top.
       One spacer above the trail, and the stage takes the other three parts.
       The two flex bases are what make it safe: the spacer's is 0, so it can only
       ever take spare height, never claim height the board needs; the stage's is
       its own content, so when the window is too short the stage is what gives
       way, shrinking and scrolling exactly as it did before. Spacers on the
       column WITHOUT that pushed the trail off the top of the window, measured at
       -24 with 700px of extra content, and the trail is the way out.
       Scoped by tab: .master-arena-inner is the Repertoire builder's column too,
       and there the spacer shared the spare height with a row that grows, which
       pushed the board halfway down the window. */
    #master-arena .master-arena-inner::before { content:''; flex:1 1 0; min-height:0; }
    #master-arena .master-stage { flex:3 1 auto; }

    /* The Repertoire builder, same treatment: its trail is a card over the board
       and the walk panel, and the pair sit a quarter of the way down. */
    body.ep-focus #rep2-builder-crumbs {
      padding: 9px 18px;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 18px;
      box-shadow: 0 1px 3px var(--shadow-hover);
      margin-bottom: 10px;
      margin-left: 0; margin-right: 0;
    }
    #rep2-builder-crumbs .book-crumbs { font-size: 1rem; gap: 5px 8px; }
    #rep2-builder .master-arena-inner::before { content:''; flex:1 1 0; min-height:0; }
    #rep2-builder .master-board-row { flex:3 1 auto; }

    /* The Learn tab's study board, same treatment. Its column is sized by its
       content, so it has to be told to fill the window before there is any spare
       height to share out. The trail is capped to the board's own width, the same
       variable the board is capped by, so the two line up whatever the window. */
    body.ep-focus #book-line-crumbs {
      padding: 9px 18px;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 18px;
      box-shadow: 0 1px 3px var(--shadow-hover);
      margin: 0 auto 10px;
      /* The fallback matters: --ep-board-max is only defined above 720px of
         height, and without it the whole declaration is dropped and the trail
         runs the full width of the column. 480px is what the board falls back
         to there. */
      max-width: max(360px, var(--ep-board-max, 480px));
    }
    #book-line-crumbs .book-crumbs { font-size: 1rem; gap: 5px 8px; }
    .book-line-inner { flex: 1 1 auto; min-height: 0; }
    .book-line-inner::before { content:''; flex:1 1 0; min-height:0; }
    .book-board-center { flex:3 1 auto; min-height:0; }
  }
