diff --git a/CHANGELOG.md b/CHANGELOG.md
index a1ce9c8..45928c7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,8 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
## [Unreleased]
### Changed
+- **Rehearser stylesheet extraction.** The main Rehearser layout, cast, stage, training, library, and script-view
+ rules now live in `static/css/rehearser.css`, loaded after the core stylesheet with release cache busting.
- **Rehearser cast-renderer extraction.** The full cast-card renderer and delegated cast interactions now live in
`rehearser-cast.js`; the legacy `renderCastList` global and existing behavior remain available.
- **Provider status clarity.** TTS and STT backend discovery now reports configuration separately from liveness,
diff --git a/TODO.md b/TODO.md
index c1a3219..ec07c33 100644
--- a/TODO.md
+++ b/TODO.md
@@ -142,7 +142,8 @@ architecture change: the larger Audiobook/Rehearser/style split.
- [x] Split the Performance/resource diagnostic styles into `static/css/resource-diagnostics.css`.
- [x] Split background-job history and retry-panel styles into `static/css/background-jobs.css`.
- [x] Split Voice Design form and sample-grid styles into `static/css/voice-design.css`.
-- [ ] Split `style.css` by feature.
+- [x] Split `style.css` by feature; Rehearser rules now live in `static/css/rehearser.css` alongside the existing
+ diagnostics, background-job, and voice-design feature styles.
- [ ] Do not migrate to TypeScript or rewrite the frontend until behavior is covered by tests.
## P1 — Frontend performance and memory safety
diff --git a/VERSION b/VERSION
index 4d65d5e..067233c 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.24.44
+1.24.45
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index cdef598..5610ded 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -42,6 +42,7 @@ Done incrementally as global-scope ordered files (no ES-module change needed)
- ✅ `static/css/resource-diagnostics.css` — isolated Performance/resource diagnostic panel styles.
- ✅ `static/css/background-jobs.css` — isolated background-job history and retry-panel styles.
- ✅ `static/css/voice-design.css` — isolated Voice Design form, samples, and preview-match styles.
+- ✅ `static/css/rehearser.css` — isolated Rehearser modal, cast, stage, training, bookshelf, and script-view styles.
- ✅ `rehearser-train.js` — transcript normalization, LCS comparison, and word-level training feedback; microphone/state wiring remains in `rehearser.js`.
- ✅ `rehearser-library.js` — SQLite persistence helpers and IndexedDB migration; import/export and auto-save remain
in `rehearser.js` until their UI/event-handler dependencies are isolated.
diff --git a/static/css/rehearser.css b/static/css/rehearser.css
new file mode 100644
index 0000000..9ac2c94
--- /dev/null
+++ b/static/css/rehearser.css
@@ -0,0 +1,1135 @@
+/* ── Script Rehearser ───────────────────────────────────────────────────── */
+.reh-phase[hidden] { display: none; }
+
+/* ── IMSDb browser modal ────────────────────────────────────────────────── */
+.reh-imsdb-modal {
+ position: fixed; inset: 0; z-index: 1000;
+ background: rgba(0,0,0,.55); display: flex; align-items: center; justify-content: center;
+ padding: 24px;
+}
+.reh-imsdb-modal[hidden] { display: none; }
+.reh-imsdb-box {
+ background: var(--bg); border: 1px solid var(--border); border-radius: 14px;
+ width: 100%; max-width: 1100px; height: 88vh; display: flex; flex-direction: column;
+ box-shadow: 0 20px 60px rgba(0,0,0,.4); overflow: hidden;
+}
+.reh-imsdb-head {
+ display: flex; align-items: center; gap: 12px; padding: 14px 18px;
+ border-bottom: 1px solid var(--border); flex-shrink: 0;
+}
+.reh-imsdb-head h3 { margin: 0; font-size: 15px; display: flex; align-items: center; gap: 7px; white-space: nowrap; }
+.reh-imsdb-url-row { display:flex; align-items:center; gap:8px; padding:10px 18px; border-bottom:1px solid var(--border); background:var(--panel); }
+.reh-imsdb-url-row .reh-imsdb-url-input { flex:1; min-width:0; height:32px; padding:0 10px; border:1px solid var(--border); border-radius:7px; background:var(--surface); color:var(--text); font-size:13px; }
+.reh-imsdb-search {
+ flex: 1; height: 34px; padding: 0 12px; border: 1px solid var(--border);
+ border-radius: 8px; background: var(--input-bg, var(--panel)); color: var(--text); font-size: 13px;
+}
+.reh-imsdb-count { font-size: 12px; color: var(--subtext); white-space: nowrap; }
+.reh-imsdb-grid {
+ flex: 1; overflow-y: auto; padding: 16px;
+ display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 14px;
+ align-content: start;
+}
+.reh-imsdb-loading { grid-column: 1/-1; text-align: center; padding: 40px; color: var(--subtext); font-size: 13px; display: flex; align-items: center; justify-content: center; gap: 10px; }
+.reh-imsdb-load-error { grid-column: 1/-1; margin-bottom: 8px; padding: 10px 12px; border: 1px solid var(--red); border-left-width: 5px; border-radius: 8px; color: var(--red); background: rgba(220,38,38,.08); font-size: 13px; font-weight: 700; }
+.reh-scripts-link { white-space: nowrap; }
+.reh-imsdb-card {
+ cursor: pointer; border-radius: 8px; overflow: hidden; background: var(--card-bg, var(--panel));
+ border: 1px solid var(--border); transition: transform .12s, box-shadow .12s, border-color .12s;
+ display: flex; flex-direction: column;
+}
+.reh-imsdb-card:hover { transform: translateY(-3px); box-shadow: 0 8px 20px rgba(0,0,0,.25); border-color: var(--accent); }
+/* Poster image: use padding-bottom trick for guaranteed 2:3 ratio regardless of parent */
+.reh-imsdb-poster-wrap {
+ position: relative; width: 100%; padding-bottom: 150%; /* 2:3 ratio */ overflow: hidden; flex-shrink: 0;
+}
+.reh-imsdb-poster {
+ position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block;
+}
+.reh-imsdb-fallback {
+ position: absolute; inset: 0; width: 100%; height: 100%;
+ display: flex; flex-direction: column; align-items: center; justify-content: center;
+ gap: 6px; color: rgba(255,255,255,.85); font-size: 28px; padding: 10px; box-sizing: border-box;
+}
+.reh-imsdb-fallback-title {
+ font-size: 11px; font-weight: 700; text-align: center; line-height: 1.35;
+ color: rgba(255,255,255,.95); text-shadow: 0 1px 4px rgba(0,0,0,.5);
+ overflow: hidden; display: -webkit-box;
+ -webkit-line-clamp: 4; -webkit-box-orient: vertical;
+ word-break: break-word; padding: 0 4px; max-width: 100%;
+}
+.reh-imsdb-meta { padding: 7px 9px; display: flex; flex-direction: column; gap: 2px; }
+.reh-imsdb-title { font-size: 12px; font-weight: 600; color: var(--text); line-height: 1.25;
+ display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+.reh-imsdb-year { font-size: 11px; color: var(--subtext); }
+
+/* IMSDb list view */
+.reh-imsdb-grid.list-view {
+ display: flex; flex-direction: column; gap: 4px; padding: 12px 16px;
+}
+.reh-imsdb-grid.list-view .reh-imsdb-card {
+ flex-direction: row; align-items: center; gap: 0; height: 48px; border-radius: 6px;
+}
+.reh-imsdb-grid.list-view .reh-imsdb-card:hover { transform: none; box-shadow: 0 2px 8px rgba(0,0,0,.15); }
+.reh-imsdb-grid.list-view .reh-imsdb-poster-wrap {
+ width: 32px; min-width: 32px; height: 48px; padding-bottom: 0; flex-shrink: 0;
+}
+.reh-imsdb-grid.list-view .reh-imsdb-fallback {
+ font-size: 13px; gap: 0;
+}
+.reh-imsdb-grid.list-view .reh-imsdb-fallback-title { display: none; }
+.reh-imsdb-grid.list-view .reh-imsdb-poster {
+ position: static; width: 32px; height: 48px; object-fit: cover;
+}
+.reh-imsdb-grid.list-view .reh-imsdb-meta {
+ flex-direction: row; align-items: center; gap: 8px; padding: 0 12px; flex: 1; min-width: 0;
+}
+.reh-imsdb-grid.list-view .reh-imsdb-title {
+ display:block; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--text); -webkit-line-clamp: unset; font-size: 13px; flex: 1;
+}
+.reh-imsdb-grid.list-view .reh-imsdb-card { background:var(--surface); }
+.reh-imsdb-grid.list-view .reh-imsdb-year { margin-left: auto; white-space: nowrap; font-size: 11px; }
+
+/* View toggle buttons in modal header */
+.reh-imsdb-view-btns {
+ display: flex; gap: 2px; flex-shrink: 0;
+}
+.reh-imsdb-view-btn {
+ background: none; border: 1px solid var(--border); border-radius: 6px;
+ padding: 4px 8px; cursor: pointer; color: var(--subtext); font-size: 15px;
+ transition: background .12s, color .12s;
+}
+.reh-imsdb-view-btn:hover { background: var(--panel); color: var(--text); }
+.reh-imsdb-view-btn.active { background: var(--accent); border-color: var(--accent); color: #fff; }
+
+/* Loading spinner */
+@keyframes reh-spin { to { transform: rotate(360deg); } }
+.reh-imsdb-spinner {
+ display: inline-block; width: 16px; height: 16px; border-radius: 50%;
+ border: 2px solid var(--border); border-top-color: var(--accent);
+ animation: reh-spin .7s linear infinite; flex-shrink: 0;
+}
+
+/* ── Page break marker ──────────────────────────────────────────────────── */
+.reh-block-pagebreak {
+ display: flex; align-items: center; justify-content: center;
+ padding: 6px 0; margin: 4px 0; gap: 10px;
+}
+.reh-block-pagebreak::before, .reh-block-pagebreak::after {
+ content: ''; flex: 1; height: 1px; background: var(--border);
+}
+.reh-pagebreak-label {
+ font-size: 10px; color: var(--subtext); white-space: nowrap; letter-spacing: .04em;
+}
+
+/* ── Scroll mode (no A4 paper) ──────────────────────────────────────────── */
+.reh-page-wrap:not(.paginated) .reh-a4-page {
+ box-shadow: none; border: none; border-radius: 0; min-height: unset;
+ padding: 24px 32px;
+}
+
+/* ── Train Mode ─────────────────────────────────────────────────────────── */
+.reh-train-panel {
+ display: flex; flex-direction: column; gap: 10px;
+ padding: 12px 16px 16px;
+}
+.reh-train-header {
+ display: flex; align-items: center; gap: 10px; flex-shrink: 0;
+}
+.reh-train-badge {
+ display: inline-flex; align-items: center; gap: 5px;
+ font-size: 11px; font-weight: 800; letter-spacing: .08em;
+ color: var(--accent); background: rgba(37,99,235,.1);
+ border: 1px solid rgba(37,99,235,.25); border-radius: 6px; padding: 3px 10px;
+}
+.reh-train-progress { font-size: 13px; color: var(--subtext); font-variant-numeric: tabular-nums; }
+.reh-train-cue, .reh-train-myline, .reh-train-rec, .reh-train-result { flex-shrink: 0; }
+.reh-train-cue.no-cue { border-style: dashed; opacity: .7; }
+.reh-train-cue.no-cue .reh-train-cue-text { color: var(--subtext); font-style: italic; }
+.reh-train-cue-head, .reh-train-myline-head {
+ display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
+}
+.reh-train-cue-avatar { display: inline-flex; flex-shrink: 0; }
+.reh-train-cue-speaker { font-size: 13px; font-weight: 700; color: var(--text); }
+.reh-train-cue-tag {
+ font-size: 10px; font-weight: 700; letter-spacing: .06em;
+ background: rgba(37,99,235,.1); color: var(--accent);
+ border-radius: 4px; padding: 1px 7px; margin-left: 2px;
+}
+.reh-train-me-tag { background: rgba(16,185,129,.1); color: var(--green); }
+.reh-train-cue-state { margin-left: auto; font-size: 12px; }
+.reh-train-cue-text { font-size: 14px; line-height: 1.55; }
+.reh-train-cue-block { display: flex; flex-direction: column; gap: 2px; margin-bottom: 8px; }
+.reh-train-cue-block:last-child { margin-bottom: 0; }
+.reh-train-cue-who { font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
+.reh-train-cue-line { font-style: italic; color: var(--subtext); }
+.reh-train-my-text { font-size: 16px; color: var(--text); line-height: 1.6; font-weight: 600; min-height: 2em; }
+.reh-train-rec-head {
+ display: flex; align-items: center; gap: 10px; font-size: 13px; font-weight: 600;
+ color: var(--text); margin-bottom: 6px;
+}
+.reh-train-rec-state { flex: 1; }
+.reh-train-rec-timer { font-variant-numeric: tabular-nums; color: var(--red); font-weight: 700; font-size: 13px; }
+.reh-train-meters { margin-top: 6px; }
+.reh-train-result-head {
+ display: flex; align-items: center; gap: 8px;
+ font-size: 13px; font-weight: 700; color: var(--text); margin-bottom: 10px;
+}
+.reh-train-score { margin-left: auto; font-size: 15px; }
+.reh-train-compare-row { display: flex; align-items: baseline; gap: 10px; margin-bottom: 6px; flex-wrap: wrap; }
+.reh-train-compare-lbl { font-size: 11px; font-weight: 700; color: var(--subtext); white-space: nowrap; min-width: 58px; }
+.reh-train-compare-text { font-size: 14px; line-height: 1.7; flex: 1; }
+.tw-ok { color: var(--green); }
+.tw-miss { color: var(--red); text-decoration: line-through; }
+.tw-extra{ color: #f59e0b; }
+.reh-train-controls {
+ display: flex; align-items: center; justify-content: center; gap: 10px;
+ margin-top: 4px; flex-shrink: 0; padding: 8px 0;
+ border-top: 1px solid var(--border);
+}
+.reh-train-rec-btn { color: var(--red) !important; }
+.reh-impex-cat { font-size: 13px; font-weight: 700; color: var(--subtext); text-transform: uppercase; letter-spacing: .05em; margin: 16px 0 10px; }
+.reh-impex-note { font-size: 12px; color: var(--subtext); margin: -4px 0 10px; }
+.reh-impex-grid { display: flex; flex-wrap: wrap; gap: 10px; }
+.reh-impex-btn {
+ display: inline-flex; flex-direction: column; align-items: center; justify-content: center;
+ gap: 6px; padding: 14px 18px; min-width: 100px;
+ background: var(--card-bg); border: 1px solid var(--border); border-radius: 10px;
+ color: var(--text); font-size: 12px; font-weight: 600; cursor: pointer;
+ transition: border-color .15s, background .15s; text-align: center;
+}
+.reh-impex-btn .mdi { font-size: 22px; color: var(--accent); }
+.reh-impex-btn:hover { border-color: var(--accent); background: rgba(var(--accent-rgb),.07); }
+.reh-impex-btn--accent { border-color: rgba(var(--accent-rgb),.35); }
+
+/* Library */
+.reh-lib-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; flex-wrap: wrap; }
+.reh-lib-list { display: flex; flex-direction: column; gap: 8px; }
+.reh-lib-empty { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 28px 0; color: var(--subtext); }
+.reh-lib-empty .mdi { font-size: 36px; opacity: .4; }
+.reh-lib-empty p { text-align: center; font-size: 13px; line-height: 1.5; margin: 0; }
+.reh-lib-row { display: flex; align-items: center; gap: 12px; padding: 10px 12px; background: var(--panel); border: 1px solid var(--border); border-radius: 8px; flex-wrap: wrap; }
+
+/* Phase 2 backend row */
+.reh-cast2-backend { display: flex; gap: 10px; align-items: flex-end; margin-bottom: 14px; flex-wrap: wrap; }
+.reh-narrator-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; margin-bottom: 8px; border-top: 1px solid var(--border); flex-wrap: wrap; }
+.reh-cast-instruct-wrap { padding: 0 12px 8px; margin-top: -4px; }
+.reh-cast-instruct { width: 100%; font-size: 11px; padding: 4px 8px; border: 1px solid var(--border); border-radius: 4px; background: var(--surface); color: var(--subtext); font-family: var(--font); }
+.reh-cast-instruct:focus { outline: none; border-color: var(--accent); color: var(--text); }
+.reh-lib-current { border-color: var(--accent); background: rgba(137,180,250,.06); }
+.reh-lib-row-main { display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0; }
+.reh-lib-avatars { display: flex; gap: 4px; flex-shrink: 0; }
+.reh-lib-info { min-width: 0; flex: 1; }
+.reh-lib-info strong { display: block; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+.reh-lib-progress-wrap { height: 3px; background: var(--border); border-radius: 2px; overflow: hidden; margin-top: 5px; }
+.reh-lib-progress { height: 100%; background: var(--accent); }
+.reh-lib-row-actions { display: flex; gap: 6px; align-items: center; flex-shrink: 0; }
+
+/* Phase 1 */
+.reh-new-script-head {
+ display: flex; align-items: center; gap: 14px; margin-bottom: 6px; flex-wrap: wrap;
+}
+.reh-script-title-inline {
+ flex: 1; min-width: 160px; max-width: 360px;
+ height: 32px; padding: 0 10px; border: 1px solid var(--border); border-radius: 6px;
+ background: var(--input-bg, var(--surface)); color: var(--text); font-size: 14px;
+ font-weight: 600; transition: border-color .15s;
+}
+.reh-script-title-inline:focus { outline: none; border-color: var(--accent); }
+.reh-input-row { display: grid; grid-template-columns: 1fr 200px; gap: 14px; align-items: start; }
+.reh-input-row textarea { min-height: 220px; font-family: monospace; font-size: 13px; width: 100%; resize: vertical; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 10px; color: var(--text); }
+.reh-input-side { display: flex; flex-direction: column; gap: 8px; }
+
+/* ── Cast list: toolbar + Card/List view toggle ───────────────────────────── */
+.reh-cast-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; flex-wrap: wrap; }
+.reh-cast-count { font-size: 13px; color: var(--subtext); font-weight: 600; }
+.reh-view-toggle { display: inline-flex; border: 1px solid var(--border); border-radius: 9px; overflow: hidden; background: var(--panel); }
+.reh-view-btn { display: inline-flex; align-items: center; gap: 5px; padding: 7px 14px; border: none; background: transparent; color: var(--subtext); font-size: 13px; font-weight: 600; cursor: pointer; font-family: var(--font); transition: background .12s, color .12s; }
+.reh-view-btn + .reh-view-btn { border-left: 1px solid var(--border); }
+.reh-view-btn:hover { color: var(--text); }
+.reh-view-btn.active { background: var(--accent); color: #fff; }
+.reh-view-btn .mdi { font-size: 16px; }
+.reh-cast-controls { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; flex: 1; min-width: 0; justify-content: center; }
+.reh-cast-ctl { font-size: 13px; padding: 6px 9px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel); color: var(--text); font-family: var(--font); }
+.reh-cast-ctl:focus { outline: none; border-color: var(--accent); }
+.reh-cast-search { min-width: 150px; flex: 1 1 150px; max-width: 240px; }
+.reh-cast-sortdir { display: inline-flex; align-items: center; justify-content: center; width: 34px; padding: 6px; cursor: pointer; color: var(--subtext); }
+.reh-cast-sortdir:hover { color: var(--accent); border-color: var(--accent); }
+.reh-cast-sortdir .mdi { font-size: 17px; }
+
+/* ── Cast cards (shared) ──────────────────────────────────────────────────── */
+.reh-cast-card { background: var(--panel); border: 1px solid var(--border); border-radius: 14px; display: flex; flex-direction: column; transition: border-color .15s, opacity .15s, box-shadow .15s; }
+.reh-cast-card.reh-cast-narrator { border-color: var(--accent); }
+.reh-cast-card.reh-cast-ignored { opacity: .5; }
+.reh-cast-card.reh-cast-hidden-c { outline: 1px dashed var(--border); }
+.reh-cc-head { display: flex; gap: 12px; }
+.reh-cc-avatar { flex-shrink: 0; }
+/* rounded-square portrait (not a circle) — overrides the inline border-radius:50% */
+.reh-cc-avatar .reh-char-img, .reh-cc-avatar span, .reh-cc-avatar img { width: 64px !important; height: 64px !important; font-size: 28px !important; border-radius: 12px !important; }
+.reh-cc-titles { flex: 1; min-width: 0; }
+.reh-cc-name { font-size: 16px; font-weight: 800; line-height: 1.15; white-space: normal; overflow-wrap: anywhere; word-break: break-word; }
+.reh-cc-sub { font-size: 12px; color: var(--subtext); margin-top: 3px; line-height: 1.3; }
+.reh-cc-voicechip { display: inline-flex; align-items: center; gap: 5px; margin-top: 7px; font-size: 11px; font-weight: 600; color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); padding: 3px 9px; border-radius: 999px; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.reh-cc-voicechip.empty { color: var(--subtext); background: var(--surface); }
+.reh-cc-sample-btn { margin-top: 7px; display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 600; padding: 4px 10px; border-radius: 999px; border: 1px solid var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); color: var(--accent); cursor: pointer; font-family: var(--font); transition: background .12s; }
+.reh-cc-sample-btn:hover { background: color-mix(in srgb, var(--accent) 16%, transparent); }
+.reh-cc-sample-btn.playing { background: var(--accent); color: #fff; }
+.reh-cc-sample-btn.loading { pointer-events: none; opacity: .8; }
+.reh-cc-sample-btn.loading .mdi { animation: spin .8s linear infinite; }
+.reh-cc-sample-btn .mdi { font-size: 13px; }
+.reh-cc-voicechip .mdi { font-size: 13px; }
+.reh-cc-occupation { font-size: 11.5px; color: var(--subtext); font-style: italic; margin-top: 2px; line-height: 1.3; }
+.reh-cc-emotions { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 7px; }
+.reh-cc-emo-chip {
+ font-size: 10.5px; font-weight: 600; padding: 2px 7px; border-radius: 999px;
+ background: var(--panel); border: 1px solid var(--border); color: var(--text); white-space: nowrap;
+}
+.reh-cc-actions { display: flex; align-items: center; gap: 5px; }
+.reh-cc-actions-spacer { flex: 1; }
+.reh-cc-me-row { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--subtext); cursor: pointer; white-space: nowrap; }
+.reh-cc-me-row:hover { color: var(--text); }
+.reh-cc-me-row .mdi { font-size: 15px; }
+.reh-cc-extra { display: flex; flex-direction: column; gap: 8px; }
+
+/* ── Card view: "character card" look ─────────────────────────────────────── */
+.reh-cast-view-card { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; }
+.reh-cast-view-card .reh-cast-card { overflow: hidden; }
+.reh-cast-view-card .reh-cast-card:hover { box-shadow: 0 6px 22px rgba(0,0,0,.10); }
+.reh-cast-view-card .reh-cc-head { flex-direction: column; align-items: center; text-align: center; gap: 10px; padding: 22px 18px 16px; background: linear-gradient(180deg, color-mix(in srgb, var(--cc-color, var(--accent)) 24%, var(--panel)), var(--panel)); border-bottom: 1px solid var(--border); }
+.reh-cast-view-card .reh-cc-titles { flex: none; display: flex; flex-direction: column; align-items: center; }
+.reh-cast-view-card .reh-cc-avatar .reh-char-img, .reh-cast-view-card .reh-cc-avatar span, .reh-cast-view-card .reh-cc-avatar img { width: 104px !important; height: 104px !important; font-size: 44px !important; border-width: 3px !important; box-shadow: 0 4px 14px rgba(0,0,0,.20); }
+.reh-cast-view-card .reh-cc-name { font-size: 22px; }
+.reh-cast-view-card .reh-cc-actions { padding: 10px 16px; border-bottom: 1px solid var(--border); }
+.reh-cast-view-card .reh-cc-body { padding: 14px 16px 16px; gap: 10px; }
+
+/* ── List view: compact rows ──────────────────────────────────────────────── */
+.reh-cast-view-list { display: flex; flex-direction: column; gap: 8px; }
+.reh-cast-view-list .reh-cast-card { flex-direction: row; align-items: center; flex-wrap: wrap; gap: 14px; padding: 10px 14px; border-radius: 10px; }
+.reh-cast-view-list .reh-cc-head { flex: 1; min-width: 200px; align-items: center; gap: 12px; padding: 0; background: none; border: none; }
+.reh-cast-view-list .reh-cc-avatar .reh-char-img, .reh-cast-view-list .reh-cc-avatar span, .reh-cast-view-list .reh-cc-avatar img { width: 52px !important; height: 52px !important; font-size: 22px !important; }
+.reh-cast-view-list .reh-cc-titles { flex: 1; }
+.reh-cast-view-list .reh-cc-name { font-size: 16px; }
+.reh-cast-view-list .reh-cc-voicechip { margin-top: 4px; }
+.reh-cast-view-list .reh-cc-body { order: 2; flex: 0 0 240px; padding: 0; }
+.reh-cast-view-list .reh-cc-extra { display: none; } /* full editor lives in Card view */
+.reh-cast-view-list .reh-cc-voice > label { display: none; }
+.reh-cast-view-list .reh-cc-actions { order: 3; padding: 0; }
+.reh-cast-view-list .reh-cc-actions-spacer { display: none; }
+@media (max-width: 620px) {
+ .reh-cast-view-list .reh-cc-body { flex-basis: 100%; }
+}
+/* ── Online voice picker (try / alternatives / agree) ─────────────────────── */
+.reh-cc-online { border: 1px solid var(--border); border-radius: 9px; background: var(--surface); padding: 9px; display: flex; flex-direction: column; gap: 7px; }
+.reh-cc-online-head { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--subtext); display: flex; align-items: center; gap: 6px; }
+.reh-vo-toggle { margin-left: auto; background: none; border: none; color: var(--accent); font-size: 11px; font-weight: 700; cursor: pointer; font-family: inherit; }
+.reh-vo-list { display: flex; flex-direction: column; gap: 6px; }
+.reh-vo-row { display: flex; align-items: center; gap: 8px; padding: 6px 7px; border-radius: 7px; border: 1px solid transparent; }
+.reh-vo-row.current { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--surface)); }
+.reh-vo-play { flex-shrink: 0; width: 30px; height: 30px; border-radius: 50%; border: 1px solid var(--border); background: var(--panel); color: var(--accent); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; font-size: 16px; transition: all .12s; }
+.reh-vo-play:hover { border-color: var(--accent); }
+.reh-vo-play.playing { background: var(--accent); color: #fff; border-color: var(--accent); }
+.reh-vo-info { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.2; }
+.reh-vo-name { font-size: 13px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.reh-vo-meta { font-size: 10px; color: var(--subtext); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.reh-vo-badge { flex-shrink: 0; font-size: 11px; font-weight: 700; color: var(--accent); display: inline-flex; align-items: center; gap: 3px; }
+.reh-vo-use { flex-shrink: 0; }
+.reh-vo-play.loading { pointer-events: none; }
+.reh-vo-play.loading .mdi { animation: spin .8s linear infinite; }
+/* change-voice tools (library / online search) */
+.reh-vo-tools { display: flex; gap: 6px; }
+.reh-vo-tool { flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 5px; padding: 6px 8px; font-size: 12px; font-weight: 600; border: 1px solid var(--border); border-radius: 7px; background: var(--panel); color: var(--subtext); cursor: pointer; font-family: inherit; transition: all .12s; }
+.reh-vo-tool:hover { color: var(--text); border-color: var(--accent); }
+.reh-vo-tool.active { color: var(--accent); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--panel)); }
+.reh-vo-tool-panel { display: flex; flex-direction: column; gap: 7px; }
+.reh-vo-search-row { display: flex; gap: 6px; }
+.reh-vo-search-row .reh-vo-search-input { flex: 1; }
+.reh-vo-local-input, .reh-vo-search-input { width: 100%; padding: 7px 9px; font-size: 12px; border: 1px solid var(--border); border-radius: 7px; background: var(--panel); color: var(--text); font-family: inherit; }
+.reh-vo-local-input:focus, .reh-vo-search-input:focus { outline: none; border-color: var(--accent); }
+.reh-vo-results { display: flex; flex-direction: column; gap: 6px; max-height: 230px; overflow-y: auto; }
+.reh-vo-hint { font-size: 11px; color: var(--subtext); padding: 4px 2px; display: flex; align-items: center; gap: 6px; }
+.reh-cc-iconbtn { width: 28px; height: 28px; border-radius: 6px; border: 1px solid var(--border); background: var(--surface); color: var(--subtext); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; font-size: 15px; transition: all .12s; }
+.reh-cc-iconbtn:hover { color: var(--text); border-color: var(--accent); }
+.reh-cc-iconbtn.active { background: var(--accent); color: #fff; border-color: var(--accent); }
+.reh-cc-del:hover { color: var(--red); border-color: var(--red); }
+.reh-cc-body { display: flex; flex-direction: column; gap: 8px; }
+.reh-cc-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; }
+.reh-cc-field { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
+.reh-cc-field > label { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--subtext); }
+.reh-cc-field select, .reh-cc-field input { width: 100%; min-width: 0; font-size: 12px; padding: 5px 7px; border: 1px solid var(--border); border-radius: 5px; background: var(--surface); color: var(--text); font-family: var(--font); }
+.reh-cc-voice .vp-root { width: 100%; min-width: 0; flex: 1 1 auto; }
+.reh-cc-voice .vp-trigger { min-height: 30px; font-size: 12px; }
+.reh-cc-voice .vp-drop { max-height: 310px; z-index: 80; }
+.reh-cc-field select:focus, .reh-cc-field input:focus { outline: none; border-color: var(--accent); }
+.reh-cc-soul { border-top: 1px solid var(--border); padding-top: 6px; }
+.reh-cc-soul > summary { font-size: 11px; font-weight: 600; color: var(--subtext); cursor: pointer; display: flex; align-items: center; gap: 6px; list-style: none; }
+.reh-cc-soul > summary::-webkit-details-marker { display: none; }
+.reh-cc-develop { margin-left: auto; padding: 2px 8px; font-size: 11px; }
+.reh-cc-soul-text { width: 100%; margin-top: 6px; min-height: 64px; resize: vertical; font-size: 12px; padding: 6px 8px; border: 1px solid var(--border); border-radius: 5px; background: var(--surface); color: var(--text); font-family: var(--font); line-height: 1.45; }
+.reh-cc-soul-text:focus { outline: none; border-color: var(--accent); }
+.reh-cc-more { border-top: 1px solid var(--border); padding-top: 8px; margin-top: 2px; }
+/* Cast card reusing the exact Library card (_charCardHtml) — no extra
+ border/shadow around it, its own 16px-radius styling already stands
+ alone; the Rehearser-only controls strip sits directly under it. */
+.reh-cast-card-libcard { background: none; border: none; box-shadow: none; }
+.reh-cast-card-libcard .lib-char-card { cursor: pointer; }
+.reh-cast-card-libcard .lib-char-select { display: none; }
+.reh-cast-card-libcard > .reh-cc-body { padding: 12px 4px 4px; }
+.reh-cc-controls { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
+.reh-cc-more > summary { font-size: 11.5px; font-weight: 700; color: var(--subtext); cursor: pointer; display: flex; align-items: center; gap: 6px; list-style: none; }
+.reh-cc-more > summary::-webkit-details-marker { display: none; }
+.reh-cc-more[open] > summary { color: var(--accent); margin-bottom: 8px; }
+.reh-cc-more .reh-cc-extra { margin-top: 8px; }
+
+
+.reh-tb-btn { width: 36px; height: 36px; border-radius: 50%; border: 1px solid var(--border); background: var(--panel); color: var(--text); cursor: pointer; font-size: 16px; display: flex; align-items: center; justify-content: center; transition: background .15s; }
+.reh-tb-btn:hover { background: var(--border); }
+.reh-tb-play { background: var(--accent); color: #fff; border-color: var(--accent); font-size: 18px; }
+.reh-tb-play:hover { opacity: .85; }
+.reh-btn-active { background: var(--accent) !important; color: #fff !important; border-color: var(--accent) !important; }
+.reh-tb-progress-wrap { flex: 1; height: 4px; background: var(--border); border-radius: 2px; overflow: hidden; }
+.reh-tb-progress { height: 100%; background: var(--accent); width: 0%; transition: width .3s ease; }
+.reh-tb-label { font-size: 12px; color: var(--subtext); white-space: nowrap; font-variant-numeric: tabular-nums; }
+
+/* Stage: script page + character sidebar (same .ab-cv-side look Read Aloud's
+ Casting view uses). The sidebar sticks in place while the script scrolls. */
+.reh-stage-area { display: flex; gap: 12px; align-items: flex-start; transition: none; }
+.reh-stage-area .reh-page-wrap { flex: 1; min-width: 0; }
+.reh-stage-area .ab-cv-side {
+ width: 220px; flex-shrink: 0; position: sticky; top: 12px;
+ max-height: calc(100vh - 160px);
+}
+.reh-stage-area.side-collapsed .ab-cv-side { width: 44px; }
+.reh-stage-area .ab-cv-side .ab-cv-chars { flex: 1; overflow-y: auto; }
+@media (max-width: 767px) {
+ /* The sidebar was a fixed 220px column next to the script page — on a
+ phone-width viewport that left almost no room for the page itself.
+ Stack the sidebar above the page instead, collapsible the same way it
+ already is on desktop, just full-width instead of a narrow rail. */
+ .reh-stage-area { flex-direction: column; }
+ .reh-stage-area .ab-cv-side { width: 100%; position: static; max-height: 220px; order: -1; }
+ .reh-stage-area.side-collapsed .ab-cv-side { width: 100%; max-height: 44px; overflow: hidden; }
+ .reh-page-wrap { padding: 16px 10px; }
+ .reh-a4-page { padding: 20px 14px; }
+}
+
+/* A4 page */
+.reh-page-wrap { padding: 24px 16px; background: var(--panel); min-height: 60vh; }
+.reh-a4-page {
+ width: min(1180px, 96%); min-height: 400px;
+ background: #fff; color: #1a1a2e;
+ padding: 56px 72px;
+ box-shadow: 0 4px 24px rgba(0,0,0,.18);
+ margin: 0 auto;
+ font-family: 'Georgia', 'Times New Roman', serif;
+ border-radius: 2px;
+}
+[data-theme="dark"] .reh-a4-page { background: #f8f8f2; color: #1a1a2e; }
+.reh-page-title { font-size: 22px; font-weight: 700; text-align: center; margin-bottom: 36px; letter-spacing: .04em; text-transform: uppercase; color: #1a1a2e; border-bottom: 2px solid #1a1a2e; padding-bottom: 12px; }
+.reh-script-lines { display: flex; flex-direction: column; gap: 0; }
+
+/* Each dialog block — flex: gutter | content */
+.reh-block { display: flex; align-items: stretch; padding: 8px 12px 8px 2px; border-left: 3px solid transparent; border-radius: 0 4px 4px 0; margin-bottom: 2px; transition: background .2s, border-color .2s; }
+.reh-block-inner { flex: 1; min-width: 0; }
+.reh-block-head { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; flex-wrap: nowrap; }
+.reh-block-name { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .12em; white-space: nowrap; }
+.reh-block-head-spacer { flex: 1; min-width: 8px; }
+.reh-block-badge { font-size: 10px; padding: 1px 6px; border-radius: 10px; font-family: sans-serif; flex-shrink: 0; }
+.reh-badge-tts { background: rgba(137,180,250,.2); color: #2563eb; }
+.reh-badge-me { background: rgba(166,227,161,.25); color: #16a34a; }
+.reh-block-dialog { font-size: 15px; line-height: 1.65; padding-left: 40px; color: #1a1a2e; }
+.reh-direction { font-style: italic; color: #666; font-size: 13px; padding: 4px 12px; }
+.reh-char-img { vertical-align: middle; }
+.reh-line-play-avatar { border:0; background:transparent; padding:0; margin:0 2px 0 0; cursor:pointer; border-radius:50%; position:relative; display:inline-flex; align-items:center; justify-content:center; }
+.reh-line-play-avatar:hover { box-shadow:0 0 0 3px rgba(37,99,235,.18); }
+/* This badge overlay is meant to sit in the corner of the bigger avatar
+ circle (dialogue's own play button) — .reh-action-play-btn (the plain
+ narrator play button, no avatar underneath) also carries the shared
+ .reh-line-play-avatar class for click wiring, so it was ALSO getting
+ this badge layered on top of its own inline icon: two visibly different
+ play icons stacked on one small button. Excluded here; it's styled to
+ look like this same badge directly instead (one icon, not two). */
+.reh-line-play-avatar:not(.reh-action-play-btn)::after { content:'\F040A'; font-family:'Material Design Icons'; position:absolute; right:-5px; bottom:-5px; width:16px; height:16px; border-radius:50%; background:var(--accent); color:#fff; font-size:11px; display:flex; align-items:center; justify-content:center; box-shadow:0 1px 4px rgba(0,0,0,.24); }
+.reh-line-active .reh-line-play-avatar:not(.reh-action-play-btn)::after { content:'\F03E4'; background:var(--red); }
+
+/* Narrator paragraph play button — no avatar image underneath, so styled
+ to look like the same small blue circular badge dialogue's own play
+ button already shows (see the ::after rule above), just as its own real
+ icon instead of a badge layered over something else. */
+.reh-action-play-btn {
+ width: 16px; height: 16px; margin: 0 5px 0 0; vertical-align: middle;
+ border: none; color: #fff; font-size: 11px;
+ background: var(--accent); flex-shrink: 0;
+}
+.reh-action-play-btn:hover { box-shadow: 0 0 0 3px rgba(37,99,235,.18); }
+.reh-line-active .reh-action-play-btn { background: var(--red); }
+
+/* Gutter: narrow play/range column left of each dialog block */
+.reh-gutter-btn { width:22px; min-width:22px; flex-shrink:0; align-self:stretch; background:none; border:none; cursor:pointer; display:flex; align-items:center; justify-content:center; color:transparent; transition:color .12s; font-size:9px; padding:0; margin-right:4px; }
+.reh-block:hover .reh-gutter-btn { color:var(--accent); }
+.reh-range-start .reh-gutter-btn { color:#10b981 !important; }
+.reh-range-end .reh-gutter-btn { color:#ef4444 !important; }
+.reh-in-range { background:rgba(245,158,11,.05) !important; }
+.reh-range-start { border-left-color:#10b981 !important; }
+.reh-range-end { border-left-color:#ef4444 !important; }
+.reh-practice-info { display:none; align-items:center; gap:3px; font-size:10px; color:var(--subtext); white-space:nowrap; margin-left:6px; }
+.reh-practice-info.visible { display:inline-flex; }
+
+/* ── Bulk-edit (line selection) ───────────────────────────────────────────── */
+.reh-bulk-bar {
+ display:flex; align-items:center; gap:6px; flex-wrap:wrap;
+ padding:6px 10px; margin:6px 0; border:1px solid var(--border); border-radius:8px;
+ background:var(--panel); position:sticky; top:0; z-index:6;
+}
+.reh-bulk-count { font-size:12px; font-weight:700; color:var(--accent); min-width:74px; }
+.reh-bulk-sep { width:1px; align-self:stretch; background:var(--border); margin:2px 2px; }
+.reh-bulk-spacer{ flex:1; }
+/* Leading checkbox in front of each line */
+.reh-bulk-check {
+ display:none; align-items:center; justify-content:center; flex-shrink:0;
+ width:26px; cursor:pointer; color:var(--subtext); font-size:20px; align-self:center;
+}
+.reh-bulk-mode .reh-bulk-check { display:inline-flex; }
+.reh-bulk-check.checked { color:var(--accent); }
+.reh-bulk-check:hover { color:var(--accent); }
+/* Non-dialog blocks: keep the checkbox inline at the start */
+.reh-bulk-mode .reh-act,
+.reh-bulk-mode .reh-scene,
+.reh-bulk-mode .reh-action-block,
+.reh-bulk-mode .reh-transition,
+.reh-bulk-mode .reh-direction,
+.reh-bulk-mode .reh-block-pagebreak { position:relative; }
+/* Ignored lines: greyed out but still visible */
+.reh-line-ignored { opacity:.42; filter:grayscale(1); }
+.reh-line-ignored .reh-block-dialog { text-decoration:line-through; text-decoration-color:rgba(0,0,0,.3); }
+/* Hidden lines revealed in bulk mode: dashed, dimmed */
+.reh-line-hidden { opacity:.5; outline:1px dashed var(--border); outline-offset:-2px; }
+
+/* Active line */
+.reh-block-active { background: rgba(37,99,235,.07); border-left-color: #2563eb; }
+.reh-block-active .reh-block-dialog { font-weight: 500; }
+
+/* Emotion picker button */
+.reh-emo-btn { font-size:11px; border:1px solid rgba(0,0,0,.15); border-radius:4px; padding:2px 6px; background:rgba(0,0,0,.03); color:#555; cursor:pointer; margin-left:auto; flex-shrink:0; font-family:sans-serif; display:inline-flex; align-items:center; gap:3px; white-space:nowrap; max-width:115px; overflow:hidden; }
+.reh-emo-btn:hover { border-color:var(--accent); }
+.reh-emo-btn.has-emotion { background:rgba(37,99,235,.07); color:#1a1a2e; border-color:rgba(37,99,235,.3); font-weight:600; }
+.reh-emo-label { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:72px; }
+
+/* Emotion picker popover */
+.reh-emo-popover { background:var(--bg); border:1px solid var(--border); border-radius:8px; box-shadow:0 8px 32px rgba(0,0,0,.18); overflow:hidden; }
+.reh-emo-pop-inner { display:flex; flex-direction:column; max-height:300px; }
+.reh-emo-search { padding:8px 10px; border:none; border-bottom:1px solid var(--border); background:var(--surface); font-size:12px; outline:none; color:var(--text); width:100%; box-sizing:border-box; }
+.reh-emo-list { overflow-y:auto; flex:1; padding:4px; }
+.reh-emo-item { display:flex; align-items:center; gap:8px; padding:5px 8px; border-radius:4px; cursor:pointer; font-size:12px; color:var(--text); }
+.reh-emo-item:hover { background:var(--panel); }
+.reh-emo-item.selected { background:rgba(37,99,235,.1); color:var(--accent); font-weight:600; }
+.reh-emo-item-emoji { font-size:15px; flex-shrink:0; }
+.reh-emo-custom-row { padding:6px; border-top:1px solid var(--border); }
+
+/* Markdown formatting toolbar */
+.reh-fmt-toolbar { display:flex; gap:3px; align-items:center; padding:4px 6px; background:var(--panel); border:1px solid var(--border); border-bottom:none; border-radius:3px 3px 0 0; flex-wrap:wrap; }
+.reh-fmt-btn { padding:2px 8px; border:1px solid var(--border); border-radius:3px; background:var(--surface); cursor:pointer; font-size:12px; color:var(--text); line-height:1.4; }
+.reh-fmt-btn:hover { background:var(--border); }
+.reh-fmt-hint { font-size:10px; color:var(--subtext); margin-left:auto; font-style:italic; }
+
+/* Markdown inside A4 */
+.reh-a4-page strong, .reh-paper strong { font-weight:700; }
+.reh-a4-page em, .reh-paper em { font-style:italic; }
+.reh-a4-page u, .reh-paper u { text-decoration:underline; }
+.reh-a4-page del, .reh-paper del { text-decoration:line-through; opacity:.65; }
+.reh-a4-page .reh-hl, .reh-paper .reh-hl { background:#fef3c7; border-radius:2px; padding:0 1px; }
+
+/* Word-level highlight during playback */
+.reh-word { display:inline; border-radius:2px; transition:background .06s, color .06s; }
+.reh-word-active { background:rgba(37,99,235,.22); color:#1a1a2e; border-radius:2px; padding:0 1px; }
+
+/* ── Proper screenplay A4 formatting ─────────────────────────────────── */
+/* Use Courier New throughout — 1 page ≈ 1 minute of screen time */
+/* Base play font size. Everything below is expressed in `em` so the whole
+ play scales together via --reh-stage-scale (the +A / -A control). */
+.reh-a4-page {
+ font-family: 'Courier New', Courier, monospace !important;
+ font-size: calc(12pt * var(--reh-stage-scale, 1));
+ line-height: 1.6;
+}
+.reh-page-title {
+ font-family: 'Courier New', Courier, monospace;
+ font-size: 1.17em;
+}
+/* Scene headings: all-caps, bold, with spacing */
+.reh-scene {
+ font-size: 1em;
+ letter-spacing: .04em;
+}
+/* Narrator / action text and spoken dialog share ONE size (1em) so the play
+ reads evenly — no narrator-vs-dialogue size mismatch. */
+.reh-action-block { font-size: 1em; }
+.reh-action-text { font-size: 1em; }
+
+/* Act headings: larger, extra top space */
+.reh-act { font-size: 1.08em; }
+
+/* Transitions: right-aligned */
+.reh-transition { font-size: 1em; }
+
+/* Dialog block — screenplay format */
+.reh-block-name {
+ font-size: 0.92em;
+ letter-spacing: .1em;
+}
+.reh-block-dialog {
+ font-size: 1em;
+ padding-left: 32px; /* ~1.5" dialog indent */
+ max-width: 440px; /* ~3.5" dialog column width */
+}
+/* Parenthetical directions: centered-ish */
+.reh-direction {
+ font-size: 0.92em;
+ padding-left: 80px;
+ color: #444;
+}
+
+/* Script editor modal */
+.reh-modal-overlay { position:fixed; inset:0; z-index:2000; background:rgba(0,0,0,.45); display:flex; align-items:center; justify-content:center; padding:16px; }
+.reh-modal-overlay[hidden] { display: none !important; }
+.reh-modal-box { background:var(--bg); border-radius:10px; box-shadow:0 20px 60px rgba(0,0,0,.3); overflow:hidden; display:flex; flex-direction:column; }
+.reh-modal-head { display:flex; align-items:center; justify-content:space-between; padding:12px 16px; border-bottom:1px solid var(--border); font-weight:700; }
+
+/* TTS status bar */
+.reh-tts-status-bar { display: flex; align-items: center; gap: 12px; padding: 8px 16px; background: var(--panel); border-top: 1px solid var(--border); font-size: 13px; }
+.reh-tts-status-bar[hidden] { display: none; }
+
+/* Recording overlay */
+.reh-rec-overlay {
+ position: sticky; bottom: 0; z-index: 60;
+ background: var(--bg); border-top: 2px solid var(--green);
+ box-shadow: 0 -4px 20px rgba(0,0,0,.15);
+}
+.reh-rec-overlay[hidden] { display: none; }
+.reh-rec-overlay-inner { padding: 14px 20px; max-width: 860px; margin: 0 auto; }
+.reh-rec-cue { font-size: 14px; margin-bottom: 10px; line-height: 1.5; }
+.reh-rec-cue-name { font-weight: 700; font-size: 12px; text-transform: uppercase; letter-spacing: .1em; }
+.reh-rec-cue-text { font-size: 17px; margin-top: 4px; padding: 8px 12px; background: rgba(166,227,161,.1); border-left: 3px solid var(--green); border-radius: 0 4px 4px 0; font-style: italic; }
+.reh-rec-inner { padding: 14px 20px; max-width: 860px; margin: 0 auto; }
+
+/* Summary */
+.reh-summary-list { display: flex; flex-direction: column; gap: 10px; }
+.reh-summary-row { display: flex; align-items: flex-start; gap: 10px; padding: 10px; background: var(--panel); border-radius: 6px; }
+
+/* Console / sticky transport bar */
+.reh-console {
+ position: sticky; top: 0; z-index: 50;
+ background: var(--bg); border-bottom: 1px solid var(--border);
+ display: flex; align-items: center; gap: 10px; padding: 7px 14px; flex-wrap: wrap;
+}
+.reh-console-transport { display: flex; gap: 4px; align-items: center; flex-shrink: 0; }
+.reh-console-progress { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 80px; }
+/* flex-shrink:0 with the flexbox-default min-width:auto meant this block could
+ never shrink below its unwrapped natural width (confirmed live: ~1560px of
+ buttons in a 524px-wide bar) — flex-wrap never got a chance to kick in since
+ the item was never considered "too wide to fit" by the wrapping algorithm,
+ so on any real (non-ultrawide) window the tail of the toolbar — font size,
+ page mode, Train, Exit — silently fell off-screen with no scrollbar to
+ reach it. Allowing it to shrink (and explicitly zeroing min-width, since
+ flex items default to min-width:auto regardless of any width you set) lets
+ it wrap onto additional rows within the available space instead. */
+.reh-console-actions { display: flex; gap: 6px; align-items: center; flex-shrink: 1; min-width: 0; flex-wrap: wrap; }
+
+/* Synth progress bar */
+/* Tone / style backend warning bar */
+.reh-tone-warn {
+ display: flex; align-items: flex-start; gap: 8px; padding: 7px 14px;
+ background: rgba(234,179,8,.1); border-bottom: 1px solid rgba(234,179,8,.35);
+ font-size: 12px; color: #92400e;
+}
+.reh-tone-warn[hidden] { display: none; }
+.reh-tone-warn .mdi { color: #d97706; font-size: 14px; flex-shrink: 0; }
+/* Aligns the alert icon with the table's own text baseline rather than the
+ whole (now taller, multi-row) bar. */
+.reh-tone-warn > .mdi { margin-top: 3px; }
+.reh-tone-warn-close {
+ margin-left: auto; background: none; border: none; cursor: pointer;
+ color: #92400e; font-size: 13px; line-height: 1; padding: 2px 4px; border-radius: 3px;
+ opacity: .7; flex-shrink: 0;
+}
+.reh-tone-warn-close:hover { opacity: 1; background: rgba(0,0,0,.06); }
+
+/* Backend tone/identity comparison table — replaced a run-on sentence with a
+ button wedged mid-clause (confirmed live: read badly and wrapped worse).
+ Same two facts, laid out as two columns instead of two clauses. */
+.reh-tone-cmp { border-collapse: collapse; font-size: 12px; }
+.reh-tone-cmp th {
+ text-align: left; font-weight: 600; padding: 2px 14px 4px 0; color: #92400e;
+ opacity: .75; white-space: nowrap;
+}
+.reh-tone-cmp td { padding: 3px 14px 3px 0; white-space: nowrap; }
+.reh-tone-cmp tr:first-child td { padding-top: 0; }
+.reh-tone-cmp-current td:first-child { font-weight: 700; }
+.reh-tone-cmp-current-tag {
+ font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
+ color: #92400e; opacity: .7;
+}
+.reh-tone-cmp-yes { color: #15803d; }
+.reh-tone-cmp-no { color: #b91c1c; opacity: .8; }
+.reh-tone-cmp-yes .mdi, .reh-tone-cmp-no .mdi { font-size: 13px; }
+.reh-tone-switch-btn { padding: 2px 10px !important; font-size: 11px !important; }
+.reh-tone-cmp-note { margin: 4px 0 0; font-size: 11.5px; opacity: .85; }
+
+/* Synth progress bar — more prominent while running */
+.reh-synth-bar {
+ display: flex; align-items: center; gap: 10px; padding: 7px 16px;
+ background: linear-gradient(90deg, rgba(37,99,235,.08), rgba(37,99,235,.04));
+ border-bottom: 2px solid rgba(37,99,235,.3); font-size: 12px;
+ box-shadow: 0 2px 8px rgba(37,99,235,.12);
+}
+.reh-synth-bar[hidden] { display: none; }
+.reh-synth-bar-lbl {
+ font-size: 11px; font-weight: 700; color: var(--accent); white-space: nowrap;
+ display: flex; align-items: center; gap: 4px; letter-spacing: .04em;
+}
+.reh-synth-track { flex: 1; height: 6px; background: var(--border); border-radius: 3px; overflow: hidden; }
+.reh-synth-fill { height: 100%; background: linear-gradient(90deg, var(--accent), #7c3aed); width: 0%; transition: width .25s ease; border-radius: 3px; }
+.reh-synth-label { font-size: 12px; font-weight: 600; color: var(--accent); white-space: nowrap; min-width: 48px; text-align: right; }
+
+/* Synthesizing dot — pulsing spinner ring */
+@keyframes reh-synth-pulse { 0%,100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.5); opacity: .5; } }
+.reh-synth-dot.synthesizing {
+ background: var(--accent);
+ animation: reh-synth-pulse .8s ease-in-out infinite;
+ box-shadow: 0 0 0 3px rgba(37,99,235,.25);
+}
+
+/* Row highlight while a line is being synthesized */
+.reh-line-synthesizing {
+ background: rgba(37,99,235,.06) !important;
+ border-left-color: var(--accent) !important;
+ transition: background .15s;
+}
+
+/* Play font-size stepper (+A / -A) */
+.reh-fontsize-ctl { display: inline-flex; gap: 2px; }
+/* No padding override here on purpose — confirmed live these two buttons
+ rendered ~18px shorter than every sibling toolbar button (2px 7px vs the
+ base .btn-secondary.btn-sm's 9px 18px) because this rule fought that
+ padding instead of inheriting it. Font weight is the only real
+ customization these need. */
+/* Padding now matches every sibling button exactly, but a residual gap
+ remained — plain "A" + superscript text computes a shorter natural
+ line-height than an icon+text button, and guessing at a line-height
+ multiplier to compensate (tried 1: overshot the other way, to 35.7px
+ against siblings' 43px) is fragile. Match the sibling toolbar buttons'
+ actual height directly instead of trying to reverse-engineer it via
+ line-height. */
+.reh-fontsize-btn { font-weight: 700; height: 43px; display: inline-flex; align-items: center; justify-content: center; }
+.reh-fontsize-btn .reh-fontsize-plus, .reh-fontsize-btn .reh-fontsize-minus { font-size: 9px; vertical-align: super; margin-left: 1px; }
+
+/* Act heading on A4 page */
+.reh-act {
+ font-family: 'Courier New', Courier, monospace; font-size: 13px; font-weight: 700;
+ text-transform: uppercase; letter-spacing: .14em; color: #1a1a2e;
+ margin: 28px 0 8px; padding: 7px 0; text-align: center;
+ border-top: 2.5px solid #1a1a2e; border-bottom: 2.5px solid #1a1a2e;
+ cursor: pointer;
+}
+
+/* Chapter marker — a horizontal rule with the chapter's own title/number
+ between the two halves, so a chapter break is visible while scrolling the
+ script itself and not just audible as a pause in the exported audiobook. */
+.reh-chapter-marker {
+ display: flex; align-items: center; gap: 14px;
+ margin: 32px 0 20px; position: relative;
+}
+.reh-chapter-marker-line { flex: 1; height: 1px; background: var(--border); }
+.reh-chapter-marker-label {
+ font-weight: 700; text-transform: uppercase; letter-spacing: .08em;
+ font-size: 13px; color: var(--subtext); white-space: nowrap; padding: 0 2px;
+}
+.reh-chapter-marker:hover .reh-edit-btn { opacity: 1; }
+
+/* Inline edit button — shown on hover */
+.reh-edit-btn {
+ opacity: 0; transition: opacity .12s; border: 1px solid transparent;
+ background: none; cursor: pointer; font-size: 10px; color: var(--subtext);
+ padding: 1px 5px; border-radius: 3px; line-height: 1; flex-shrink: 0;
+}
+.reh-block:hover .reh-edit-btn,
+.reh-action-block:hover .reh-edit-btn,
+.reh-scene:hover .reh-edit-btn,
+.reh-act:hover .reh-edit-btn { opacity: 1; }
+.reh-edit-btn:hover { background: var(--panel); border-color: var(--border); color: var(--accent); }
+
+/* Scene heading on the A4 page */
+.reh-scene {
+ font-family: 'Courier New', Courier, monospace; font-size: 11.5px; font-weight: 700;
+ text-transform: uppercase; letter-spacing: .07em; color: #1a1a2e;
+ margin: 22px 0 8px; padding: 5px 0;
+ border-top: 1.5px solid #333; border-bottom: 1.5px solid #333;
+ cursor: pointer; display: flex; align-items: center; gap: 6px;
+}
+.reh-scene .mdi { font-size: 11px; opacity: .5; flex-shrink: 0; }
+
+/* Action / description block — 1em so narrator matches spoken dialog and
+ scales with the +A / -A control. */
+.reh-action-block {
+ font-family: 'Courier New', Courier, monospace; font-size: 1em; line-height: 1.75;
+ color: #2c2c3e; padding: 2px 0 10px; cursor: pointer;
+}
+
+/* Transition line */
+.reh-transition {
+ font-family: 'Courier New', Courier, monospace; font-size: 0.75em; font-weight: 700;
+ text-align: right; text-transform: uppercase; letter-spacing: .1em;
+ color: #555; padding: 8px 0;
+}
+
+/* Active line highlight (works on any block type) */
+.reh-line-active.reh-block { background: rgba(37,99,235,.08) !important; border-left-color: var(--accent) !important; }
+.reh-line-active.reh-scene { background: rgba(37,99,235,.06) !important; outline: 2px solid var(--accent); outline-offset: 1px; border-radius: 3px; }
+.reh-line-active.reh-action-block { background: rgba(37,99,235,.05) !important; padding-left: 6px; border-left: 2px solid var(--accent); }
+.reh-line-active.reh-direction { background: rgba(37,99,235,.04) !important; }
+
+/* Emotion selector on TTS dialog blocks */
+.reh-emotion-sel {
+ font-size: 10px; border: 1px solid rgba(0,0,0,.18); border-radius: 3px;
+ padding: 1px 4px; background: rgba(0,0,0,.04); color: #555;
+ cursor: pointer; margin-left: auto; flex-shrink: 0; font-family: sans-serif;
+ max-width: 90px;
+}
+.reh-emotion-sel:hover, .reh-emotion-sel:focus { border-color: var(--accent); outline: none; }
+.reh-emotion-sel.has-emotion { background: rgba(37,99,235,.08); color: var(--accent); border-color: var(--accent); font-weight: 600; }
+
+/* Pre-synthesized dot */
+.reh-synth-dot {
+ width: 7px; height: 7px; border-radius: 50%; background: var(--green);
+ flex-shrink: 0; display: inline-block; margin-left: 3px;
+}
+.reh-synth-dot.stale {
+ background: var(--red, #ef4444);
+ box-shadow: 0 0 0 2px rgba(239,68,68,.2);
+}
+
+/* Per-line re-synth button */
+.reh-resynth-btn {
+ background: none; border: none; cursor: pointer; padding: 2px 4px;
+ color: var(--red, #ef4444); font-size: 12px; border-radius: 4px;
+ opacity: .85; transition: opacity .15s, background .15s;
+ display: inline-flex; align-items: center;
+}
+.reh-resynth-btn:hover { opacity: 1; background: rgba(239,68,68,.1); }
+
+/* Note area — right gutter of dialog block (holds edit btn + note btn) */
+.reh-block { position: relative; }
+.reh-note-area {
+ display: flex; flex-direction: column; align-items: flex-end; gap: 2px;
+ min-width: 28px; flex-shrink: 0; padding-top: 2px;
+}
+/* Edit button inside note-area gutter */
+.reh-note-area .reh-edit-btn {
+ opacity: 0; transition: opacity .12s; border: 1px solid transparent;
+ background: none; cursor: pointer; font-size: 13px; color: var(--subtext);
+ padding: 3px 5px; border-radius: 4px; line-height: 1; flex-shrink: 0;
+}
+.reh-block:hover .reh-note-area .reh-edit-btn { opacity: 1; }
+.reh-note-area .reh-edit-btn:hover { background: var(--panel); border-color: var(--border); color: var(--accent); }
+.reh-note-btn {
+ background: none; border: none; cursor: pointer; padding: 3px;
+ font-size: 14px; color: var(--subtext); opacity: .45; border-radius: 4px;
+ transition: opacity .15s, color .15s; line-height: 1; flex-shrink: 0;
+}
+.reh-note-btn:hover, .reh-note-btn.has-note { opacity: 1; color: #f59e0b; }
+.reh-note-ta {
+ display: none; width: 160px; min-height: 60px; resize: vertical;
+ font-size: 11px; font-family: inherit; line-height: 1.45;
+ padding: 6px 8px; border: 1px solid #f59e0b55; border-radius: 6px;
+ background: rgba(245,158,11,.06); color: var(--text); outline: none;
+}
+.reh-note-ta.open { display: block; }
+.reh-note-ta:focus { border-color: #f59e0b; background: rgba(245,158,11,.1); }
+
+@media (max-width: 700px) {
+ .reh-input-row { grid-template-columns: 1fr; }
+ .reh-a4-page { padding: 28px 20px; }
+ .reh-console { gap: 8px; }
+ .reh-cc-grid { grid-template-columns: 1fr; }
+}
+
+/* ── Phase sub-tabs ─────────────────────────────────────────────────────── */
+.reh-subtabs {
+ display: flex; gap: 4px; padding: 4px; margin: 0 0 14px;
+ background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
+ flex-wrap: wrap;
+}
+.reh-subtab {
+ display: inline-flex; align-items: center; gap: 6px;
+ padding: 7px 14px; border: none; border-radius: 7px; cursor: pointer;
+ background: transparent; color: var(--subtext); font-size: 13px; font-weight: 600;
+ transition: background .15s, color .15s;
+}
+.reh-subtab .mdi { font-size: 16px; }
+.reh-subtab:hover:not(:disabled):not(.active) { background: var(--border); color: var(--text); }
+.reh-subtab.active { background: var(--surface); color: var(--accent); box-shadow: 0 1px 3px rgba(0,0,0,.08); }
+.reh-subtab:disabled { opacity: .4; cursor: not-allowed; }
+@media (max-width: 600px) {
+ .reh-subtab span:not(.mdi) { display: none; }
+ .reh-subtab { padding: 8px 12px; }
+}
+
+/* ── Studio phase tabs (deliberately separate classes from .reh-subtab —
+ rehearser.js wires its OWN click listeners onto every .reh-subtab in the
+ document via querySelectorAll, and every section's HTML is present in the
+ DOM at once regardless of which is visible, so reusing that class name
+ here would silently attach Rehearser's phase-switching logic to Studio's
+ own tab buttons too) ───────────────────────────────────────────────── */
+.stu-subtabs {
+ display: flex; gap: 4px; padding: 4px; margin: 0 0 14px;
+ background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
+ flex-wrap: wrap;
+}
+.stu-subtab {
+ display: inline-flex; align-items: center; gap: 6px;
+ padding: 7px 14px; border: none; border-radius: 7px; cursor: pointer;
+ background: transparent; color: var(--subtext); font-size: 13px; font-weight: 600;
+ transition: background .15s, color .15s;
+}
+.stu-subtab .mdi { font-size: 16px; }
+.stu-subtab:hover:not(:disabled):not(.active) { background: var(--border); color: var(--text); }
+.stu-subtab.active { background: var(--surface); color: var(--accent); box-shadow: 0 1px 3px rgba(0,0,0,.08); }
+.stu-subtab:disabled { opacity: .4; cursor: not-allowed; }
+.stu-subtab-num {
+ display: inline-flex; align-items: center; justify-content: center;
+ width: 16px; height: 16px; border-radius: 50%; font-size: 10px; font-weight: 700;
+ background: var(--border); color: var(--subtext); flex-shrink: 0;
+}
+.stu-subtab.active .stu-subtab-num { background: var(--accent); color: #fff; }
+@media (max-width: 600px) {
+ .stu-subtab span:not(.mdi):not(.stu-subtab-num) { display: none; }
+ .stu-subtab { padding: 8px 12px; }
+}
+
+/* Nested sub-tabs inside Studio's Characters/Perform phases (smaller, inline) */
+.stu-inner-tabs { display: flex; gap: 4px; margin: 0 0 12px; }
+.stu-book-context-btn {
+ display: inline-flex; align-items: center; gap: 6px; margin-right: 4px;
+ padding: 6px 12px; border: 1px solid var(--border); border-radius: 6px;
+ cursor: pointer; background: var(--panel); color: var(--subtext);
+ font-size: 12.5px; font-weight: 600;
+}
+.stu-book-context-btn:hover { color: var(--accent); border-color: var(--accent); background: var(--surface); }
+.stu-inner-tab {
+ padding: 6px 12px; border: 1px solid var(--border); border-radius: 6px; cursor: pointer;
+ background: var(--panel); color: var(--subtext); font-size: 12.5px; font-weight: 600;
+}
+.stu-inner-tab.active { background: var(--surface); color: var(--accent); border-color: var(--accent); }
+.stu-mode-toggle {
+ display: inline-flex; align-items: center; gap: 8px; padding: 8px 12px;
+ border: 1px solid var(--border); border-radius: 8px; background: var(--panel);
+ font-size: 13px; font-weight: 600; margin: 0 0 14px; cursor: pointer;
+}
+
+/* ── Auto-design voices (Phase 2) ───────────────────────────────────────── */
+.reh-autodesign {
+ border: 1px solid var(--border); border-radius: 10px;
+ padding: 12px 14px; margin: 4px 0 14px;
+ background: linear-gradient(180deg, rgba(37,99,235,.04), transparent);
+}
+.reh-autodesign-head { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; font-size: 13px; }
+.reh-autodesign-head .note { font-weight: 400; }
+.reh-autodesign-row { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; }
+.reh-autodesign-row input, .reh-autodesign-row select {
+ width: 100%; padding: 6px 8px; border: 1px solid var(--border);
+ border-radius: 5px; background: var(--surface); color: var(--text); font-size: 12px;
+}
+.reh-llm-refresh { border: none; background: none; cursor: pointer; color: var(--accent); font-size: 13px; padding: 0 2px; }
+.reh-autodesign-progress { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
+
+/* Three casting options (local match / online match / design) */
+.reh-cast-options { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 10px; margin-top: 12px; }
+.reh-cast-opt { display: flex; gap: 10px; padding: 12px; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; }
+.reh-co-num { flex-shrink: 0; width: 22px; height: 22px; border-radius: 50%; background: var(--accent); color: #fff; font-size: 12px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; }
+.reh-co-body { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
+.reh-co-body > strong { font-size: 13px; display: flex; align-items: center; gap: 6px; }
+.reh-co-body > strong .mdi { color: var(--accent); }
+.reh-co-body > p { font-size: 11.5px; color: var(--subtext); line-height: 1.45; margin: 0; }
+.reh-co-body > p em { font-style: normal; color: var(--text); font-weight: 600; }
+.reh-co-body > button { align-self: flex-start; margin-top: 2px; white-space: nowrap; }
+
+/* Per-character status pill while designing */
+.reh-cast-designing { opacity: .75; }
+.reh-cast-design-badge {
+ font-size: 10px; padding: 1px 7px; border-radius: 10px; margin-left: 6px;
+ background: rgba(37,99,235,.12); color: var(--accent); font-weight: 600;
+}
+.reh-cast-design-badge.done { background: rgba(22,163,74,.15); color: var(--green); }
+.reh-cast-design-badge.err { background: rgba(220,38,38,.15); color: var(--red); }
+
+/* ── Per-character auto-design detail panel ─────────────────────────────── */
+.reh-cast-design-panel {
+ margin: -4px 12px 8px 12px; padding: 10px 12px;
+ border: 1px solid rgba(37,99,235,.25); border-radius: 8px;
+ background: rgba(37,99,235,.05); display: flex; flex-direction: column; gap: 7px;
+ animation: reh-cdp-in .15s ease-out;
+}
+@keyframes reh-cdp-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
+.reh-cast-design-panel.done {
+ border-color: rgba(22,163,74,.25); background: rgba(22,163,74,.04);
+}
+.reh-cdp-meta { display: flex; flex-wrap: wrap; gap: 5px; align-items: center; }
+.reh-cdp-chip {
+ font-size: 10px; font-weight: 600; padding: 2px 8px; border-radius: 10px;
+ background: var(--panel); border: 1px solid var(--border); color: var(--subtext);
+ display: inline-flex; align-items: center; gap: 4px; white-space: nowrap;
+}
+.reh-cdp-gender { color: #8b5cf6; border-color: rgba(139,92,246,.3); background: rgba(139,92,246,.07); }
+.reh-cdp-lang { color: #0ea5e9; border-color: rgba(14,165,233,.3); background: rgba(14,165,233,.07); }
+.reh-cdp-name { color: var(--accent); border-color: rgba(37,99,235,.3); background: rgba(37,99,235,.07); font-family: monospace; font-size: 10px; }
+.reh-cdp-desc {
+ font-size: 11px; line-height: 1.5; color: var(--text); font-style: italic;
+ border-left: 2px solid var(--accent); padding-left: 8px; opacity: .85;
+}
+.reh-cdp-step {
+ display: flex; align-items: center; gap: 7px; font-size: 11px; color: var(--subtext);
+}
+.reh-cdp-step .reh-imsdb-spinner { border-top-color: var(--accent); }
+
+/* ── Drag & drop importer ───────────────────────────────────────────────── */
+.reh-dropzone {
+ margin-top: 14px; padding: 26px 16px; border: 2px dashed var(--border);
+ border-radius: 12px; text-align: center; color: var(--subtext);
+ display: flex; flex-direction: column; align-items: center; gap: 5px;
+ transition: border-color .15s, background .15s; cursor: default;
+}
+.reh-dropzone-icon { font-size: 34px; opacity: .5; }
+.reh-dropzone.dragover {
+ border-color: var(--accent); background: rgba(37,99,235,.06); color: var(--accent);
+}
+.reh-dropzone.dragover .reh-dropzone-icon { opacity: 1; }
+
+/* ── Bookshelf library ──────────────────────────────────────────────────── */
+.reh-bookshelf {
+ display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
+ gap: 18px 16px; padding: 6px 4px 4px;
+}
+.sk { background: linear-gradient(90deg, var(--panel), rgba(37,99,235,.08), var(--panel)); background-size: 220% 100%; animation: skeletonSweep 1.15s ease-in-out infinite; }
+.lib-book-sk { background: linear-gradient(135deg, var(--panel), var(--border)) !important; opacity: .7; animation: none; cursor: default; }
+.lib-book-sk .sk { border-radius: 4px; }
+.reh-book {
+ position: relative; cursor: pointer; border-radius: 4px 7px 7px 4px;
+ aspect-ratio: 2 / 3; padding: 14px 12px 12px 18px; color: #fff; overflow: hidden;
+ box-shadow: 0 5px 14px rgba(0,0,0,.22), inset 3px 0 0 rgba(0,0,0,.18);
+ display: flex; flex-direction: column;
+ transition: transform .16s ease, box-shadow .16s ease;
+ background: linear-gradient(135deg, var(--bk1, #4f46e5), var(--bk2, #7c3aed));
+}
+.reh-book::before { /* spine highlight */
+ content: ''; position: absolute; left: 6px; top: 0; bottom: 0; width: 2px;
+ background: rgba(255,255,255,.25);
+}
+.reh-book:hover { transform: translateY(-5px) rotate(-.6deg); box-shadow: 0 12px 26px rgba(0,0,0,.3), inset 3px 0 0 rgba(0,0,0,.18); }
+.reh-book-title {
+ font-size: 14px; font-weight: 800; line-height: 1.25; letter-spacing: .01em;
+ text-shadow: 0 1px 3px rgba(0,0,0,.35); display: -webkit-box;
+ -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
+}
+.reh-book-meta { margin-top: auto; font-size: 10.5px; opacity: .9; line-height: 1.5; }
+.reh-book-avatars { display: flex; gap: 3px; margin-bottom: 6px; flex-wrap: wrap; }
+.reh-book-avatars span {
+ width: 19px; height: 19px; border-radius: 50%; font-size: 9px; font-weight: 700;
+ display: inline-flex; align-items: center; justify-content: center;
+ border: 1.5px solid rgba(255,255,255,.7); color: #fff;
+}
+.reh-book-progress { height: 3px; border-radius: 2px; background: rgba(255,255,255,.28); margin-top: 5px; overflow: hidden; }
+.reh-book-progress > div { height: 100%; background: rgba(255,255,255,.9); }
+.reh-book-actions {
+ position: absolute; top: 6px; right: 6px; display: flex; gap: 3px; opacity: 0;
+ transition: opacity .14s;
+}
+.reh-book:hover .reh-book-actions { opacity: 1; }
+.reh-book-act {
+ width: 22px; height: 22px; border-radius: 5px; border: none; cursor: pointer;
+ background: rgba(0,0,0,.32); color: #fff; font-size: 12px;
+ display: inline-flex; align-items: center; justify-content: center;
+}
+.reh-book-act:hover { background: rgba(0,0,0,.55); }
+.reh-book-active-tag {
+ position: absolute; top: 8px; left: 14px; font-size: 8.5px; font-weight: 700;
+ text-transform: uppercase; letter-spacing: .08em; background: rgba(255,255,255,.92);
+ color: #1a1a2e; padding: 1px 6px; border-radius: 8px;
+}
+/* List-view fallback reuses old rows */
+.reh-bookshelf.list-view { display: flex; flex-direction: column; gap: 8px; }
+.reh-bookshelf.list-view .reh-book {
+ aspect-ratio: auto; flex-direction: row; align-items: center; gap: 12px;
+ padding: 10px 14px; color: var(--text); background: var(--panel) !important;
+ box-shadow: none; border: 1px solid var(--border); border-radius: 8px;
+}
+.reh-bookshelf.list-view .reh-book:hover { transform: none; }
+.reh-bookshelf.list-view .reh-book::before { display: none; }
+.reh-bookshelf.list-view .reh-book-title { -webkit-line-clamp: 1; text-shadow: none; font-size: 14px; }
+.reh-bookshelf.list-view .reh-book-meta { margin: 0 0 0 auto; }
+.reh-bookshelf.list-view .reh-book-actions { position: static; opacity: 1; }
+.reh-bookshelf.list-view .reh-book-act { background: var(--surface); color: var(--text); border: 1px solid var(--border); }
+
+/* ── Multi-page (paginated) script view ─────────────────────────────────── */
+.reh-page-wrap.paginated { display: flex; flex-direction: column; align-items: center; gap: 22px; }
+.reh-paper {
+ width: min(1180px, 96%); background: #fff; color: #1a1a2e;
+ padding: 56px 72px 40px; box-shadow: 0 4px 24px rgba(0,0,0,.18);
+ font-family: 'Courier New', Courier, monospace; font-size: calc(12pt * var(--reh-stage-scale, 1)); line-height: 1.6;
+ border-radius: 2px; position: relative; box-sizing: border-box;
+ min-height: min(1123px, calc((100vw - 32px) * 1.414));
+}
+.reh-paper-num {
+ position: absolute; top: 20px; right: 32px; font-size: 10pt; color: #888;
+ font-family: 'Courier New', monospace;
+}
+[data-theme="dark"] .reh-paper { background: #f8f8f2; }
+
+/* Keep script-browser list rows readable even when old cached list styles collapse. */
+.reh-imsdb-grid.list-view > .reh-imsdb-card {
+ height: 48px !important;
+ min-height: 48px !important;
+ max-height: 48px;
+ overflow: hidden;
+}
+.reh-imsdb-grid.list-view > .reh-imsdb-card .reh-imsdb-meta {
+ height: 48px;
+ min-height: 48px;
+}
+
+
+.reh-bulk-hint { font-size:11px; color:var(--subtext); margin-right:4px; }
+.reh-bulk-mode .reh-selected {
+ background: rgba(37,99,235,.11) !important;
+ outline: 2px solid rgba(37,99,235,.35);
+ outline-offset: -2px;
+}
+.reh-bulk-mode .reh-selected .reh-block-dialog { font-weight:600; }
+
+/* Keep script-browser thumbnail cards tall enough to click/download in cover view. */
+.reh-imsdb-grid:not(.list-view) > .reh-imsdb-card {
+ display: flex !important;
+ flex-direction: column !important;
+ min-height: 230px !important;
+ height: auto !important;
+ overflow: hidden;
+}
+.reh-imsdb-grid:not(.list-view) > .reh-imsdb-card .reh-imsdb-poster-wrap {
+ display: block !important;
+ position: relative !important;
+ width: 100% !important;
+ aspect-ratio: 2 / 3 !important;
+ min-height: 178px !important;
+ height: auto !important;
+ padding-bottom: 0 !important;
+ flex: 0 0 auto !important;
+}
+.reh-imsdb-grid:not(.list-view) > .reh-imsdb-card .reh-imsdb-poster,
+.reh-imsdb-grid:not(.list-view) > .reh-imsdb-card .reh-imsdb-fallback {
+ position: absolute !important;
+ inset: 0 !important;
+ width: 100% !important;
+ height: 100% !important;
+}
+.reh-imsdb-grid:not(.list-view) > .reh-imsdb-card .reh-imsdb-meta {
+ min-height: 46px;
+ flex: 0 0 auto;
+}
diff --git a/static/index.html b/static/index.html
index aafa52a..32d05d8 100644
--- a/static/index.html
+++ b/static/index.html
@@ -10,7 +10,7 @@
-
+
@@ -27,10 +27,11 @@
-
-
-
-
+
+
+
+
+
@@ -395,7 +396,7 @@ window.toggleNavTree = function(treeId, chevronId) {
-
+