/* ============================================================
   Scrived — UI styles
   Inspired by classic book-design aesthetics: warm cream paper,
   serif typography, restrained accent color, three-pane editor.
   ============================================================ */

:root {
  --bg-page:        #f5f3ee;
  --bg-card:        #ffffff;
  --bg-paper:       #fdfbf6;
  --bg-rail:        #efece4;
  --bg-rail-hover:  #e6e2d6;
  --bg-topbar:      #1d1b18;
  --bg-topbar-soft: #2a2723;
  /* Secondary paper tint for small chips (series toggle/count). Was
     referenced with literal-light fallbacks long before it was defined,
     so those chips never darkened — defining it here fixes them all. */
  --paper-2:        #f3efe6;

  --ink-1:    #1a1a1a;
  --ink-2:    #3a3733;
  --ink-3:    #6a665e;
  --ink-4:    #98948a;
  --ink-5:    #c8c3b6;

  --line:     #e2dcd0;
  --line-soft:#ece8dc;
  --line-strong:#d4cdba;
  /* Alias used across marketing.css (blog, articles, landing tables). It was
     referenced 20+ times but never defined, so it always fell back to the
     light #e2dcd0 — borders never darkened in dark mode. Aliasing it to
     --line makes every one of those borders theme-aware in a single place
     (same value in light mode, so nothing changes there). */
  --line-1:   var(--line);

  --accent:        #8b6914;   /* old gold */
  --accent-soft:   #b58a2f;
  --accent-bg:     #f6efdb;
  --danger:        #a93b2a;
  --success:       #4d7a2e;

  --font-sans:  'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-serif: 'Lora', Georgia, 'Times New Roman', serif;
  --font-display: 'Playfair Display', Georgia, serif;

  --radius-sm: 4px;
  --radius:    8px;
  --radius-lg: 12px;

  --shadow-1: 0 1px 2px rgba(0,0,0,.04), 0 2px 8px rgba(0,0,0,.04);
  --shadow-2: 0 2px 6px rgba(0,0,0,.06), 0 12px 36px rgba(0,0,0,.08);
  --shadow-paper: 0 1px 0 rgba(0,0,0,.02), 0 12px 28px rgba(60,52,32,.08);

  --topbar-h: 56px;
}

/* ============================================================
   Dark theme — applied via [data-theme="dark"] on <html>.
   Set before render by an inline script in layout.php so there's
   no flash of light theme on cold loads.
   Only the editor chrome + writing-view manuscript darken; the
   preview / print / EPUB views still use the book's saved theme
   colors (via --pv-* vars), so WYSIWYG stays accurate.
   ============================================================ */
:root[data-theme="dark"] {
  --bg-page:        #1c1a17;
  --bg-card:        #26221d;
  --bg-paper:       #26221d;
  --bg-rail:        #1a1815;
  --bg-rail-hover:  #2a2722;
  --bg-topbar:      #131210;
  --bg-topbar-soft: #1d1b18;
  --paper-2:        #2d2823;

  --ink-1: #ebe6d8;
  --ink-2: #c8c2b3;
  --ink-3: #968f80;
  --ink-4: #6c655a;
  --ink-5: #45403a;

  --line:        #3a342c;
  --line-soft:   #2d2823;
  --line-strong: #4d4538;

  --accent:      #c8a042;
  --accent-soft: #d4a64f;
  --accent-bg:   #322816;
  --danger:      #d36456;
  --success:     #84b66a;

  --shadow-1: 0 1px 2px rgba(0,0,0,.35), 0 2px 8px rgba(0,0,0,.25);
  --shadow-2: 0 2px 6px rgba(0,0,0,.4),  0 12px 36px rgba(0,0,0,.5);
  --shadow-paper: 0 1px 0 rgba(0,0,0,.25), 0 12px 28px rgba(0,0,0,.4);
}

/* A handful of literal-colored values can't ride the var system
   directly (they're either inline styles set by JS or were typed as
   literal hex codes). Override them per-component in dark mode. */
:root[data-theme="dark"] body { background: var(--bg-page); }
:root[data-theme="dark"] .editor-topbar { background: var(--bg-topbar); }
:root[data-theme="dark"] .topbar-iconbtn:hover { background: rgba(255,255,255,.06); }
:root[data-theme="dark"] .ql-toolbar.ql-snow {
  background: var(--bg-paper);
  border-color: var(--line);
}
:root[data-theme="dark"] .ql-toolbar.ql-snow .ql-stroke { stroke: var(--ink-2); }
:root[data-theme="dark"] .ql-toolbar.ql-snow .ql-fill   { fill:   var(--ink-2); }
:root[data-theme="dark"] .ql-toolbar.ql-snow .ql-picker-label { color: var(--ink-2); }
:root[data-theme="dark"] .ql-toolbar.ql-snow button:hover .ql-stroke,
:root[data-theme="dark"] .ql-toolbar.ql-snow button.ql-active .ql-stroke { stroke: var(--accent); }
:root[data-theme="dark"] .ql-toolbar.ql-snow button:hover .ql-fill,
:root[data-theme="dark"] .ql-toolbar.ql-snow button.ql-active .ql-fill   { fill:   var(--accent); }
:root[data-theme="dark"] .ql-snow .ql-picker-options {
  background: var(--bg-card);
  border-color: var(--line);
  color: var(--ink-1);
}
:root[data-theme="dark"] .ql-editor blockquote { color: var(--ink-2); }
:root[data-theme="dark"] .ql-editor::before { color: var(--ink-4); } /* placeholder */
:root[data-theme="dark"] input,
:root[data-theme="dark"] textarea,
:root[data-theme="dark"] select {
  background: var(--bg-paper);
  color: var(--ink-1);
  border-color: var(--line);
}
:root[data-theme="dark"] input::placeholder,
:root[data-theme="dark"] textarea::placeholder { color: var(--ink-4); }
:root[data-theme="dark"] input[type=checkbox],
:root[data-theme="dark"] input[type=radio] { accent-color: var(--accent); }

/* Modal / panel backgrounds were hardcoded to white in places. */
:root[data-theme="dark"] .preset-pop,
:root[data-theme="dark"] .timer-popover,
:root[data-theme="dark"] .tts-popover,
:root[data-theme="dark"] .prose-panel,
:root[data-theme="dark"] .bible-mention-popover,
:root[data-theme="dark"] .res-link-composer,
:root[data-theme="dark"] .chapter-settings-popover,
:root[data-theme="dark"] .resource-detail,
:root[data-theme="dark"] .bible-form,
:root[data-theme="dark"] .corkboard-card,
:root[data-theme="dark"] .diff-chapter,
:root[data-theme="dark"] .bible-sidebar,
:root[data-theme="dark"] .resources-sidebar {
  background: var(--bg-card);
  color: var(--ink-1);
}
/* The shared reset above sets `background` (the color) but the corkboard
   card's light paper gradient is a background-IMAGE and survived it,
   painting a white wash over dark cards. Kill it explicitly — standalone
   rule, since other members of the list may legitimately carry images. */
:root[data-theme="dark"] .corkboard-card { background-image: none; }

/* Diff page row colors: tweak the green/red highlights so they read on dark. */
:root[data-theme="dark"] .diff-row-add { background: rgba(80, 160, 80, 0.12); }
:root[data-theme="dark"] .diff-row-del { background: rgba(200, 70, 70, 0.12); }
:root[data-theme="dark"] .diff-row-del .diff-row-text { color: #d49591; }
:root[data-theme="dark"] .diff-row-mod { background: rgba(200, 160, 60, 0.10); }

/* Prose-check and find-match highlights stay legible. */
:root[data-theme="dark"] ::highlight(prose-weasel) { background-color: rgba(200,160,60,.25); }
:root[data-theme="dark"] ::highlight(prose-wordy)  { background-color: rgba(90,130,210,.22); }
:root[data-theme="dark"] ::highlight(prose-repeat) { background-color: rgba(200,80,80,.22); }
:root[data-theme="dark"] ::highlight(prose-flash)  { background-color: rgba(70,135,255,.85); color: #fff; }
:root[data-theme="dark"] ::highlight(find-match)   { background-color: rgba(255,200,80,.30); }
:root[data-theme="dark"] ::highlight(find-current) { background-color: rgba(255,160,60,.55); }

/* TOC dotted leader and page-sheet borders use ink colors that need
   to stay on the dark page. The fallbacks via CSS vars cover most. */
:root[data-theme="dark"] .save-status { color: var(--ink-3); }

/* Library cards + admin pages use the same vars, so they pick up
   dark mode automatically. Cover fallbacks need a lighter tint. */
:root[data-theme="dark"] .cover-fallback { background: var(--bg-rail); color: var(--ink-2); }

*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; }

/* The browser's default `[hidden] { display: none }` rule loses to any
   class-based display rule (e.g. `.field { display: block }`). Force
   it to win — most "hidden" elements in this app are toggled via the
   HTML `hidden` attribute and we want that to actually work. */
[hidden] { display: none !important; }
body {
  margin: 0;
  background: var(--bg-page);
  color: var(--ink-1);
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-soft); }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 18px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  font-family: var(--font-sans);
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: .01em;
  cursor: pointer;
  /* Theme-safe, not literal white: in dark mode --bg-card is the dark
     card surface, so buttons follow the theme instead of glowing. */
  background: var(--bg-card);
  color: var(--ink-1);
  border-color: var(--line);
  transition: all .15s ease;
  text-decoration: none;
  line-height: 1;
}
.btn:hover { background: var(--bg-rail); border-color: var(--line-strong); }
/* THE dark-button convention: ink-on-page inverted. --ink-1 flips light
   in dark mode, so the text must be --bg-page (not literal #fff), or the
   dark theme renders white-on-cream. */
.btn-primary {
  background: var(--ink-1);
  color: var(--bg-page);
  border-color: var(--ink-1);
}
.btn-primary:hover { background: #000; border-color: #000; color: #fff; }
:root[data-theme="dark"] .btn-primary:hover { background: #fff; border-color: #fff; color: #1c1a17; }
.btn-ghost { background: transparent; border-color: transparent; }
.btn-ghost:hover { background: var(--bg-rail); border-color: var(--line); }
.btn-block { width: 100%; }
.btn-lg { padding: 12px 24px; font-size: 14px; }
.btn-sm { padding: 6px 12px; font-size: 12.5px; }
.btn-link {
  background: none; border: none; padding: 0;
  color: var(--ink-3); font-size: 13px; cursor: pointer;
  font-family: inherit;
}
.btn-link:hover { color: var(--ink-1); }
/* ---------- Form fields ---------- */
.field { display: block; margin-bottom: 14px; }
.field-label {
  display: block;
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-3);
  margin-bottom: 5px;
  letter-spacing: .03em;
  text-transform: uppercase;
}
.field input,
.field select,
.field textarea,
.select {
  width: 100%;
  padding: 9px 11px;
  border: 1px solid var(--line);
  background: var(--bg-paper);
  border-radius: var(--radius-sm);
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--ink-1);
  transition: border-color .12s ease, box-shadow .12s ease;
}
.field input:focus,
.field select:focus,
.field textarea:focus,
.select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(139,105,20,.12);
  /* Theme-safe: this rule loses to nothing later in the sheet, so a
     literal #fff here used to flash light fields on focus in dark mode. */
  background: var(--bg-card);
}
/* Radios and checkboxes inside a .field (e.g. inline pickers) should keep
   their native size — without this they inherit the text-input width:100%. */
.field input[type="radio"],
.field input[type="checkbox"] {
  width: auto;
  padding: 0;
  margin: 0;
  background: transparent;
  border: 0;
  box-shadow: none;
}
.field-toggle {
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; color: var(--ink-2);
  margin: 8px 0;
  cursor: pointer;
}
.field-toggle-tight {
  margin: 2px 0 4px;
  font-size: 12px;
  color: var(--ink-3);
}
.req { color: var(--danger); }

/* Presenter mode indicator (migration 177). Deliberately tiny and
   wordless: it has to be legible to the owner mid-recording without
   becoming a caption in the recording. Hover brings it to full opacity
   and its title attribute explains itself; clicking goes to the switch. */
.presenter-dot {
  position: fixed;
  left: 10px; bottom: 10px;
  z-index: 60;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: #d9a441;
  opacity: .45;
  transition: opacity .15s ease;
}
.presenter-dot:hover,
.presenter-dot:focus-visible { opacity: 1; }
@media print { .presenter-dot { display: none; } }

/* Presenter mode, 'blur' cover style (migration 178). The alternative to
   removing the artwork: the REAL cover stays in the page and is blurred
   here, so a shelf still looks like a shelf on camera without any title
   being legible.

   SCREEN ONLY. Every path that produces a file drops the cover under both
   styles (Presenter::bookForExport) — a filter can't reach an image
   already zipped into an EPUB, a page mPDF has drawn, or a PDF painted
   onto a canvas.

   Radii are ~4-5% of each surface's rendered width, which is past the
   point where a title survives while the composition and colour still
   read as a book cover.

   Never target #cover-preview: book_details.js replaces both the cover
   and the logo <img> after an upload with a fresh element carrying no id
   and no class, so only the container selectors are durable.

   scale(1.06) is the standard companion to a blur on an <img> — the
   filter samples the transparency outside the element box and would
   otherwise fade the outer few pixels. .book-cover, .details-cover,
   .details-logo and .export-cover already clip, so the overscan is
   trimmed; .cvf-thumb gets a box below because it has neither. */
.presenter-blur .book-cover img,
.presenter-blur .details-cover img,
.presenter-blur .details-logo img,
.presenter-blur .export-cover img,
.presenter-blur .cvf-thumb img {
  filter: blur(var(--presenter-blur, 9px));
  transform: scale(1.06);
  transform-origin: 50% 50%;
}
/* Library grid columns are minmax(190px, 1fr), so a card cover renders
   190-240px wide. */
.presenter-blur .book-cover img { --presenter-blur: 9px; }
/* Book details' side column and the editor's export summary render the
   cover close to twice that; the logo is a wordmark and earns as much. */
.presenter-blur .details-cover img,
.presenter-blur .details-logo img,
.presenter-blur .export-cover img { --presenter-blur: 11px; }
/* Workshop -> Cover -> Final files thumbs cap at 120x180. */
.presenter-blur .cvf-thumb img { --presenter-blur: 6px; }
/* .cvf-thumb has no rule of its own — the anchor shrink-wraps the image
   with no clipping — so the blur halo would bleed over the spec checks
   beside it. Give it a box, but only while the mask is up. */
.presenter-blur .cvf-thumb {
  display: inline-block;
  overflow: hidden;
  border-radius: 4px;
}

/* ----- Codex portraits under the mask (v2.69) -----

   Portraits follow the same presenter_cover_style the covers do. Under
   'card' they never reach the page at all — bible.js's projection nulls
   image_path, so the app's OWN imageless fallback renders (the kind emoji
   on a tint of the entry's colour) and there is no new artwork to
   maintain. Under 'blur' they stay put and this is the whole treatment.

   The drawer thumbnails and the form portrait are <img> in a clipping box,
   so they follow the cover idiom above, overscan included.

   THE MAP IS DIFFERENT, and pleasantly so. A CSS filter DOES apply to an
   SVG <image>, and in SVG the filter runs BEFORE clip-path — so the blur
   halo is trimmed by the node's own circle for free. That makes the
   scale(1.06) overscan unnecessary here, which is just as well: a CSS
   transform on a node would fight the translate the layout already puts
   on it. */
.presenter-blur .bible-row-thumb img,
.presenter-blur .bible-form-image img,
.presenter-blur .bible-mention-imgwrap img {
  filter: blur(var(--presenter-blur, 6px));
  transform: scale(1.06);
  transform-origin: 50% 50%;
}
/* A drawer row thumb is ~34px and the hover popover's ~48px; the form
   portrait is several times either, and earns more. */
.presenter-blur .bible-row-thumb img { --presenter-blur: 4px; }
.presenter-blur .bible-mention-imgwrap img { --presenter-blur: 5px; }
.presenter-blur .bible-form-image img { --presenter-blur: 10px; }
/* No transform: see the SVG note above. */
.presenter-blur .map-node image {
  filter: blur(5px);
  transform: none;
}

/* The codex form is read-only while the mask is up. The notice sits with
   the fields rather than above the form, because that is where the author
   is looking when they discover the inputs don't take. */
.bible-mask-notice {
  margin: 6px 0 0;
  padding: 7px 10px;
  border-radius: 6px;
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 26%, transparent);
  color: var(--text-muted, inherit);
  font-family: var(--font-sans);
  font-size: 12px;
  line-height: 1.45;
}

/* Cover-style radios on the presenter card. Same row idiom as
   .admin-toggle, which styles only input[type="checkbox"] — hence the
   scoped block rather than a change to the shared rule. */
.presenter-cover-styles { margin: 4px 0 14px; }
.presenter-cover-styles .admin-toggle { margin: 6px 0; align-items: flex-start; }
.presenter-cover-styles input[type="radio"] {
  width: 18px; height: 18px;
  accent-color: var(--accent);
  margin-top: 1px;
}

.alert {
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  font-size: 13px;
  margin-bottom: 16px;
}
.alert-error {
  background: #fdf0ed;
  color: #8a2c1d;
  border-left: 3px solid var(--danger);
}

.inline-form { display: inline; }

/* ============================================================
   AUTH (login)
   ============================================================ */
.auth-body { background: var(--bg-page); }
.auth-shell {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 20px;
}
.auth-card {
  width: 100%;
  max-width: 400px;
  background: var(--bg-card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  padding: 40px 36px;
  border: 1px solid var(--line);
}
.auth-brand { text-align: center; margin-bottom: 28px; }
.brand-mark {
  font-family: var(--font-display);
  font-size: 32px;
  font-weight: 600;
  margin: 0;
  letter-spacing: .02em;
  color: var(--ink-1);
}
/* The wordmark links back to the homepage — keep the heading look,
   hint at clickability on hover. */
.brand-mark a {
  color: inherit;
  text-decoration: none;
}
.brand-mark a:hover { color: var(--accent); }
.brand-tag {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-3);
  margin: 4px 0 0;
}
.auth-form { margin-top: 8px; }
.auth-form .btn { margin-top: 8px; }
.auth-footer {
  text-align: center;
  font-size: 12px;
  color: var(--ink-4);
  margin: 24px 0 0;
}
/* Trust line under the register submit — same quiet tone as .auth-footer,
   sitting inside the form so it reads as part of the decision moment. */
.auth-trust-line {
  text-align: center;
  font-size: 12px;
  color: var(--ink-4);
  margin: 12px 0 0;
}
/* Buyer-intent note (?plan=lifetime): slightly more present than the
   trust line — it explains what the submit button will do next. */
.auth-plan-note {
  text-align: center;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-3);
  margin: 12px 0 0;
}

/* Show/hide toggle for password inputs. Wrap the input in
   .field-reveal and add a .field-reveal-btn button after it. */
.field-reveal { position: relative; display: block; }
.field-reveal input { padding-right: 40px; }
.field-reveal-btn {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  border: 0;
  background: transparent;
  padding: 4px;
  line-height: 0;
  color: var(--ink-4);
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.field-reveal-btn:hover { color: var(--ink-1); }
.field-reveal-btn .eye-closed { display: none; }
.field-reveal-btn.is-showing .eye-open { display: none; }
.field-reveal-btn.is-showing .eye-closed { display: inline; }

/* ============================================================
   APP shell + topbar
   ============================================================ */
.app-body { background: var(--bg-page); min-height: 100vh; }
.app-shell { min-height: 100vh; display: flex; flex-direction: column; }

.topbar {
  height: var(--topbar-h);
  background: var(--bg-topbar);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 24px;
}
.topbar-left, .topbar-right { display: flex; align-items: center; gap: 14px; }
.brand {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 600;
  color: #fff;
  letter-spacing: .02em;
}
.brand:hover { color: #fff; opacity: .85; }
.topbar .btn-link { color: #c8c3b6; }
.topbar .btn-link:hover { color: #fff; }

/* Library / shell topbar wordmark next to the gold S logo */
.topbar-wordmark {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  color: #fff;
  letter-spacing: .12em;
}
.topbar-signout {
  font-size: 12.5px;
  letter-spacing: .01em;
}

/* ============================================================
   LIBRARY
   ============================================================ */
/* Full-width flex column: the header band bleeds edge-to-edge while
   .lib-band-inner / .lib-shelf reinstate the old 1400px + gutters. */
.library {
  flex: 1;
  display: flex;
  flex-direction: column;
  width: 100%;
}

/* ---- Cinematic header band ----------------------------------------
   Always dark, in BOTH themes (the shelves below follow the theme).
   Stacking contract (a LAW, same as the marketing hero):
     .lib-band-bg   z0  position:fixed artwork — NEVER
                        background-attachment:fixed (iOS ignores it and
                        it forces a repaint every scroll frame)
     .lib-band-inner z1 the header content
     .lib-below      z1 opaque theme surface that scrolls up over the art
   The topbar is lifted to z2 via .library-page (a fixed layer paints
   over unpositioned statics regardless of source order).

   ⚠ Palette: band-scoped --band-* custom properties, deliberately NOT
   an --ink-* flip — .book-cover and .btn-primary use --ink-1 as a
   BACKGROUND, and the homepage hero shipped an invisible button once
   because of exactly that. Nothing here touches the global tokens. */
.library-page .topbar { position: relative; z-index: 2; }
.lib-band {
  position: relative;
  overflow: hidden;
  --band-ink-1: #f3eee1;   /* titles    — keep ≥7:1 on the band */
  --band-ink-2: #d8d0bf;   /* body      — keep ≥4.5:1 */
  --band-ink-3: #b3a993;   /* captions */
  --band-accent: #e8c882;  /* lifted gold, matches the marketing hero */
  --band-glass: rgba(255,255,255,.09);
  --band-glass-line: rgba(255,255,255,.22);
  /* Built-in "study at dusk" — lamplight upper right, warm shadowed
     floor lower left. This is the whole look when no artwork is
     uploaded, and the paint-behind while an uploaded image decodes. */
  background:
    radial-gradient(85% 130% at 88% 0%,  rgba(200,160,66,.18) 0%, rgba(200,160,66,0) 55%),
    radial-gradient(120% 90% at 8% 110%, rgba(70,52,30,.5)    0%, rgba(70,52,30,0)   60%),
    linear-gradient(175deg, #2b251d 0%, #1e1a15 55%, #15120e 100%);
  min-height: clamp(240px, 36vh, 400px);
  display: flex;
  align-items: flex-end;
}
.lib-band-inner {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
  padding: 48px 48px 36px;
}
.lib-band-bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  background: #15120e;
  pointer-events: none;
}
/* Only the top ~40-50% of the frame is ever revealed by the band, so
   pin the crop to the top of the artwork. */
.lib-band-bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  display: block;
}
/* Scrim over uploaded art — an accessibility requirement, not styling
   (target ≥7:1 for the title, ≥4.5:1 for the sub/stats). Weighted LEFT
   because that's where the title and stats sit. If a future artwork
   misses those ratios, DEEPEN THIS — never shrink or lighten the type. */
.lib-band-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(90deg,  rgba(14,11,8,.80) 0%, rgba(14,11,8,.56) 48%, rgba(14,11,8,.42) 100%),
    linear-gradient(180deg, rgba(14,11,8,.42) 0%, rgba(14,11,8,.22) 45%, rgba(14,11,8,.62) 100%);
}
/* The opaque surface the shelves live on. flex:1 so a two-book library
   still covers the viewport — fixed art must never peek out below. */
.lib-below {
  position: relative;
  z-index: 1;
  flex: 1;
  background: var(--bg-page);
}
.lib-shelf {
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
  padding: 40px 48px 80px;
}

/* Band text + chips */
.lib-band .library-title {
  color: var(--band-ink-1);
  text-shadow: 0 2px 18px rgba(0,0,0,.55);
}
.lib-band .library-sub {
  color: var(--band-ink-2);
  text-shadow: 0 1px 10px rgba(0,0,0,.5);
}
.lib-band .library-sub strong { color: var(--band-ink-1); }
.lib-band .library-stat {
  background: var(--band-glass);
  border-color: var(--band-glass-line);
  color: var(--band-ink-2);
}
.lib-band .library-stat-value { color: var(--band-ink-1); }
.lib-band .library-stat-label { color: var(--band-ink-3); }

/* Band controls — pinned literals on purpose: the band is dark in both
   themes, so these must not ride tokens that flip. Hover rules carry an
   explicit dark-theme twin where a later dark override would win. */
.lib-band .btn {
  background: rgba(255,255,255,.10);
  border-color: rgba(255,255,255,.24);
  color: #f3eee1;
}
.lib-band .btn:hover {
  background: rgba(255,255,255,.18);
  border-color: rgba(255,255,255,.38);
  color: #fff;
}
.lib-band .btn-primary {
  background: #f3eee1;
  border-color: #f3eee1;
  color: #1a1613;
  box-shadow: 0 8px 24px rgba(0,0,0,.4);
}
.lib-band .btn-primary:hover,
:root[data-theme="dark"] .lib-band .btn-primary:hover { /* beats the global dark hover */
  background: #fff;
  border-color: #fff;
  color: #000;
}
/* Three classes deep so these also beat `:root[data-theme="dark"] input/select`. */
.lib-band .library-actions .library-search-input,
.lib-band .library-actions .select {
  background: rgba(255,255,255,.10);
  border-color: rgba(255,255,255,.24);
  color: #f3eee1;
}
.lib-band .library-actions .library-search-input::placeholder { color: rgba(243,238,225,.55); }
.lib-band .library-actions .library-search-input:focus,
.lib-band .library-actions .select:focus {
  background: rgba(255,255,255,.16);
  border-color: var(--band-accent);
  box-shadow: 0 0 0 3px rgba(232,200,130,.28);
}
/* The <select>'s dropdown popup is native-rendered on a light sheet. */
.lib-band .library-actions .select option { color: #1a1a1a; background: #fff; }
.lib-band .btn:focus-visible,
.lib-band .library-actions .library-search-input:focus-visible,
.lib-band .library-actions .select:focus-visible {
  outline: 2px solid #f6e6bd;
  outline-offset: 2px;
  box-shadow: 0 0 0 5px rgba(0,0,0,.45);
}

.library-hero {
  display: flex;
  align-items: end;
  justify-content: space-between;
  margin-bottom: 0; /* the band's padding owns the spacing now */
  gap: 16px;
}
.library-title {
  font-family: var(--font-display);
  font-size: 36px;
  font-weight: 600;
  margin: 0 0 4px;
  letter-spacing: .01em;
}
.library-sub {
  margin: 0;
  color: var(--ink-3);
  font-size: 14px;
}
.library-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; justify-content: flex-end; }
.library-search-input {
  width: 220px;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 13.5px;
  color: var(--ink-1);
  background: var(--bg-paper);
  transition: border-color .12s, box-shadow .12s, background .12s;
}
.library-search-input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg-card);
  box-shadow: 0 0 0 3px rgba(139,105,20,.12);
}

/* ---- Cross-book search results panel ---- */
.library-search-results {
  margin: 0 0 32px;
}
.library-search-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}
.library-search-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 500;
  color: var(--ink-1);
}
.library-search-empty {
  padding: 18px 0;
  color: var(--ink-3);
  font-style: italic;
}
.library-search-body {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.lsr-book {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px 20px 14px;
}
.lsr-book-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
  border-bottom: 1px solid var(--line-soft);
  padding-bottom: 8px;
}
.lsr-book-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 500;
  color: var(--ink-1);
}
.lsr-book-meta {
  font-size: 12px;
  color: var(--ink-3);
  letter-spacing: .02em;
}
.lsr-chapters {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.lsr-chapter {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 4px 10px;
}
.lsr-chapter-title {
  background: transparent;
  border: none;
  padding: 0;
  font: inherit;
  font-size: 13.5px;
  color: var(--accent);
  cursor: pointer;
  text-align: left;
  font-weight: 500;
  text-decoration: none;
}
.lsr-chapter-title:hover { text-decoration: underline; }
.lsr-chapter-count {
  font-size: 11px;
  color: var(--ink-3);
  background: var(--bg-rail);
  padding: 1px 8px;
  border-radius: 999px;
  align-self: start;
  font-variant-numeric: tabular-nums;
}
.lsr-snippets {
  list-style: none;
  margin: 4px 0 0;
  padding: 0;
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.lsr-snippet {
  font-family: var(--font-serif, Georgia, serif);
  font-size: 13px;
  line-height: 1.55;
  color: var(--ink-2);
}
.lsr-snippet mark {
  background: rgba(255,235,170,.7);
  color: var(--ink-1);
  padding: 0 2px;
  border-radius: 2px;
  font-weight: 500;
}
.lsr-ctx { color: var(--ink-3); }
.lsr-truncated {
  margin: 4px 0 0;
  font-size: 11.5px;
  color: var(--ink-3);
  font-style: italic;
  grid-column: 1 / -1;
}

/* Streak + goal stats. Four pills under the "13 books · 868,884 words"
   line — populated client-side from /api/writing-sessions/stats. The
   .is-empty state mutes them so a brand new user (or one between
   sessions) sees a calmer row instead of four zeros at full contrast. */
.library-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 14px;
}
.library-stats[hidden] { display: none; }
.library-stat {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 7px 12px 7px 10px;
  background: var(--bg-card);
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  font-size: 13px;
  color: var(--ink-2);
  transition: opacity .15s ease;
}
.library-stat-icon {
  font-size: 14px;
  line-height: 1;
}
.library-stat-value {
  font-weight: 600;
  color: var(--ink-1);
  font-variant-numeric: tabular-nums;
}
.library-stat-label {
  font-size: 12px;
  color: var(--ink-3);
}
.library-stats.is-empty .library-stat { opacity: .55; }
.library-actions .select { width: 200px; }

.book-grid {
  list-style: none;
  margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  /* --shelf-gap must equal the column gap: each card's ledge overhangs
     by half of it on both sides so neighbouring segments butt together
     into one continuous plank. Row gap leaves room for the 11px ledge. */
  --shelf-gap: 24px;
  gap: 40px var(--shelf-gap);
}
.book-card {
  position: relative;
  display: flex;
  flex-direction: column;
}
/* ---- Logline hover card (166) ----
   Body-mounted, fixed-position, placed by library.js from viewport
   rects. Appears only over cards carrying data-logline. Theme tokens
   only, so it's a complete opaque surface in light and dark. */
.book-logline-pop {
  position: fixed;
  z-index: 400;
  max-width: 340px;
  /* A logline can run to 1000 chars since 196 — never off-screen. */
  max-height: 60vh;
  overflow-y: auto;
  background: var(--bg-card);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  padding: 12px 14px;
}
.book-logline-pop[hidden] { display: none; }
.book-logline-pop-title {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--ink-2);
  margin: 0 0 6px;
}
.book-logline-pop-text {
  margin: 0;
  font-size: 13px;
  line-height: 1.55;
  color: var(--ink-1);
}
/* Drag-reorder visuals kick in on grids marked .is-draggable — every
   series grid, plus the standalone grid when in Manual sort. Cards stay
   [draggable=true] everywhere (so they can be dragged into a series),
   but the grab cursor + drop styling only show where a drop sticks. */
.book-grid.is-draggable .book-card { cursor: grab; }
.book-card.is-dragging { opacity: .55; cursor: grabbing; }
.book-grid.is-draggable .book-card.drag-over {
  outline: 2px dashed var(--accent);
  outline-offset: 4px;
}

/* Drag handle — six-dot grip in the top-left corner of each card.
   Hidden by default; revealed when the grid is in Manual sort.
   Positioned outside the cover-link so a click on the handle never
   navigates into the editor. */
.book-drag-handle {
  position: absolute;
  top: 6px;
  left: 6px;
  width: 26px;
  height: 26px;
  display: none;
  align-items: center;
  justify-content: center;
  background: rgba(255,255,255,.92);
  border: 1px solid var(--line);
  border-radius: 6px;
  color: var(--ink-2);
  cursor: grab;
  padding: 0;
  z-index: 2;
  box-shadow: 0 1px 3px rgba(0,0,0,.08);
  transition: background .12s ease, color .12s ease;
}
.book-grid.is-draggable .book-drag-handle { display: inline-flex; }
.book-drag-handle:hover { background: #fff; color: var(--ink-1); }
.book-drag-handle:active { cursor: grabbing; }

/* ----- Shelf grip (v2.44) — reorder series + universe shelves -----
   Mouse-only, like .book-drag-handle and .admin-drag-handle: tabindex=-1
   + aria-hidden, no keyboard duty. Sits first in the shelf header, which
   is what keeps it structurally outside every .book-grid and .book-card
   so a shelf drag and a card drag can never share a dragstart. */
.shelf-drag-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  padding: 0;
  background: none;
  border: 1px solid transparent;
  border-radius: 6px;
  color: var(--ink-3);
  opacity: .32;
  cursor: grab;
  transition: background .12s ease, color .12s ease, border-color .12s ease, opacity .12s ease;
}
/* Faint at rest rather than hidden: eight fully-opaque grips would be
   noise on a library this size, but a grip nobody can see is a feature
   nobody finds. Full strength on hover, and while a drag is in flight so
   the drop targets stay legible. */
.series-head:hover .shelf-drag-handle,
.universe-head:hover .shelf-drag-handle,
.shelf-drag-handle:focus-visible,
#library-books.is-dragging-active .shelf-drag-handle { opacity: 1; }
.shelf-drag-handle:hover { background: var(--bg-page); border-color: var(--line); color: var(--ink-1); }
.shelf-drag-handle:active { cursor: grabbing; }
/* Standalone DOES drag (149) — it's a top-level shelf like any other.
   Archived doesn't: it's a filing cabinet pinned below everything, and the
   PHP omits its grip anyway. Belt and braces. */
.series-shelf.is-archived .shelf-drag-handle { display: none; }

.series-shelf.is-dragging,
.universe-shelf.is-dragging { opacity: .55; }
/* Insertion feedback uses the LIST-ROW idiom (an inset rule on the head),
   not the dashed outline card grids use — a shelf is a row in a list of
   shelves, not a cell in a grid. */
.series-shelf.shelf-drag-over  > .series-head,
.universe-shelf.shelf-drag-over > .universe-head { box-shadow: inset 0 3px 0 var(--accent); }

/* Series selector in the Book details modal: select + inline "New". */
.series-select-row { display: flex; gap: 8px; align-items: stretch; }
.series-select-row select { flex: 1 1 auto; }
.series-select-row .btn-sm { flex: 0 0 auto; white-space: nowrap; }

/* ----- Series shelves ----- */
.series-shelf { margin-bottom: 44px; }
.series-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 18px;
  padding-bottom: 8px;
  /* Engraved-label rule: fades out to the right instead of running the
     full width, so the header reads as a plaque, not a table border. */
  border-bottom: 1px solid transparent;
  border-image: linear-gradient(90deg, var(--line-strong), rgba(0,0,0,0) 85%) 1;
}
.series-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px; height: 26px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--paper-2, #fff);
  color: var(--ink-2);
  cursor: pointer;
  flex: 0 0 auto;
}
.series-toggle:hover { color: var(--ink-1); background: var(--bg-card); }
.series-caret { transition: transform .15s ease; }
.series-shelf.is-collapsed .series-caret { transform: rotate(-90deg); }
.series-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.series-count {
  font-size: .8rem;
  color: var(--ink-3, var(--ink-2));
  background: var(--paper-2, #f3efe6);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 2px 10px;
  white-space: nowrap;
}
.series-actions {
  margin-left: auto;
  display: flex;
  gap: 8px;
}
.series-delete { color: var(--danger, #b3261e); }
.series-delete:hover { color: var(--danger, #b3261e); }

/* Collapsed → hide the grid but keep the header (and its drop target
   for dragging a book in, via the count badge / header zone).
   .book-grid (not .series-grid) so the standalone shelf collapses too. */
.series-shelf.is-collapsed .book-grid { display: none; }

/* The standalone shelf header is lighter — it's a catch-all, not a
   user-named series, so no rename/delete chrome (it does collapse). */
.series-shelf.is-standalone .series-title { color: var(--ink-3); font-weight: 500; }

/* ----- Universe shelves (115): a shelf-of-shelves wrapping series +
   standalone books that share one story world. Header echoes the
   series header but heavier; the body indents its children slightly
   with a hairline so membership reads at a glance. ----- */
.universe-shelf { margin-bottom: 52px; }
.universe-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 16px;
  padding-bottom: 10px;
  border-bottom: 3px double var(--line-strong, var(--line));
}
.universe-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 700;
  letter-spacing: .03em;
  color: var(--ink-1);
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.universe-glyph { color: var(--accent); flex: 0 0 auto; }
.universe-body {
  padding-left: 14px;
  border-left: 2px solid var(--line);
}
.universe-shelf.is-collapsed .universe-body { display: none; }
.universe-shelf.is-collapsed .series-caret { transform: rotate(-90deg); }
.universe-empty-hint {
  margin: 0;
  padding: 18px 14px;
  color: var(--ink-3, var(--ink-2));
  font-size: .85rem;
  border: 1px dashed var(--line);
  border-radius: 8px;
}
/* The per-shelf universe <select> is chrome, not content — keep it small. */
.series-actions .select-sm {
  font-size: .78rem;
  padding: 3px 6px;
  max-width: 160px;
}
/* Shared-entry marker in the Codex list */
.bible-row-shared { font-size: .75em; opacity: .75; margin-left: 4px; }
/* The universe scope header has no collapse toggle, so it keeps its own
   padding (the kind headers moved theirs onto .bible-group-toggle). */
.bible-universe-head { margin-top: 18px; border-top: 1px solid var(--line); padding: 12px 18px 6px; display: flex; align-items: center; gap: 6px; }
.bible-universe-empty { font-size: .8rem; }
/* Bulk-share affordance (v2.30) — rides the universe header's right edge. */
.bible-share-all { margin-left: auto; font-size: .72rem; color: var(--accent); white-space: nowrap; }
.bible-share-all:hover { text-decoration: underline; color: var(--accent); }
.bible-share-all:disabled { opacity: .5; cursor: default; }

/* Empty grids still need height so they're a reachable drop target.
   `.is-empty` is toggled by library.js (a CSS :empty selector wouldn't
   match because the PHP-rendered <ul> contains whitespace). */
.book-grid.is-empty {
  min-height: 90px;
  border: 1px dashed var(--line);
  border-radius: 8px;
  display: grid;
  place-items: center;
}
.book-grid.is-empty::after {
  content: 'Drag a book here';
  color: var(--ink-3, var(--ink-2));
  font-size: .85rem;
}
/* Highlight the shelf a dragged card is hovering over. */
.book-grid.drag-target {
  outline: 2px dashed var(--accent);
  outline-offset: 6px;
  border-radius: 8px;
}
.book-cover-link { display: block; position: relative; }
/* ---- Shelf ledge: every book stands on a wooden plank. The grid wraps,
   so there's no single per-row element to style; instead each card's
   cover-link grows a segment that overhangs by half the column gap on
   both sides — adjacent segments meet, so a full row reads as one
   continuous shelf and a part-filled row ends where the books end.
   Anchored to the cover-link (covers share the 2:3 frame) so the ledge
   lines up across a row no matter how tall each card's meta block is. */
.book-cover-link::after {
  content: "";
  position: absolute;
  z-index: 0;
  left: calc(var(--shelf-gap, 24px) / -2);
  right: calc(var(--shelf-gap, 24px) / -2);
  bottom: -11px;
  height: 10px;
  border-radius: 1px;
  background: linear-gradient(180deg, #a87e50 0%, #8a6238 45%, #6e4c29 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.28), /* lit front edge */
    0 3px 5px rgba(60,40,20,.35),
    0 9px 16px rgba(60,40,20,.18);
  pointer-events: none;
  transition: opacity .15s ease;
}
/* Contact shadow where the book meets the plank; softens on hover as
   the book lifts off the shelf. */
.book-cover-link::before {
  content: "";
  position: absolute;
  z-index: 1;
  left: 5%; right: 5%;
  bottom: -9px;
  height: 7px;
  background: radial-gradient(50% 100% at 50% 100%, rgba(22,14,5,.45), rgba(22,14,5,0) 72%);
  pointer-events: none;
  transition: opacity .2s ease;
}
:root[data-theme="dark"] .book-cover-link::after {
  background: linear-gradient(180deg, #4a3826 0%, #372a1a 45%, #281e12 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.09),
    0 3px 5px rgba(0,0,0,.5),
    0 9px 16px rgba(0,0,0,.3);
}
/* While a card is being dragged the woodwork recedes so the dashed
   accent drop-target outlines stay the loudest thing on screen. */
#library-books.is-dragging-active .book-cover-link::before,
#library-books.is-dragging-active .book-cover-link::after { opacity: .25; }
.book-cover {
  position: relative;
  aspect-ratio: 2 / 3;
  background: var(--ink-1);
  border-radius: 3px;
  overflow: hidden;
  box-shadow: var(--shadow-paper);
  transition: transform .2s ease, box-shadow .2s ease;
}
.book-card:hover .book-cover {
  transform: translateY(-6px);
  box-shadow: 0 4px 0 rgba(0,0,0,.04), 0 18px 40px rgba(60,52,32,.2);
}
.book-card:hover .book-cover-link::before { opacity: .55; }
/* Spine curve on the left + page-sliver highlight on the right — makes
   the 2:3 frame read as a physical book, and dresses the dark letterbox
   bars that object-fit:contain leaves around off-ratio covers. */
.book-cover::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  border-radius: inherit;
  background:
    linear-gradient(90deg,  rgba(0,0,0,.28) 0, rgba(255,255,255,.10) 2.5%, rgba(0,0,0,.10) 5%, rgba(0,0,0,0) 9%),
    linear-gradient(270deg, rgba(255,255,255,.16) 0, rgba(255,255,255,0) 2%);
}
/* Show the full cover (no cropping) inside the uniform 2:3 grid frame.
   Anything narrower or taller than 2:3 letterboxes against the dark frame. */
.book-cover img { width: 100%; height: 100%; object-fit: contain; display: block; }
.book-cover-fallback {
  position: absolute; inset: 0;
  background: linear-gradient(155deg, #2c2820 0%, #1a1715 100%);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  padding: 20px 14px;
  border-left: 4px solid var(--accent);
}
.book-cover-title {
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 1.25;
  color: #f1ead2;
  font-weight: 500;
}
.book-cover-author {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 11px;
  color: rgba(241,234,210,.6);
  margin-top: 12px;
  letter-spacing: .04em;
}
.book-meta { padding: 18px 4px 0; } /* clears the 11px ledge overhang */
.book-title {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 500;
  margin: 0 0 2px;
  color: var(--ink-1);
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.book-info {
  margin: 0;
  font-size: 12px;
  color: var(--ink-3);
}

/* Book-stage pill — author-only writing-stage marker shown beneath the
   word count on each library card. Click cycles draft → revising →
   complete → live → draft. Complete (manuscript finished) is blue;
   Live (published) takes the green. */
.book-status-pill {
  display: inline-block;
  margin-top: 6px;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--bg-paper);
  color: var(--ink-2);
  font-family: var(--font-sans);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background .12s ease, color .12s ease, border-color .12s ease, transform .12s ease;
}
.book-status-pill:hover { transform: translateY(-1px); }
.book-status-pill[data-current="draft"]    { background: #eceef1; color: #4b5560; border-color: #d6dae0; }
.book-status-pill[data-current="revising"] { background: #fbe4cb; color: #8a4d18; border-color: #efc99a; }
.book-status-pill[data-current="complete"] { background: #dbe6f5; color: #2d5583; border-color: #bcd0ec; }
.book-status-pill[data-current="live"]     { background: #d8ebd8; color: #2f6633; border-color: #b8d8b8; }
.book-status-pill.is-saving { opacity: .55; cursor: progress; }

/* Per-book word-target progress. Rendered only when word_target > 0
   on the book row. Color is keyed to the same status palette as the
   pill above so the card reads as a single colored unit. */
.book-progress {
  position: relative;
  height: 6px;
  margin: 6px 0 8px;
  background: var(--bg-rail);
  border-radius: 999px;
  overflow: hidden;
  border: 1px solid var(--line-soft);
}
.book-progress-fill {
  position: absolute;
  inset: 0 auto 0 0;
  background: #b6bcc3;
  transition: width .25s ease;
  border-radius: inherit;
}
.book-progress[data-status="draft"]    .book-progress-fill { background: #b6bcc3; }
.book-progress[data-status="revising"] .book-progress-fill { background: #f0a253; }
.book-progress[data-status="complete"] .book-progress-fill { background: #6f9bd8; }
.book-progress[data-status="live"]     .book-progress-fill { background: #6cba6c; }
.book-progress-label {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--ink-3);
  background: rgba(255,255,255,.85);
  padding: 0 4px;
  border-radius: 3px;
  pointer-events: none;
}

/* ---- Library card controls: dark-mode variants. The white-ish chips
   (drag handle, duplicate/delete, progress label) and the four pastel
   stage pills were all literal light values, so they glowed on the dark
   theme. Same hues, re-pitched for a dark surface. ---- */
:root[data-theme="dark"] .book-drag-handle { background: rgba(32,28,23,.92); }
:root[data-theme="dark"] .book-drag-handle:hover { background: var(--bg-card); }
:root[data-theme="dark"] .shelf-drag-handle:hover { background: var(--bg-card); border-color: var(--line); }
:root[data-theme="dark"] .book-delete,
:root[data-theme="dark"] .book-duplicate,
:root[data-theme="dark"] .book-archive { background: rgba(32,28,23,.92); }
:root[data-theme="dark"] .book-progress-label { background: rgba(38,34,29,.85); }
:root[data-theme="dark"] .book-status-pill[data-current="draft"]    { background: #2e3136; color: #aeb6c0; border-color: #3d434b; }
:root[data-theme="dark"] .book-status-pill[data-current="revising"] { background: #3a2a15; color: #e0a86a; border-color: #54401f; }
:root[data-theme="dark"] .book-status-pill[data-current="complete"] { background: #1f2c3f; color: #8fb2e0; border-color: #2e4460; }
:root[data-theme="dark"] .book-status-pill[data-current="live"]     { background: #1f321f; color: #8cc48c; border-color: #2f4d2f; }
:root[data-theme="dark"] .book-progress[data-status="draft"]    .book-progress-fill { background: #7f868f; }
:root[data-theme="dark"] .book-progress[data-status="revising"] .book-progress-fill { background: #c9853f; }
:root[data-theme="dark"] .book-progress[data-status="complete"] .book-progress-fill { background: #5a83bd; }
:root[data-theme="dark"] .book-progress[data-status="live"]     .book-progress-fill { background: #58a058; }

/* ---------- /settings page ---------- */
.topbar-back-link {
  color: rgba(241,234,210,.78);
  text-decoration: none;
  font-size: 13px;
  letter-spacing: .04em;
  margin-left: 8px;
  transition: color .12s ease;
}
.topbar-back-link:hover { color: #f1ead2; }

.settings-main {
  max-width: 720px;
  margin: 0 auto;
  padding: 36px 28px 80px;
}
.settings-hero {
  position: relative;
  padding-bottom: 22px;
  border-bottom: 1px solid var(--line-soft);
  margin-bottom: 28px;
}
.settings-title {
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 500;
  margin: 0 0 6px;
  color: var(--ink-1);
}
.settings-sub {
  margin: 0;
  font-size: 13px;
  color: var(--ink-3);
  line-height: 1.55;
  max-width: 560px;
}
.settings-save-status {
  position: absolute;
  top: 4px;
  right: 0;
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.settings-save-status[data-state="busy"]  { color: var(--ink-2); }
.settings-save-status[data-state="idle"]  { color: var(--success, #2f6633); }
.settings-save-status[data-state="error"] { color: var(--danger, #b04646); }

.settings-form {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.settings-card {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.settings-card-head {
  padding: 18px 22px 12px;
  border-bottom: 1px solid var(--line-soft);
}
.settings-card-head h2 {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 500;
  margin: 0 0 4px;
  color: var(--ink-1);
}
.settings-card-sub {
  margin: 0;
  font-size: 12.5px;
  color: var(--ink-3);
  line-height: 1.5;
}
.settings-card-body { padding: 20px 22px 6px; }
.settings-card-body .field { margin-bottom: 18px; }

/* ----- Settings › Page templates management list ----- */
.mt-list { padding-bottom: 14px; }
.mt-group + .mt-group { margin-top: 18px; }
.mt-group-head {
  margin: 0 0 8px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-3);
}
.mt-rows { list-style: none; margin: 0; padding: 0; }
.mt-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}
.mt-row:last-child { border-bottom: none; }
.mt-row-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 14px;
  color: var(--ink-1);
  cursor: text;
  padding: 3px 6px;
  margin: -3px -6px;
  border-radius: 5px;
  border: 1px solid transparent;
}
.mt-row-label:hover,
.mt-row-label:focus {
  background: var(--bg-rail);
  border-color: var(--line);
  outline: none;
}
.mt-row-input {
  flex: 1 1 auto;
  min-width: 0;
  font: inherit;
  font-size: 14px;
  color: var(--ink-1);
  padding: 3px 6px;
  margin: -1px 0;
  border: 1px solid var(--accent);
  border-radius: 5px;
  background: var(--bg-1, #fff);
}
.mt-row-input:focus { outline: none; }
.mt-row-actions {
  flex: 0 0 auto;
  display: inline-flex;
  gap: 6px;
}
.mt-row-delete:hover,
.mt-row-delete:focus { color: var(--danger, #c0392b); }
.mt-drag { cursor: grab; flex-shrink: 0; }
.mt-drag:active { cursor: grabbing; }
.mt-dragging { opacity: .45; }
button.mt-row-label { text-align: left; font: inherit; background: transparent; cursor: pointer; }
.mt-dialog { width: min(840px, calc(100vw - 32px)); max-height: 90vh; box-sizing: border-box; overflow-y: auto; padding: 24px; border: 1px solid var(--line); border-radius: 12px; color: var(--ink-1); background: var(--bg-card); }
.mt-dialog::backdrop { background: rgb(0 0 0 / .5); }
.mt-fields { border: 0; margin: 0; padding: 0; min-width: 0; }
.mt-options { display: flex; flex-wrap: wrap; gap: 12px 24px; margin: 16px 0; }
.mt-toolbar { display: flex; flex-wrap: wrap; gap: 4px; padding: 8px; border: 1px solid var(--line); border-radius: 6px 6px 0 0; }
.mt-body { min-height: 240px; max-height: 45vh; overflow-y: auto; padding: 20px; border: 1px solid var(--line); border-top: 0; line-height: 1.65; overflow-wrap: anywhere; }
.mt-body .ql-align-center { text-align: center; }
.mt-body .ql-align-right { text-align: right; }
.mt-body .ql-align-justify { text-align: justify; }
.mt-body:focus { outline: 2px solid var(--accent, #8b6b13); outline-offset: -2px; }
.mt-body img { max-width: 100%; height: auto; }
.mt-dialog-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; }
.mt-error { color: var(--danger, #c0392b); }
@media (max-width: 600px) {
  .mt-row { flex-wrap: wrap; }
  .mt-row-actions { width: 100%; justify-content: flex-end; }
  .mt-dialog { padding: 16px; }
}

/* ----- Settings › Membership card ----- */
.membership-state { font-size: 14px; line-height: 1.5; color: var(--ink-1); }
.membership-state.is-member strong { color: var(--accent); }
.membership-price {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
}
.membership-price-was {
  font-size: 18px;
  color: var(--ink-3);
  text-decoration: line-through;
}
.membership-price-now {
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 700;
  color: var(--ink-1);
  line-height: 1;
}
.membership-price-note { font-size: 12.5px; color: var(--ink-3); }

/* ----- Authenticated app footer (library, etc.) ----- */
.app-foot {
  border-top: 1px solid var(--line);
  margin-top: 48px;
}
.app-foot-inner {
  max-width: 1180px;
  margin: 0 auto;
  padding: 26px 32px 34px;
  display: flex;
  align-items: center;
  gap: 18px 26px;
  flex-wrap: wrap;
}
.app-foot-brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
}
.app-foot-brand .logo-mark {
  width: 28px; height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent);
  color: #fff;
  border-radius: 6px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 16px;
}
/* Uploaded-logo variant: drop the accent tile, contain the image. */
.app-foot-brand img.logo-mark { background: transparent; object-fit: contain; }
.app-foot-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 16px;
  color: var(--ink-1);
}
.app-foot-links {
  display: flex;
  gap: 22px;
  flex-wrap: wrap;
  margin-left: auto;
}
.app-foot-links a {
  color: var(--ink-2);
  text-decoration: none;
  font-size: 14px;
}
.app-foot-links a:hover,
.app-foot-links a:focus { color: var(--accent); }
.app-foot-fineprint {
  flex-basis: 100%;
  margin: 2px 0 0;
  font-size: 12.5px;
  color: var(--ink-3);
}

.settings-card-body .field:last-child { margin-bottom: 14px; }
.settings-card-body .select { width: 100%; }
.field-hint {
  display: block;
  font-size: 11.5px;
  color: var(--ink-3);
  margin-top: 6px;
  line-height: 1.45;
}
.book-delete {
  position: absolute;
  top: 8px; right: 8px;
  width: 26px; height: 26px;
  border-radius: 50%;
  border: none;
  background: rgba(255,255,255,.92);
  color: var(--ink-2);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  transition: opacity .15s ease, background .15s ease, color .15s ease;
  box-shadow: 0 1px 4px rgba(0,0,0,.16);
}
.book-card:hover .book-delete { opacity: 1; }
.book-delete:hover { background: var(--danger); color: #fff; }

.book-duplicate {
  position: absolute;
  top: 8px; right: 40px;
  width: 26px; height: 26px;
  border-radius: 50%;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255,255,255,.92);
  color: var(--ink-2);
  cursor: pointer;
  opacity: 0;
  transition: opacity .15s ease, background .15s ease, color .15s ease;
  box-shadow: 0 1px 4px rgba(0,0,0,.16);
}
.book-card:hover .book-duplicate { opacity: 1; }
.book-duplicate:hover { background: var(--accent); color: #fff; }
.book-duplicate:disabled { cursor: default; opacity: .5; }

/* Archive / restore (146) — the third button in the hover row, left of
   duplicate. On an archived card it's the ONLY one of the three (no grip,
   no duplicate) and it restores instead, so it takes duplicate's slot. */
.book-archive {
  position: absolute;
  top: 8px; right: 72px;
  width: 26px; height: 26px;
  border-radius: 50%;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255,255,255,.92);
  color: var(--ink-2);
  cursor: pointer;
  opacity: 0;
  transition: opacity .15s ease, background .15s ease, color .15s ease;
  box-shadow: 0 1px 4px rgba(0,0,0,.16);
}
.book-card:hover .book-archive { opacity: 1; }
.book-archive:hover { background: var(--accent); color: #fff; }
.book-archive:disabled { cursor: default; opacity: .5; }
.book-card[data-archived] .book-archive { right: 40px; }

/* The Archived shelf is a filing cabinet, not a shelf you work from —
   pushed down and muted a step further than Standalone (:1003). */
.series-shelf.is-archived { margin-top: 48px; }
.series-shelf.is-archived .series-title { color: var(--ink-3); font-weight: 500; }
.series-shelf.is-archived .book-card { filter: saturate(.72); opacity: .88; }
.series-shelf.is-archived .book-card:hover { filter: none; opacity: 1; }

.empty-state {
  text-align: center;
  padding: 80px 24px;
  background: var(--bg-card);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-lg);
}
.empty-illustration {
  font-size: 56px;
  margin-bottom: 16px;
  filter: grayscale(.2);
}
.empty-state h2 {
  font-family: var(--font-display);
  font-size: 22px;
  margin: 0 0 8px;
}
.empty-state p {
  color: var(--ink-3);
  max-width: 460px;
  margin: 0 auto 24px;
}

/* ============================================================
   CONFIRMATION DIALOG (Scrived.confirm replacement for window.confirm)
   ============================================================ */
.sc-confirm {
  position: fixed;
  inset: 0;
  z-index: 1300; /* the only thing demanding the user's attention — must
                    beat the codex drawer (1000) and the relationship
                    modal (1200), both of which can spawn a confirm */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  opacity: 0;
  transition: opacity .15s ease;
  pointer-events: none;
}
.sc-confirm.is-open { opacity: 1; pointer-events: auto; }
.sc-confirm[hidden] { display: none; }
.sc-confirm-backdrop {
  position: absolute; inset: 0;
  background: rgba(20, 18, 14, .50);
}
.sc-confirm-card {
  position: relative;
  width: min(440px, 100%);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 24px 60px rgba(0,0,0,.32);
  padding: 22px 24px 18px;
}
.sc-confirm-title {
  margin: 0 0 6px;
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 500;
  color: var(--ink-1);
  line-height: 1.35;
}
.sc-confirm-msg {
  margin: 0 0 18px;
  font-size: 13.5px;
  color: var(--ink-2);
  line-height: 1.5;
  white-space: pre-line;
}
.sc-confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
.sc-confirm-cancel,
.sc-confirm-ok {
  border: 1px solid var(--line);
  background: var(--bg-card);
  color: var(--ink-2);
  padding: 8px 16px;
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.sc-confirm-cancel:hover { background: var(--bg-rail); color: var(--ink-1); }
/* Follows the app's dark-button convention (.btn-primary), not the gold
   accent — gold is for badges and active states, primary actions are ink. */
.sc-confirm-ok.is-primary {
  background: var(--ink-1);
  border-color: var(--ink-1);
  color: var(--bg-page);
  font-weight: 600;
}
.sc-confirm-ok.is-primary:hover { filter: brightness(.92); }
.sc-confirm-ok.is-danger {
  background: var(--danger, #b04646);
  border-color: var(--danger, #b04646);
  color: #fff;
  font-weight: 600;
}
.sc-confirm-ok.is-danger:hover { filter: brightness(.92); }
.sc-confirm-ok:focus-visible,
.sc-confirm-cancel:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(139,105,20,.20);
}
:root[data-theme="dark"] .sc-confirm-card {
  background: var(--bg-card);
  border-color: var(--line);
}
/* prompt.js — the confirm card with a text box (shares every rule above). */
.sc-prompt-input {
  display: block;
  width: 100%;
  margin: 0 0 12px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-page, #fff);
  color: var(--ink-1);
  font: inherit;
  font-size: 14px;
}
.sc-prompt-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(139,105,20,.18); }
.sc-prompt .sc-confirm-msg { margin-bottom: 10px; }
.sc-prompt-error { margin: -6px 0 12px; font-size: 12.5px; color: var(--danger, #b04646); }
.sc-prompt-error[hidden] { display: none; }

/* ============================================================
   Page loading overlay — the unmissable "opening this book" state.
   Two users: the editor renders it server-side and editor.js fades it
   out after the first chapter lands; the library unhides its copy the
   moment a book card is clicked, so heavy editor loads never look like
   a dead click. Sits above everything (drawer 1000, link modal 1200,
   confirm 1300) — nothing should compete with "we're loading".
   ============================================================ */
.page-loading-overlay {
  position: fixed;
  inset: 0;
  z-index: 1400;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(245, 243, 238, .88);
  backdrop-filter: blur(2px);
  opacity: 1;
  transition: opacity .25s ease;
}
.page-loading-overlay.is-done { opacity: 0; pointer-events: none; }
.page-loading-overlay[hidden] { display: none; }
.page-loading-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  min-width: 300px;
  max-width: min(480px, 90vw);
  padding: 40px 48px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  text-align: center;
}
.page-loading-spinner {
  width: 44px;
  height: 44px;
  border: 4px solid var(--line-soft);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: scrived-spin .8s linear infinite;
}
.page-loading-title {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 600;
  color: var(--ink-1);
  overflow-wrap: anywhere;
}
.page-loading-sub {
  font-size: 12.5px;
  color: var(--ink-3);
}
:root[data-theme="dark"] .page-loading-overlay {
  background: rgba(24, 22, 18, .88);
}

/* ============================================================
   KEYBOARD SHORTCUT HELP OVERLAY (?)
   ============================================================ */
.kbd-help {
  position: fixed;
  inset: 0;
  z-index: 240;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px 16px;
  opacity: 0;
  transition: opacity .15s ease;
  pointer-events: none;
}
.kbd-help.is-open { opacity: 1; pointer-events: auto; }
.kbd-help[hidden] { display: none; }
.kbd-backdrop {
  position: absolute; inset: 0;
  background: rgba(20, 18, 14, .42);
}
.kbd-card {
  position: relative;
  width: min(720px, 100%);
  max-height: calc(100vh - 64px);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 24px 60px rgba(0,0,0,.28);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.kbd-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--line);
}
.kbd-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 500;
  color: var(--ink-1);
}
.kbd-close {
  width: 28px; height: 28px;
  border: none; background: transparent;
  color: var(--ink-3);
  font-size: 22px; line-height: 1;
  cursor: pointer; padding: 0;
  border-radius: 4px;
}
.kbd-close:hover { color: var(--ink-1); background: var(--bg-rail); }
.kbd-body {
  flex: 1;
  overflow-y: auto;
  padding: 18px 20px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 22px 32px;
}
.kbd-group { min-width: 0; }
.kbd-group-title {
  margin: 0 0 8px;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.kbd-list { margin: 0; padding: 0; }
.kbd-row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 10px;
  align-items: baseline;
  padding: 5px 0;
  font-size: 13px;
}
.kbd-keys {
  margin: 0;
  flex-shrink: 0;
  white-space: nowrap;
}
.kbd-desc {
  margin: 0;
  color: var(--ink-2);
  line-height: 1.4;
}
.kbd-plus {
  display: inline-block;
  margin: 0 2px;
  color: var(--ink-3);
  font-size: 11px;
}
.kbd-help kbd,
.kbd-foot kbd {
  display: inline-block;
  min-width: 22px;
  padding: 1px 6px;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--bg-paper);
  font-family: var(--font-sans);
  font-size: 11.5px;
  font-weight: 600;
  color: var(--ink-1);
  text-align: center;
  box-shadow: 0 1px 0 var(--line);
}
.kbd-foot {
  padding: 10px 20px;
  border-top: 1px solid var(--line-soft);
  background: var(--bg-rail);
  font-size: 11.5px;
  color: var(--ink-3);
  text-align: center;
}
:root[data-theme="dark"] .kbd-card {
  background: var(--bg-card);
  border-color: var(--line);
}
:root[data-theme="dark"] .kbd-help kbd,
:root[data-theme="dark"] .kbd-foot kbd {
  background: var(--bg-rail);
}
@media (max-width: 640px) {
  .kbd-body { grid-template-columns: 1fr; }
}

/* ============================================================
   COMMAND PALETTE (Cmd/Ctrl+K)
   ============================================================ */
.cmd-palette {
  position: fixed;
  inset: 0;
  z-index: 250; /* above toasts so it can be dismissed cleanly */
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 12vh;
  opacity: 0;
  transition: opacity .14s ease;
  pointer-events: none;
}
.cmd-palette.is-open { opacity: 1; pointer-events: auto; }
.cmd-palette[hidden] { display: none; }
.cmd-backdrop {
  position: absolute; inset: 0;
  background: rgba(20, 18, 14, .42);
}
.cmd-card {
  position: relative;
  width: min(640px, calc(100vw - 32px));
  max-height: calc(100vh - 24vh);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 24px 60px rgba(0,0,0,.28);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.cmd-input {
  width: 100%;
  padding: 16px 18px;
  font-family: inherit;
  font-size: 15px;
  color: var(--ink-1);
  border: none;
  border-bottom: 1px solid var(--line);
  background: transparent;
  outline: none;
}
.cmd-input::placeholder { color: var(--ink-3); }
.cmd-list {
  list-style: none;
  margin: 0;
  padding: 6px 0;
  overflow-y: auto;
  flex: 1;
}
.cmd-item {
  display: grid;
  grid-template-columns: 28px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 8px 16px;
  cursor: pointer;
  font-size: 14px;
}
.cmd-item.is-active {
  background: var(--accent-soft, rgba(139,105,20,.12));
}
.cmd-icon {
  font-size: 16px;
  text-align: center;
  flex-shrink: 0;
  line-height: 1;
}
.cmd-title {
  color: var(--ink-1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cmd-meta {
  font-size: 11.5px;
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: .08em;
  white-space: nowrap;
}
.cmd-empty {
  padding: 16px;
  text-align: center;
  color: var(--ink-3);
  font-style: italic;
}
.cmd-foot {
  display: flex;
  gap: 16px;
  padding: 8px 16px;
  border-top: 1px solid var(--line-soft);
  background: var(--bg-rail);
  font-size: 11.5px;
  color: var(--ink-3);
}
.cmd-foot kbd {
  display: inline-block;
  min-width: 20px;
  padding: 1px 5px;
  margin: 0 2px;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: #fff;
  font-family: inherit;
  font-size: 11px;
  color: var(--ink-2);
}
:root[data-theme="dark"] .cmd-card {
  background: var(--bg-card);
  border-color: var(--line);
}
:root[data-theme="dark"] .cmd-foot kbd { background: var(--bg-rail); }

/* ============================================================
   TOAST NOTIFICATIONS
   ============================================================ */
.toast-container {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 200; /* above modals, popovers, the rail panel */
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none; /* let clicks fall through the gaps */
  max-width: 360px;
}
.toast {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 10px 10px 14px;
  background: #fff;
  border: 1px solid var(--line);
  border-left: 3px solid var(--ink-3);
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px rgba(0,0,0,.12);
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--ink-1);
  pointer-events: auto;
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity .18s ease, transform .18s ease;
}
.toast.is-visible { opacity: 1; transform: translateY(0); }
.toast.is-leaving { opacity: 0; transform: translateY(-6px); }
.toast-msg { flex: 1; min-width: 0; word-break: break-word; }
.toast-close {
  flex-shrink: 0;
  width: 22px; height: 22px;
  border: none; background: transparent;
  color: var(--ink-3);
  font-size: 18px; line-height: 1;
  cursor: pointer; padding: 0;
  border-radius: 4px;
}
.toast-close:hover { color: var(--ink-1); background: var(--bg-rail); }
.toast-info    { border-left-color: var(--accent); }
.toast-success { border-left-color: #4a8b4d; }
.toast-warn    { border-left-color: #d18a3d; }
.toast-error   { border-left-color: var(--danger, #b04646); }
:root[data-theme="dark"] .toast {
  background: var(--bg-card);
  color: var(--ink-1);
  border-color: var(--line);
}

/* ============================================================
   MODAL
   ============================================================ */
.modal {
  position: fixed; inset: 0;
  z-index: 100;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; pointer-events: none;
  transition: opacity .18s ease;
}
.modal.is-open { opacity: 1; pointer-events: auto; }
.modal-backdrop {
  position: absolute; inset: 0;
  background: rgba(20,17,12,.55);
}
.modal-card {
  position: relative;
  width: 100%;
  max-width: 440px;
  background: var(--bg-card);
  border-radius: var(--radius-lg);
  padding: 28px 32px 24px;
  box-shadow: var(--shadow-2);
  transform: translateY(8px);
  transition: transform .18s ease;
}
.modal.is-open .modal-card { transform: translateY(0); }
.modal-title {
  font-family: var(--font-display);
  font-size: 22px;
  margin: 0 0 18px;
}
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 18px;
}

/* ============================================================
   EDITOR — Atticus-style layout
   ============================================================ */
.editor-body {
  height: 100vh;
  overflow: hidden;
  background: var(--bg-page);
}
/* ⚠ .editor-shell must NEVER gain transform / filter / perspective /
   contain / container-type / will-change. #codex-drawer is a
   position:fixed CHILD of this element and relies on the viewport being
   its containing block; any of those properties would make the shell the
   containing block instead and break the drawer. The dock gutter below
   is padding for exactly that reason. See the codex block at ~13830. */
.editor-shell {
  height: 100vh;
  display: flex;
  flex-direction: column;
  /* Codex drawer width. Declared HERE, not on #codex-drawer, because two
     separate elements read it — the drawer's own width and this shell's
     dock gutter — and custom properties only inherit downward, so the
     shell is their nearest common ancestor. editor.js overwrites it
     inline with a viewport-clamped value. */
  --codex-w: 880px;
}
/* Docked codex: instead of overlaying the manuscript, the drawer reserves
   its own gutter and the topbar + .editor-body-grid shift right, so the
   page sheet re-centres in what's left and is never covered. Merely
   narrowing the overlay can't achieve this — the sheet is centred in the
   1fr manuscript track, so at 1180px it starts at x≈316 and no usable
   drawer width clears it.

   Padding the SHELL, not .editor-body-grid: otherwise the drawer's dark
   head covers the topbar-left cluster (logo, home, book title) and takes
   away the way out of the editor.

   editor.js owns .is-codex-docked — the decision depends on --codex-w AND
   the viewport AND the active view, which no media query can express. */
.editor-shell.is-codex-docked { padding-left: min(var(--codex-w), 96vw); }
/* Armed one frame after boot, exactly like .codex-drawer-ready, so a
   shell that reloads already docked doesn't animate on first paint. */
.editor-shell.is-codex-ready { transition: padding-left 260ms ease; }

/* ----- Top bar ----- */
.editor-topbar {
  height: var(--topbar-h);
  background: var(--bg-topbar);
  color: #fff;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 0 18px;
  gap: 16px;
}
.editor-topbar .topbar-left   { justify-self: start; gap: 12px; }
/* Center holds the view tabs AND the Export pill beside them (v2.16). */
.editor-topbar .topbar-center {
  justify-self: center;
  display: flex;
  align-items: center;
  gap: 10px;
}
.editor-topbar .topbar-right  { justify-self: end; gap: 14px; }

.topbar-logo {
  width: 30px; height: 30px;
  background: var(--accent);
  border-radius: 6px;
  display: inline-flex; align-items: center; justify-content: center;
  color: #fff;
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 700;
  text-decoration: none;
}
.topbar-logo:hover { color: #fff; opacity: .92; }

/* ----- Uploaded brand logo (replaces the lettered "P" mark) -----
   The <img> keeps the .logo-mark class so each context's existing box
   sizing still applies; these rules drop the accent tile behind it and
   make it sit as a contained image. The context is baked into the
   selector so it out-specifies the base `.<ctx> .logo-mark { background:
   accent }` rules. */
.topbar-logo img.logo-mark,
.admin-logo img.logo-mark {
  background: transparent;
  object-fit: contain;
}
/* Main + snapshot-diff topbars paint the accent tile on the *wrapper*;
   blank it when it holds an uploaded logo and let the image fill the slot. */
.topbar-logo:has(img.logo-mark) { background: transparent; }
.topbar-logo img.logo-mark { width: 100%; height: 100%; border-radius: 6px; }
.topbar-home {
  width: 30px; height: 30px;
  display: inline-flex; align-items: center; justify-content: center;
  color: #c8c3b6;
  border-radius: 6px;
  border: 1px solid transparent;
}
.topbar-home:hover { color: #fff; background: var(--bg-topbar-soft); border-color: #3a3733; }

.topbar-title-block {
  display: flex; align-items: center; gap: 14px;
  min-width: 0;
}
.topbar-title-text {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  color: #fff;
  letter-spacing: .12em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 320px;
}
.btn-edit-details {
  background: transparent;
  border: 1px solid #3a3733;
  color: #c8c3b6;
  font-family: inherit;
  font-size: 12.5px;
  padding: 5px 12px;
  border-radius: 999px;
  cursor: pointer;
  letter-spacing: .01em;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
  white-space: nowrap;
}
.btn-edit-details:hover { background: var(--bg-topbar-soft); color: #fff; border-color: #555048; }

.view-tabs {
  display: flex;
  gap: 2px;
  background: var(--bg-topbar-soft);
  border-radius: 999px;
  padding: 3px;
}
.view-tab {
  background: transparent;
  border: none;
  color: #c8c3b6;
  padding: 7px 22px;
  font-size: 13px;
  font-weight: 500;
  border-radius: 999px;
  cursor: pointer;
  font-family: inherit;
  letter-spacing: .02em;
  transition: all .15s ease;
}
.view-tab:hover { color: #fff; }
.view-tab.is-active {
  background: #fff;
  color: var(--ink-1);
}

.topbar-iconbtn {
  width: 30px; height: 30px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
  color: #c8c3b6;
  cursor: pointer;
  padding: 0;
}
.topbar-iconbtn:hover { color: #fff; background: var(--bg-topbar-soft); border-color: #3a3733; }

/* ============================================================
   Custom tooltips (assets/js/tooltips.js).
   The JS shim copies [title] onto opted-in icon buttons into a
   [data-tip] attribute and removes the native title (so the
   browser's slow grey OS tooltip doesn't double up). This rule
   surfaces a styled tooltip below the button on hover/focus.
   ============================================================ */
[data-tip] {
  position: relative;
}
[data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  top: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%) translateY(-2px);
  background: #1d1b18;
  color: #f5f1e6;
  padding: 5px 10px;
  border-radius: 5px;
  font-family: var(--font-sans);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: .01em;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  z-index: 1100;
  box-shadow: 0 4px 14px rgba(0,0,0,.25);
  transition: opacity .12s ease, transform .12s ease;
}
[data-tip]:hover::after,
[data-tip]:focus-visible::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
/* Rail variant: the rail hugs the right viewport edge, so the default
   centered-below nowrap tip would overflow offscreen. Anchor it to the
   button's LEFT instead. */
.icon-rail [data-tip]::after {
  top: 50%;
  left: auto;
  right: calc(100% + 8px);
  transform: translateY(-50%) translateX(2px);
}
.icon-rail [data-tip]:hover::after,
.icon-rail [data-tip]:focus-visible::after {
  opacity: 1;
  transform: translateY(-50%) translateX(0);
}
/* Multiline variant (v2.52): the base tip is nowrap, which is right
   for icon labels but truncates nothing and stretches forever for
   sentence-length help. Anything longer than ~6 words wears this. */
[data-tip].tip-multi::after {
  white-space: normal;
  width: max-content;
  max-width: 260px;
  line-height: 1.45;
  text-align: left;
}

/* Compact export pills in the topbar (next to the Export view icon).
   Single click triggers a download via export.js. */
.topbar-export-pill {
  height: 28px;
  padding: 0 12px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: 999px;
  background: var(--bg-topbar-soft);
  border: 1px solid #3a3733;
  color: #d8d3c5;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
}
.topbar-export-pill:hover {
  color: #fff;
  background: var(--accent);
  border-color: var(--accent);
}
.topbar-export-pill[aria-busy="true"] {
  opacity: .65;
  pointer-events: none;
  cursor: progress;
}

/* Export modal (topbar). The toggle reuses .topbar-export-pill; the items
   render inside the app's standard .modal/.modal-card (see MODAL above),
   not a bespoke anchored dropdown, so it gets the same centered card +
   backdrop treatment as every other Workshop dialog. */
.topbar-export { position: relative; display: inline-flex; }
.topbar-export-toggle .export-caret { transition: transform .15s ease; opacity: .85; }
.topbar-export-toggle[aria-expanded="true"] {
  color: #fff;
  background: var(--accent);
  border-color: var(--accent);
}
.topbar-export-toggle[aria-expanded="true"] .export-caret { transform: rotate(180deg); }
/* Same icon + title + description language as the Workshop page picker
   (.wm-menu-item, above) — its own class family since the two menus are
   unrelated and will likely diverge, matching how .wm-menu itself stays
   separate from .tf-menu. */
.export-modal-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 4px;
}
.export-menu-item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  width: 100%;
  padding: 10px 12px;
  background: transparent;
  border: 0;
  border-radius: 8px;
  color: var(--ink-1);
  font-family: var(--font-sans);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.export-menu-item:hover { background: var(--accent-soft); }
.export-menu-item[aria-busy="true"] { opacity: .6; pointer-events: none; cursor: progress; }
.export-menu-glyph {
  flex: 0 0 20px;
  margin-top: 1px;
  color: var(--accent);
}
.export-menu-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
/* Explicit color, not inherited: book_details.php's export items are <a>
   elements (editor.php's are <button>), and the global a:hover rule
   otherwise wins over the ancestor's color once hovered — see the
   identical fix on .wm-menu-text strong for the full explanation. */
.export-menu-text strong { font-size: 13.5px; font-weight: 600; color: var(--ink-1); }
.export-menu-text span { font-size: 11.5px; line-height: 1.4; color: var(--ink-3); }
.topbar-export-sep { height: 1px; background: var(--line); margin: 5px 4px; }

.topbar-divider { width: 1px; height: 22px; background: #3a3733; display: inline-block; }
.user-avatar {
  width: 30px; height: 30px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .03em;
  display: inline-flex; align-items: center; justify-content: center;
}

/* Account menu — avatar button + sign-out dropdown (in-book topbars). */
.user-menu { position: relative; display: inline-flex; }
.user-avatar-btn {
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
}
.user-avatar-btn:hover { filter: brightness(1.08); }
.user-menu-dropdown {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 250;
  min-width: 190px;
  padding: 6px;
  background: #fff;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  box-shadow: 0 12px 32px rgba(0,0,0,.18);
}
.user-menu-dropdown[hidden] { display: none; }
.user-menu-identity {
  padding: 6px 10px 8px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--line);
}
.user-menu-name {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-1);
  word-break: break-word;
}
.user-menu-form { margin: 0; }
.user-menu-item {
  display: block;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--ink-1);
  font-family: var(--font-sans);
  font-size: 13px;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.user-menu-item:hover { background: var(--bg-rail); }
.user-menu-signout { color: var(--danger); }
.user-menu-signout:hover { background: var(--danger); color: #fff; }

.save-status {
  font-size: 12.5px;
  color: #d4cfc1;
  font-weight: 500;
  letter-spacing: .03em;
  margin-right: 4px;
}
.save-status[data-state=dirty]   { color: #e6c876; }
.save-status[data-state=busy]    { color: #e6c876; }
.save-status[data-state=error]   { color: #f08c7c; }
.save-status[data-state=idle]    { color: #b8d6a3; }
.save-status[data-state=idle]::before {
  content: '✓ ';
  color: #8bc56a;
  font-weight: 700;
}

/* ----- Three-pane layout (sidebar | manuscript | rail-panel | rail) ----- */
.editor-body-grid {
  flex: 1;
  display: grid;
  grid-template-columns: 268px 1fr 56px;
  overflow: hidden;
  background: var(--bg-page);
}
.editor-body-grid.is-panel-open {
  grid-template-columns: 268px 1fr 280px 56px;
}

/* ----- Sidebar (sectioned chapter list) ----- */
.chapter-sidebar {
  background: var(--bg-rail);
  border-right: 1px solid var(--line);
  display: flex; flex-direction: column;
  overflow: hidden;
}
.sidebar-scroll {
  flex: 1;
  overflow-y: auto;
  padding: 14px 8px 14px;
}
.sidebar-section { margin-bottom: 14px; }
.sidebar-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 4px 8px 4px 6px;
  border-radius: var(--radius-sm);
}
.sidebar-section-toggle {
  flex: 1;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: transparent;
  border: none;
  padding: 4px 4px;
  cursor: pointer;
  font-family: inherit;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--ink-3);
}
.sidebar-section-toggle:hover { color: var(--ink-1); }
.sidebar-section-toggle .chev {
  color: var(--ink-4);
  transition: transform .15s ease;
}
.sidebar-section.is-collapsed .sidebar-section-toggle .chev { transform: rotate(-90deg); }
.sidebar-section.is-collapsed .chapter-list { display: none; }
.sidebar-section-add {
  width: 22px; height: 22px;
  border: 1px solid transparent;
  background: transparent;
  color: var(--ink-3);
  font-size: 16px;
  line-height: 1;
  border-radius: 4px;
  cursor: pointer;
  padding: 0;
}
.sidebar-section-add:hover { color: var(--ink-1); border-color: var(--line-strong); background: #fff; }

/* Status filter chip row — sits above the chapter sections. Reuses
   .res-chip from the corkboard, slightly tightened for the narrow
   sidebar. Wraps to a second row on smaller widths rather than
   horizontally scrolling. */
.sidebar-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 10px 10px 6px;
  border-bottom: 1px solid var(--line-soft);
}
.sidebar-filter .res-chip {
  padding: 3px 9px;
  font-size: 11px;
  letter-spacing: .02em;
}

/* Per-section status distribution. Lives inside the section toggle so
   the dots+numbers float to the right edge of the section heading. */
.sidebar-section-counts {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 500;
  font-size: 10px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-3);
}
.sidebar-section-counts .sc-pill {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-variant-numeric: tabular-nums;
}
.sidebar-section-counts .sc-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #b6bcc3;
}
.sidebar-section-counts .sc-dot[data-status="draft"]    { background: #b6bcc3; }
.sidebar-section-counts .sc-dot[data-status="revising"] { background: #f0a253; }
.sidebar-section-counts .sc-dot[data-status="revised"]  { background: #b27dd8; }
.sidebar-section-counts .sc-dot[data-status="final"]    { background: #6cba6c; }

/* Filter hide rules. The active chapter is always kept visible
   (`:not(.is-active)`) so the author can't lose track of where they
   are by toggling a filter that excludes their current document. */
.chapter-sidebar[data-filter="draft"]    .chapter-item:not([data-status="draft"]):not(.is-active),
.chapter-sidebar[data-filter="revising"] .chapter-item:not([data-status="revising"]):not(.is-active),
.chapter-sidebar[data-filter="revised"]  .chapter-item:not([data-status="revised"]):not(.is-active),
.chapter-sidebar[data-filter="final"]    .chapter-item:not([data-status="final"]):not(.is-active) {
  display: none;
}
/* When any non-"all" filter is active, hide the front-matter
   placeholder row too — its data-status is undefined so it would
   otherwise sit there during a filtered view. */
.chapter-sidebar:not([data-filter="all"]) .sidebar-empty-row { display: none; }

/* Empty-state for the status filter. Lives at the top of the
   .sidebar-scroll region so it's always visible, even when sections
   below it scroll. Italics + muted color so it reads as guidance
   rather than a section. */
.sidebar-no-matches {
  margin: 12px 8px;
  padding: 12px 14px;
  text-align: center;
  font-size: 12px;
  font-style: italic;
  color: var(--ink-3);
  background: var(--bg-paper);
  border: 1px dashed var(--line);
  border-radius: var(--radius-sm);
}

.chapter-list {
  list-style: none;
  margin: 4px 0 0;
  padding: 0;
}
.chapter-item {
  display: grid;
  /* number | name | status-dot | comments-badge? | words | delete
     — items are pinned to columns explicitly below so an empty badge
     row still keeps words+delete aligned with neighbouring rows. */
  grid-template-columns: 22px 1fr auto auto auto auto;
  align-items: center;
  gap: 8px;
  padding: 7px 10px 7px 8px;
  margin: 1px 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 13.5px;
  color: var(--ink-2);
  position: relative;
  transition: background .12s ease;
}
.chapter-item > .chapter-number,
.chapter-item > .chapter-bullet { grid-column: 1; }
.chapter-item > .chapter-name { grid-column: 2; }
.chapter-item > .chapter-status-dot { grid-column: 3; }
.chapter-item > .chapter-comments-badge { grid-column: 4; }
.chapter-item > .chapter-words { grid-column: 5; }
.chapter-item > .chapter-delete { grid-column: 6; }
.chapter-item:hover { background: var(--bg-rail-hover); }
/* Find-in-book match count — a leading accent pill (Atticus-style) shown
   on each chapter while a whole-book search is active. Absolutely placed in
   the gutter; .has-find-badge shifts the row's grid content right for it. */
.chapter-item.has-find-badge { padding-left: 30px; }
.chapter-find-badge {
  position: absolute;
  left: 4px;
  top: 50%;
  transform: translateY(-50%);
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  border-radius: 9px;
  background: var(--accent);
  color: #fff;
  font-size: 10.5px;
  font-weight: 700;
  font-style: normal;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  z-index: 1;
}
.chapter-item.is-active {
  background: #fff;
  color: var(--ink-1);
  font-weight: 500;
  box-shadow: 0 1px 2px rgba(0,0,0,.04);
}
.chapter-item.is-active::before {
  content: '';
  position: absolute;
  left: -8px;
  top: 6px;
  bottom: 6px;
  width: 3px;
  background: var(--accent);
  border-radius: 0 2px 2px 0;
}
.chapter-number {
  font-family: var(--font-serif);
  font-style: italic;
  color: var(--ink-4);
  font-size: 12px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.chapter-bullet {
  color: var(--ink-4);
  font-size: 14px;
  text-align: center;
  line-height: 1;
}
.chapter-item.is-active .chapter-number,
.chapter-item.is-active .chapter-bullet { color: var(--accent); }
.chapter-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chapter-name-input {
  width: 100%;
  font-family: inherit;
  font-size: inherit;
  color: var(--ink-1);
  background: #fff;
  border: 1px solid var(--accent);
  border-radius: 3px;
  padding: 2px 6px;
  margin: -3px 0;
  outline: none;
  box-shadow: 0 0 0 3px rgba(139,105,20,.12);
}
.chapter-words {
  font-size: 11px;
  color: var(--ink-4);
  font-variant-numeric: tabular-nums;
}
/* Sidebar comment-count bubble. Neutral = author's own unresolved notes;
   .has-beta = at least one beta-reader comment in the bunch (gold tint). */
.chapter-comments-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  margin-left: 4px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  background: var(--bg-rail);
  color: var(--ink-2);
  border: 1px solid var(--line-soft);
  flex-shrink: 0;
}
.chapter-comments-badge.has-beta {
  background: rgba(139,105,20,.16);
  color: var(--accent);
  border-color: rgba(139,105,20,.32);
}
.chapter-item.is-active .chapter-comments-badge {
  background: rgba(255,255,255,.18);
  color: #fff;
  border-color: rgba(255,255,255,.25);
}
.chapter-item.is-active .chapter-comments-badge.has-beta {
  background: rgba(255,235,150,.85);
  color: #6e4f14;
  border-color: rgba(255,235,150,1);
}

/* Writing-stage indicator. Same color vocabulary as the corkboard pill;
   click cycles through none → draft → revising → revised → final. */
.chapter-status-dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1.5px solid var(--ink-4);
  background: transparent;
  padding: 0;
  cursor: pointer;
  flex-shrink: 0;
  opacity: .55;
  transition: opacity .12s ease, transform .12s ease, box-shadow .12s ease, background .12s ease, border-color .12s ease;
}
.chapter-status-dot:hover {
  opacity: 1;
  transform: scale(1.2);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
/* Keyboard focus must be visible — the dot is an interactive control that
   cycles the writing stage. */
.chapter-status-dot:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  opacity: 1;
}
/* State is signalled by colour AND a filled/outlined shape so it doesn't
   rely on hue alone: unset stages stay hollow, set stages fill. Draft is
   darkened for contrast against the pale sidebar. */
.chapter-status-dot[data-current="draft"]    { background: #9aa1ac; border-color: #656e7a; opacity: 1; }
.chapter-status-dot[data-current="revising"] { background: #f0a253; border-color: #b3670f; opacity: 1; }
.chapter-status-dot[data-current="revised"]  { background: #b27dd8; border-color: #6c4494; opacity: 1; }
.chapter-status-dot[data-current="final"]    { background: #6cba6c; border-color: #337333; opacity: 1; }
.chapter-item.is-active .chapter-status-dot[data-current="none"] { border-color: var(--accent); }
.chapter-delete {
  width: 22px; height: 22px;
  border: none;
  background: transparent;
  color: var(--ink-4);
  font-size: 18px;
  line-height: 1;
  border-radius: 3px;
  cursor: pointer;
  opacity: 0;
  padding: 0;
}
/* Reveal on hover, but also on keyboard focus and whenever anything in the
   row is focused — otherwise the control is mouse-only and unreachable by
   keyboard or touch. */
.chapter-item:hover .chapter-delete,
.chapter-item:focus-within .chapter-delete,
.chapter-delete:focus-visible { opacity: 1; }
.chapter-delete:focus-visible {
  outline: 2px solid var(--danger);
  outline-offset: 1px;
}
.chapter-delete:hover { background: var(--danger); color: #fff; }
.chapter-item.is-dragging { opacity: .5; }
.chapter-item.drag-over { box-shadow: 0 -2px 0 var(--accent); }

.sidebar-empty-row {
  list-style: none;
  padding: 6px 12px;
  font-size: 11.5px;
  color: var(--ink-4);
  font-style: italic;
}

.sidebar-footer {
  padding: 10px;
  border-top: 1px solid var(--line);
  background: var(--bg-rail);
}
.btn-add-chapter {
  width: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 12px;
  background: var(--ink-1);
  color: #fff;
  border: none;
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: background .12s ease;
}
.btn-add-chapter:hover { background: #000; }
.btn-add-chapter .plus {
  font-size: 16px; line-height: 1; font-weight: 600;
}
.btn-renumber-chapters {
  width: 100%;
  margin-top: 6px;
  padding: 7px 12px;
  background: transparent;
  color: var(--ink-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 500;
  cursor: pointer;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.btn-renumber-chapters:hover { background: var(--bg-1); color: var(--ink-1); border-color: var(--ink-4); }
.btn-renumber-chapters:disabled { opacity: .5; cursor: default; }

/* ----- Preset popover (opens off the section "+" buttons) ----- */
.preset-popover {
  position: absolute;
  z-index: 250;
  min-width: 240px;
  max-width: 300px;
  background: #fff;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  font-size: 13px;
  overflow: hidden;
  animation: preset-pop-in .12s ease-out;
}
@keyframes preset-pop-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.preset-popover-head {
  padding: 9px 14px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--ink-3);
  background: var(--bg-rail);
  border-bottom: 1px solid var(--line);
}
.preset-popover-list {
  list-style: none;
  margin: 0;
  padding: 4px 0;
  max-height: 380px;
  overflow-y: auto;
}
.preset-popover-item {
  display: block;
  padding: 8px 14px;
  cursor: pointer;
  border-left: 3px solid transparent;
}
.preset-popover-item:hover,
.preset-popover-item:focus {
  background: var(--bg-rail);
  border-left-color: var(--accent);
  outline: none;
}
.preset-popover-label {
  display: block;
  font-weight: 500;
  color: var(--ink-1);
  line-height: 1.3;
}
.preset-popover-desc {
  display: block;
  font-size: 11.5px;
  color: var(--ink-3);
  margin-top: 2px;
  line-height: 1.3;
}
/* "My templates" sub-heading — a second labelled group under the presets. */
.preset-popover-subhead {
  border-top: 1px solid var(--line);
}
/* Saved-template rows carry an inline delete (×); lay them out as a row. */
.preset-popover-item[data-template-id] {
  display: flex;
  align-items: center;
  gap: 8px;
}
.preset-popover-item[data-template-id] .preset-popover-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.preset-popover-del {
  flex: 0 0 auto;
  border: none;
  background: transparent;
  color: var(--ink-3);
  font-size: 16px;
  line-height: 1;
  width: 22px;
  height: 22px;
  border-radius: 4px;
  cursor: pointer;
  opacity: .55;
}
.preset-popover-del:hover,
.preset-popover-del:focus {
  opacity: 1;
  color: var(--danger, #c0392b);
  background: rgba(0, 0, 0, .06);
  outline: none;
}

/* ----- "Save as template" in the Chapter-properties rail panel ----- */
.inspector-template-tools {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}
.btn-save-template {
  display: inline-block;
  padding: 7px 12px;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-1);
  background: var(--bg-rail);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  cursor: pointer;
}
.btn-save-template:hover,
.btn-save-template:focus {
  border-color: var(--accent);
  color: var(--accent);
  outline: none;
}

/* ----- Email verification status chips (settings + admin users) ----- */
.email-status {
  display: inline-flex;
  align-items: center;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .04em;
  vertical-align: middle;
}
.email-status-ok {
  background: #e6f5ea;
  color: #1e6b35;
  border: 1px solid #c4e4cf;
}
.email-status-pending {
  background: #fbf1cf;
  color: #6b5210;
  border: 1px solid #efdf95;
}
:root[data-theme="dark"] .email-status-ok      { background: rgba(80,160,80,.18); color: #84b66a; border-color: rgba(80,160,80,.35); }
:root[data-theme="dark"] .email-status-pending { background: rgba(200,160,60,.18); color: #d4a64f; border-color: rgba(200,160,60,.35); }

/* ----- Auth-page extras (forgot/reset) ----- */
.auth-prose {
  font-family: var(--font-serif);
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--ink-2);
  margin: 8px 0 18px;
}
.auth-link {
  color: var(--accent);
  text-decoration: none;
  font-weight: 500;
}
.auth-link:hover { text-decoration: underline; }
.auth-footer-sep {
  margin: 0 8px;
  color: var(--ink-4);
}
.alert-ok {
  background: #e6f5ea;
  color: #1e6b35;
  border: 1px solid #c4e4cf;
  padding: 10px 14px;
  border-radius: 6px;
  font-size: 13.5px;
}
:root[data-theme="dark"] .alert-ok { background: rgba(80,160,80,.12); color: #b6dca0; border-color: rgba(80,160,80,.3); }

/* ----- Settings page inline form bits (account / email) ----- */
.settings-inline-form { display: flex; flex-direction: column; gap: 12px; }
.settings-inline-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* ----- Manuscript (center) ----- */
.manuscript {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--bg-page);
  --editor-zoom: 1;
}
.page-stage {
  flex: 1;
  overflow-y: auto;
  padding: 0 40px 80px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* ---- Writing-stage backdrop (v2.17): the library band artwork behind
   the page sheet, toggled from the rail. An absolute layer inside
   .manuscript (which is position:relative + overflow:hidden, so it's
   clipped to the writing column and never fights the topbar/sidebar/
   rail); #page-stage lifts to z1 so the sheet, sticky toolbar and
   overlays all paint above it. The art stays put while the paper
   scrolls — the layer sits outside the scroller. ---- */
.editor-backdrop {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
}
.editor-backdrop img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  display: block;
}
/* Dim the art so the paper stays the brightest thing on screen — the
   sheet is the light source, the art is the room. Deeper in dark mode. */
.editor-backdrop::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(12,9,6,.28) 0%, rgba(12,9,6,.44) 100%);
}
:root[data-theme="dark"] .editor-backdrop::after {
  background: linear-gradient(180deg, rgba(10,8,6,.45) 0%, rgba(10,8,6,.62) 100%);
}
.manuscript.has-backdrop .page-stage {
  position: relative;
  z-index: 1;
}
/* The footer word-count bar is an in-flow sibling of the absolute z0 art
   layer, and positioned elements paint above in-flow content — so with a
   backdrop on, the art covered the whole bar. Same lift as #page-stage;
   the bar is opaque (#fff + border) so it reads as a content island. */
.manuscript.has-backdrop .manuscript-footer {
  position: relative;
  z-index: 1;
}
/* Distraction-free means exactly that. */
.editor-shell.is-focus-mode .editor-backdrop { display: none !important; }

/* Fixed-position variant: whole-page backdrop for regular scrolling
   pages (Book details). Content lifts above it per page below. */
.editor-backdrop.is-fixed {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.details-body .editor-topbar { position: relative; z-index: 2; }
.details-body .details-main  { position: relative; z-index: 1; }

/* Quill toolbar host — pinned at the top of the manuscript area */
.editor-toolbar-host {
  position: sticky;
  top: 0;
  z-index: 6;
  width: 100%;
  max-width: 760px;
  background: var(--bg-page);
  padding: 10px 0 8px;
}
.editor-toolbar-host .ql-toolbar.ql-snow {
  position: static;
  margin: 0;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 6px 10px;
  box-shadow: 0 1px 3px rgba(0,0,0,.04);
}

.page-sheet {
  /* Text-feature colour aliases — see #preview-stage for the pair. The
     editor has no per-book accent, so it lends its own paper palette. */
  --tf-accent: var(--accent, #8b6914);
  --tf-text:   var(--ink-1, #1a1a1a);
  --tf-bg:     var(--bg-paper, #fdfbf6);
  width: 100%;
  max-width: 760px;
  background: var(--bg-paper);
  border: 1px solid var(--line);
  border-radius: 2px;
  box-shadow: var(--shadow-paper);
  padding: 56px 72px 80px;
  /* `.page-stage` is a flex column. Default `flex-shrink: 1` would
     compress the page-sheet when content exceeds the visible area,
     which makes the cream background end mid-chapter. Pin it open. */
  flex-shrink: 0;
  height: auto;
  min-height: 100%;
  margin-top: 14px;
  transform: scale(var(--editor-zoom));
  transform-origin: top center;
}
.page-sheet[hidden] { display: none; }
.page-sheet-header { margin-bottom: 12px; position: relative; }
.import-review-row { border: 1px solid var(--line); padding: 12px; margin: 12px 0; border-radius: 8px; }
.import-review-row .input { display: block; width: 100%; margin: 8px 0; }
.import-review-preview { width: 100%; height: 320px; border: 1px solid var(--line); background: white; }
.import-review-row summary { cursor: pointer; padding: 10px 0; }
.chapter-last-edited { text-align: right; padding-right: 34px; margin-bottom: 12px; font: 12px/1.5 var(--font-ui, sans-serif); color: var(--ink-3); user-select: none; }
.chapter-last-edited[hidden], .editor-shell.is-focus-mode .chapter-last-edited { display: none !important; }
.page-sheet.is-invert-colors .chapter-last-edited { color: #c8c3b6; }
@media print { .chapter-last-edited { display: none !important; } }

/* ---------- Chapter display gear (top-right of the page sheet) ---------- */
.chapter-settings-btn {
  position: absolute;
  top: -6px;
  right: -6px;
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 50%;
  color: var(--ink-3);
  cursor: pointer;
  padding: 0;
  z-index: 4;
}
.chapter-settings-btn:hover { color: var(--ink-1); background: var(--bg-rail); border-color: var(--line); }
.chapter-settings-popover {
  position: absolute;
  top: 32px;
  right: -6px;
  z-index: 220;
  width: 270px;
  background: #fff;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  font-size: 13px;
  animation: preset-pop-in .12s ease-out;
}
.chapter-settings-popover[hidden] { display: none; }
.chapter-settings-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px 10px;
  border-bottom: 1px solid var(--line);
}
.chapter-settings-title {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--ink-2);
  margin: 0;
}
.chapter-settings-body {
  padding: 12px 14px 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Opening background image block in the gear popover (migration 103). */
.chapter-opening-image {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--line-soft);
}
.chapter-opening-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.chapter-opening-thumb {
  width: 48px;
  height: 64px;
  object-fit: cover;
  border-radius: 4px;
  border: 1px solid var(--line);
  flex: 0 0 auto;
}
.chapter-opening-actions { display: flex; gap: 6px; flex-wrap: wrap; }

/* Fade sliders for the opening spread (migration 106). Two-line rows —
   label + % on top, full-width slider beneath — so nothing can overflow
   the narrow popover (the one-line flex layout ran past its edge: range
   inputs refuse to shrink below their intrinsic width in some engines). */
.chapter-opening-fades { margin-top: 10px; display: grid; gap: 8px; }
.chapter-fade-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 2px 8px;
  font-size: 12px;
  min-width: 0;
}
.chapter-fade-label { color: var(--muted, #6b6257); }
.chapter-fade-val { text-align: right; font-variant-numeric: tabular-nums; color: var(--muted, #6b6257); }
.chapter-fade-row input[type="range"] {
  grid-column: 1 / -1;
  width: 100%;
  min-width: 0;
  margin: 0;
}

/* Sidebar glyph for full-page image chapters — replaces the word count. */
/* Full-page-image rows: labeled IMAGE tag (the old emoji glyph was an
   unreadable monochrome box in dark mode) + italic title so plates are
   unmistakable next to text chapters. */
.chapter-plate-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--accent-bg);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-radius: 999px;
  padding: 2px 7px 2px 5px;
  line-height: 1;
}
.chapter-item.is-plate-row .chapter-name { font-style: italic; color: var(--ink-2); }

/* ---------- Per-chapter display overrides ---------- */
/* Live editor preview — hide heading not relevant (heading lives outside Quill),
   but invert and smaller-title can be felt in the editor too. */
.page-sheet.is-invert-colors {
  background: #1a1715;
  color: #f1ead2;
  border-color: #2a2723;
}
.page-sheet.is-invert-colors .chapter-title-input,
.page-sheet.is-invert-colors .chapter-subtitle-input,
.page-sheet.is-invert-colors .ql-editor {
  color: #f1ead2;
}
.page-sheet.is-invert-colors .ql-editor blockquote { color: #d6cdac; }
.page-sheet.is-invert-colors .chapter-settings-btn { color: #c8c3b6; }
.page-sheet.is-smaller-title .chapter-title-input { font-size: 18px; }

/* Hide-chapter-heading: dim the title + subtitle inputs so the author
   sees at-a-glance that they won't appear in the published book.
   (Inputs stay editable — this is purely visual feedback.) */
.page-sheet.is-hide-heading .chapter-title-input,
.page-sheet.is-hide-heading .chapter-subtitle-input {
  opacity: .35;
}

/* Faux drop cap inside the editor, mirroring what the published theme
   will render. Gated by data-theme-drop-cap (set from the theme's
   drop_cap setting) so turning the drop cap off in Formatting removes it
   here too — and by :not(.is-hide-drop-cap) so a single chapter can still
   suppress it. Uses the theme's drop-cap font when one is set. */
.page-sheet[data-theme-drop-cap="1"]:not(.is-hide-drop-cap) .ql-editor p:first-child::first-letter {
  font-family: var(--editor-drop-cap-font, var(--editor-heading-font, var(--font-display)));
  font-weight: 600;
  font-size: 2.6em;
  line-height: .85;
  float: left;
  padding: .04em .1em 0 0;
  color: inherit;
}
.page-sheet.is-hide-drop-cap .ql-editor p:first-child::first-letter { font-size: inherit; float: none; padding: 0; color: inherit; }

/* Preview view */
.preview-page.is-invert-colors {
  background: #1a1715;
  color: #f1ead2;
  --pv-text-color: #f1ead2;
  --pv-bg-color: #1a1715;
}
.preview-page.is-invert-colors .chapter-title,
.preview-page.is-invert-colors .chapter-num-label { color: #f1ead2; }
/* NB: :not(.is-continued) is here to KEEP THE MARGIN. This rule cancels the
   drop cap ~7000 lines below and wins on specificity alone; that rule carries
   the same guard, so dropping it here would level the two and let the later
   one win. Continuation pages take no drop cap either way. */
.preview-stage[data-drop-cap="1"] .preview-page.is-hide-drop-cap[data-kind="chapter"]:not(.is-continued) .chapter-body p:first-of-type::first-letter {
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
  float: none;
  padding: 0;
  color: inherit;
  font-weight: inherit;
}

body[data-page-width="narrow"]   .page-sheet { max-width: 620px; }
body[data-page-width="wide"]     .page-sheet { max-width: 880px; }
body[data-page-width="narrow"]   .editor-toolbar-host { max-width: 620px; }
body[data-page-width="wide"]     .editor-toolbar-host { max-width: 880px; }

.page-sheet-empty {
  width: 100%;
  max-width: 720px;
  background: var(--bg-paper);
  border: 1px solid var(--line);
  border-radius: 2px;
  box-shadow: var(--shadow-paper);
  padding: 80px 72px;
  min-height: 70vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.page-sheet-empty[hidden] { display: none; }

/* ----- Auto title-page panel (replaces Quill for auto front_matter title_page) ----- */
.page-sheet-auto[hidden] { display: none; }
.page-sheet-auto {
  /* Inherits .page-sheet width / shadow / radius — just override the inner
     padding so the banner can hug the top edge. */
  padding: 0;
}
.auto-page-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 24px;
  background: #fff8e6;
  border-bottom: 1px solid #f1e3b8;
  border-top-left-radius: 2px;
  border-top-right-radius: 2px;
}
.auto-page-banner-text {
  margin: 0;
  font-size: 13px;
  color: #6e5818;
  line-height: 1.5;
  flex: 1;
  min-width: 0;
}
.auto-page-banner .btn { flex-shrink: 0; white-space: nowrap; }
.auto-page-body { padding: 56px 72px; }
.auto-page-title {
  font-family: var(--font-display);
  font-size: 28px;
  font-weight: 600;
  margin: 0 0 32px;
  color: var(--ink-1);
}
.auto-page-fields {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 22px;
}
.auto-page-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  border-bottom: 1px solid var(--line-soft);
  padding-bottom: 14px;
}
.auto-page-field:last-child { border-bottom: none; padding-bottom: 0; }
.auto-page-field dt {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--ink-3);
  margin: 0;
}
.auto-page-field dd {
  margin: 0;
  font-size: 16px;
  color: var(--ink-1);
  font-family: var(--font-sans);
  word-break: break-word;
}
.auto-page-field dd:empty::before {
  content: '—';
  color: var(--ink-4);
}
.auto-page-help {
  font-size: 14px;
  color: var(--ink-3);
  margin: 0 0 18px;
}
.auto-page-hint {
  font-size: 13px;
  color: var(--ink-4);
  font-style: italic;
  margin: 18px 0 0;
}
.auto-page-toc-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.auto-page-toc-item {
  padding: 8px 0;
  border-bottom: 1px dotted var(--line-soft);
  font-size: 15px;
  color: var(--ink-1);
}
.auto-page-toc-item:last-child { border-bottom: none; }
.auto-page-toc-item.is-front-matter,
.auto-page-toc-item.is-back-matter { font-style: italic; color: var(--ink-2); }
.auto-page-toc-item.is-part {
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--ink-1);
}
.empty-mark {
  font-family: var(--font-serif);
  font-size: 36px;
  color: var(--accent);
  margin-bottom: 18px;
  opacity: .85;
}
.empty-heading {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 500;
  color: var(--ink-1);
  margin: 0 0 10px;
  letter-spacing: .01em;
}
.empty-sub {
  font-family: var(--font-serif);
  font-size: 15px;
  color: var(--ink-3);
  max-width: 380px;
  margin: 0 0 28px;
  line-height: 1.6;
}
.chapter-title-input {
  width: 100%;
  border: none;
  background: transparent;
  font-family: var(--editor-heading-font, var(--font-display));
  font-size: 28px;
  font-weight: 600;
  color: var(--ink-1);
  margin-bottom: 4px;
  padding: 4px 0;
  letter-spacing: .01em;
}
.chapter-title-input::placeholder { color: var(--ink-4); font-style: italic; font-weight: 400; }
.chapter-title-input:focus { outline: none; }
.chapter-subtitle-input {
  width: 100%;
  border: none;
  background: transparent;
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 15px;
  color: var(--ink-3);
  padding: 0 0 8px;
}
.chapter-subtitle-input::placeholder { color: var(--ink-4); }
.chapter-subtitle-input:focus { outline: none; }

/* Quill overrides — paper-like reading area.
   Quill's snow theme gives .ql-editor `height: 100%; overflow-y: auto` so
   it scrolls inside its own container. We don't want that — we want the
   editor to grow with its content and let the outer .page-stage handle
   all scrolling. Otherwise long chapters render below the cream paper
   sheet boundary. */
.ql-container.ql-snow {
  border: none;
  /* Body text follows the theme's body font (falls back to the editor
     default) so the writing surface previews the published typeface. */
  font-family: var(--editor-body-font, var(--font-serif));
  font-size: 17px;
  line-height: 1.65;
  color: var(--ink-1);
  height: auto;
}
.ql-editor {
  padding: 0;
  min-height: 60vh;
  height: auto;
  overflow: visible;
}
.ql-editor p { margin: 0 0 .8em; text-indent: 0; }
.ql-editor p + p { text-indent: 1.5em; margin-top: 0; }
/* A paragraph that follows a scene break (dinkus) or page break is still a
   continuing body paragraph, not the start of a new section, so it keeps its
   first-line indent — matching print/export, the main preview, and the share
   reader (which indent every paragraph except the chapter's very first). The
   break's <div>/<hr> otherwise breaks the `p + p` chain above and the next
   paragraph would wrongly lose its indent. */
.ql-editor .scene-break + p,
.ql-editor hr.page-break + p,
.ql-editor figure.book-figure + p { text-indent: 1.5em; }
/* Same chain break, one more shape (v2.65): a text feature is a <div>, so
   the paragraph after it is not `p + p` and would silently lose its indent
   — which would make the gear's two "Format next paragraph as" choices
   look identical. `continue` is the case that genuinely wants no indent:
   the quotation interrupted a paragraph and the prose resumes. */
.ql-editor .tf + p { text-indent: 1.5em; }
.ql-editor .tf[data-tf-next="continue"] + p { text-indent: 0; }

/* ---- Text features in the editor: STRUCTURE ONLY ----
   Everything that varies by preset — the insets, the alignment, the
   italics, the type size, the em dash — is compiled by
   TextFeatureStyle::forBrowserAllPresets() and emitted into the page by
   editor.php, keyed on .page-sheet[data-blockquote-style]. It is NOT
   repeated here.

   That is deliberate. An earlier draft of this block hand-wrote the default
   preset so a quotation would be legible before the compiler existed — and
   the moment the compiler shipped, that turned into exactly the thing this
   subsystem exists to abolish: a second, hand-maintained copy of one preset
   that nothing would keep in step. What is left below is only the shape
   that is true of EVERY preset.

   text-indent: 0 is not cosmetic and is the reason quotations looked wrong
   everywhere: `.ql-editor p + p` gives every paragraph a first-line indent,
   so without this reset the second line of a quotation is indented and so
   is the attribution. It carries !important because it has to beat the
   per-paragraph override at [data-noindent="0"] too — a text feature line
   is never body prose. */
.ql-editor .tf { margin: 1em 0; }
.ql-editor .tf-line,
.ql-editor .tf-attrib { margin: 0; text-indent: 0 !important; }
/* A stanza break is an EMPTY tf-line, which Quill renders as
   <p class="tf-line"><br></p>. :empty will not match that — the <br> is a
   child — so nothing here or in any compiler may use it. */

/* Per-paragraph first-line indent (v2.65).
   !important is deliberate and is the whole point of the feature: an
   explicit per-paragraph choice has to beat all four of the things that
   otherwise decide the indent — the paragraph's position, the book-wide
   theme setting, the per-chapter override, and the p + p chain — several
   of which carry higher specificity than any selector available here. An
   ABSENT data-noindent matches nothing, so an untouched paragraph still
   follows the book. */
.ql-editor [data-noindent="1"] { text-indent: 0 !important; }
.ql-editor [data-noindent="0"] { text-indent: 1.5em !important; }

/* Horizontal line. Distinct from hr.page-break, which marks a PAGE break
   and is drawn as a labelled pill rather than a rule.

   THE HIT AREA IS A PSEUDO-ELEMENT, AND THAT IS THE POINT. Drawn the
   obvious way — height: 0 with a border-top — the element the mouse has
   to find is ONE PIXEL tall, so the hover-delete button could never be
   summoned and the line read as undeletable.

   Giving the <hr> a real height fixes the target and moves the line: an
   18px box plus its margins is 6px taller than a hairline plus its
   margins, and margin collapsing means you cannot simply subtract half
   the height from each side (the top margin collapses with the
   paragraph's bottom margin and the bottom one does not). MEASURED, not
   reasoned about — it pushed the line 6.9px down the page.

   So the box stays zero-height and an absolutely-positioned ::after
   supplies the target. Hit-testing attributes a pseudo-element's hits to
   the element that owns it, so `closest('hr.tf-rule')` matches. Layout is
   byte-identical to before, and the overlay stays inside the surrounding
   paragraph margins so it never steals a click meant for the text. */
.ql-editor hr.tf-rule {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 1.1em 0;
  height: 0;
  background: none;
  position: relative;
  overflow: visible;
}
.ql-editor hr.tf-rule::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: -8px;
  height: 17px;
}
/* Inside a text feature it divides the block rather than the chapter, so
   it sits closer and does not need a full paragraph gap. The .7em gap is
   tighter than the chapter one, so the target narrows to match rather
   than overlapping the lines on either side. */
.ql-editor .tf hr.tf-rule { margin: .7em 0; }
.ql-editor .tf hr.tf-rule::after { top: -5px; height: 11px; }
/* Same chain break as the scene break and the tf container: an <hr> is not
   a <p>, so the paragraph after one would silently lose its indent. */
.ql-editor hr.tf-rule + p { text-indent: 1.5em; }

/* The same rule on the other browser surfaces — the live preview and the
   beta-reader share page. Without these an <hr> falls back to the browser's
   default inset grey bevel, which is not what the editor or the print proof
   shows. (PdfExporter and EpubExporter draw their own; the preset-driven
   version of all four arrives with TextFeatureStyle.) */
.preview-page .chapter-body hr.tf-rule,
.share-reader-page hr.tf-rule {
  border: 0;
  border-top: 1px solid currentColor;
  opacity: .45;
  margin: 1.1em 0;
  height: 0;
  background: none;
}
.preview-page .chapter-body .tf hr.tf-rule,
.share-reader-page .tf hr.tf-rule { margin: .7em 0; }
.preview-page .chapter-body hr.tf-rule + p,
.share-reader-page hr.tf-rule + p { text-indent: var(--pv-indent, 1.5em); }
/* Per-chapter "Indent first paragraph" override — when the chapter gear
   toggle is on, restore the indent on the first paragraph so the editor
   matches what print/EPUB will render. */
.page-sheet.is-indent-first .ql-editor p:first-child { text-indent: 1.5em; }
/* Theme-level default: indent every chapter's first paragraph. Mirrors the
   per-chapter override above but is driven by the theme, so it carries to
   all chapters without ticking the box on each one. */
.page-sheet[data-theme-indent-first="1"] .ql-editor p:first-child { text-indent: 1.5em; }
.ql-editor h1, .ql-editor h2, .ql-editor h3 {
  font-family: var(--editor-heading-font, var(--font-display));
  font-weight: 600;
  color: var(--ink-1);
  margin: 1.4em 0 .5em;
}
.ql-editor h1 { font-size: 28px; }
.ql-editor h2 { font-size: 22px; }
.ql-editor h3 { font-size: 18px; }
.ql-editor blockquote {
  border-left: 3px solid var(--accent);
  padding: 4px 18px;
  margin: 1em 0;
  font-family: var(--font-serif);
  font-style: italic;
  color: var(--ink-2);
}
/* ---------- Scene break (visual divider in the editor) ----------
 * The saved HTML is just <div class="scene-break"></div>. The visual
 * (asterisks / flourish / etc.) is rendered via a ::before pseudo-
 * element whose content depends on the page-sheet's
 * data-scene-break-style attribute. Editor.js syncs that attribute
 * with the book's current scene_break_style on init and on changes
 * from the formatting tab.
 */
.ql-editor .scene-break {
  display: block;
  text-align: center;
  margin: 1.4em 0;
  color: var(--accent);
  font-family: var(--font-display);
  font-size: 1.1em;
  letter-spacing: .25em;
  user-select: none;
  cursor: default;
  min-height: 1.5em;
  position: relative;
}
.ql-editor .scene-break::before { content: '*  *  *'; } /* asterisks default */
.page-sheet[data-scene-break-style="asterisks"] .ql-editor .scene-break::before { content: '*  *  *'; letter-spacing: .25em; }
.page-sheet[data-scene-break-style="stars"]     .ql-editor .scene-break::before { content: '★  ★  ★'; font-size: .8em; letter-spacing: .25em; }
.page-sheet[data-scene-break-style="dots"]      .ql-editor .scene-break::before { content: '•  •  •'; font-size: .9em; letter-spacing: .25em; }
.page-sheet[data-scene-break-style="diamonds"]  .ql-editor .scene-break::before { content: '◆  ◆  ◆'; font-size: .65em; letter-spacing: .3em; }
.page-sheet[data-scene-break-style="asterism"]  .ql-editor .scene-break::before { content: '⁂'; font-size: 1.3em; letter-spacing: 0; }
.page-sheet[data-scene-break-style="ornament-line"] .ql-editor .scene-break::before { content: '─── ❖ ───'; font-size: .9em; letter-spacing: 0; }
.page-sheet[data-scene-break-style="hearts"]    .ql-editor .scene-break::before { content: '♥  ♥  ♥'; font-size: .7em; letter-spacing: .25em; }
.page-sheet[data-scene-break-style="celestial"] .ql-editor .scene-break::before { content: '☾ ✦ ☽'; font-size: .9em; letter-spacing: 0; }
.page-sheet[data-scene-break-style="daggers"]   .ql-editor .scene-break::before { content: '†  ‡  †'; font-size: .9em; letter-spacing: .25em; }
.page-sheet[data-scene-break-style="florettes"] .ql-editor .scene-break::before { content: '❀  ❀  ❀'; font-size: .8em; letter-spacing: .25em; }
.page-sheet[data-scene-break-style="fleuron-line"] .ql-editor .scene-break::before { content: '── ❦ ──'; font-size: .9em; letter-spacing: 0; }
.page-sheet[data-scene-break-style="flourish"]  .ql-editor .scene-break::before { content: '❦'; font-size: 1.5em; letter-spacing: 0; }
.page-sheet[data-scene-break-style="rune"]      .ql-editor .scene-break::before { content: '✦'; font-size: 1.4em; letter-spacing: 0; }
.page-sheet[data-scene-break-style="dingbat"]   .ql-editor .scene-break::before { content: '❧'; font-size: 1.5em; letter-spacing: 0; }
.page-sheet[data-scene-break-style="thin-line"] .ql-editor .scene-break {
  letter-spacing: 0;
}
.page-sheet[data-scene-break-style="thin-line"] .ql-editor .scene-break::before {
  content: '';
  display: block;
  height: 1px;
  background: currentColor;
  width: 80px;
  margin: .8em auto;
  opacity: .35;
}
.page-sheet[data-scene-break-style="double-line"] .ql-editor .scene-break {
  letter-spacing: 0;
}
.page-sheet[data-scene-break-style="double-line"] .ql-editor .scene-break::before {
  content: '';
  display: block;
  height: 0;
  border-top: 3px double currentColor;
  width: 120px;
  margin: .8em auto;
  opacity: .5;
}
.page-sheet[data-scene-break-style="dotted-line"] .ql-editor .scene-break {
  letter-spacing: 0;
}
.page-sheet[data-scene-break-style="dotted-line"] .ql-editor .scene-break::before {
  content: '';
  display: block;
  height: 0;
  border-top: 2px dotted currentColor;
  width: 120px;
  margin: .8em auto;
  opacity: .5;
}

/* Floating trash icon — appears on hover over a scene-break in the editor.
 * Positioned absolutely against the document; editor.js sets top/left.
 * Stays clear of Quill's content tree so saved HTML stays clean.
 */
.scene-break-delete-floater {
  position: absolute;
  z-index: 200;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--danger);
  cursor: pointer;
  box-shadow: var(--shadow-1);
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.scene-break-delete-floater:hover {
  background: var(--danger);
  color: #fff;
  border-color: var(--danger);
}

/* Figure hover toolbar (v2.53) — same body-level floater architecture. */
.figure-toolbar-floater {
  position: absolute;
  z-index: 200;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: var(--shadow-1);
}
.figure-toolbar-floater[hidden] { display: none; }
.figure-toolbar-floater button {
  border: 0;
  background: none;
  padding: 3px 7px;
  border-radius: 5px;
  font-size: 13px;
  line-height: 1;
  color: var(--ink-2);
  cursor: pointer;
}
.figure-toolbar-floater button:hover { background: var(--accent-soft); color: var(--ink-1); }
.figure-toolbar-floater [data-fig-act="delete"]:hover { background: var(--danger); color: #fff; }
.figure-toolbar-floater .figure-convert-btn { font-size: 12px; font-weight: 600; }

/* Figure edit modal bits. */
.figure-edit-card { max-width: 420px; }
.figure-seg { display: inline-flex; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
.figure-seg-btn {
  border: 0;
  background: var(--bg-paper);
  padding: 6px 12px;
  font: inherit;
  font-size: 12.5px;
  color: var(--ink-2);
  cursor: pointer;
}
.figure-seg-btn + .figure-seg-btn { border-left: 1px solid var(--line); }
.figure-seg-btn.is-active { background: var(--accent); color: #fff; }
.figure-seg-btn:disabled { opacity: .45; cursor: not-allowed; }
.figure-size-row { display: flex; align-items: center; gap: 10px; }
.figure-size-row input[type="range"] { flex: 1; }
.figure-size-readout { min-width: 44px; text-align: right; font-variant-numeric: tabular-nums; color: var(--ink-2); }
.scene-break-delete-floater[hidden] { display: none; }

/* Chapter images uploaded via the toolbar — keep them within the page width.
   (Legacy bare <img> only — figure images below carry their own rules.) */
.ql-editor img {
  max-width: 100%;
  height: auto;
  display: block;
  margin: 0.6em auto;
  border-radius: 2px;
}

/* Inline figures (v2.53) — the canonical book-figure block. The same
   shape is repeated (values adapted) in print.php, EpubExporter::buildCss,
   PdfExporter (as translated divs) and the preview/share stylesheets:
   data-* attributes carry the truth, --fig-w carries the width. */
.ql-editor figure.book-figure {
  width: var(--fig-w, 100%);
  margin: 1em auto;
  text-align: center;
  page-break-inside: avoid;
  break-inside: avoid;
}
.ql-editor figure.book-figure img {
  display: block;
  width: 100%;
  max-width: none;   /* width comes from the figure, not the legacy img rule */
  height: auto;
  margin: 0;
}
.ql-editor figure.book-figure[data-align="left"]  { margin-left: 0; }
.ql-editor figure.book-figure[data-align="right"] { margin-right: 0; }
.ql-editor figure.book-figure[data-wrap="left"]  { float: left;  margin: .2em 1.4em .8em 0; }
.ql-editor figure.book-figure[data-wrap="right"] { float: right; margin: .2em 0 .8em 1.4em; }
.ql-editor figure.book-figure figcaption {
  font-size: .85em;
  font-style: italic;
  opacity: .85;
  text-align: center;
  margin-top: .4em;
  text-indent: 0;
}
/* A floated figure at the end of a chapter must not bleed past the sheet. */
.ql-editor::after { content: ""; display: block; clear: both; }
/* Make the figure read as an editable object. */
.ql-editor figure.book-figure { cursor: pointer; }
.ql-editor figure.book-figure:hover {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}

/* ---------- Comment anchor highlight (in the editor) ---------- */
.ql-editor .cmt {
  background: rgba(252, 236, 176, 0.55);
  border-bottom: 2px solid var(--accent-soft);
  border-radius: 2px;
  padding: 0 1px;
  cursor: help;
  transition: background .15s ease;
}
.ql-editor .cmt:hover { background: rgba(252, 236, 176, 0.85); }
.ql-editor .cmt.is-flashing {
  animation: cmt-flash 1.1s ease-out;
}
@keyframes cmt-flash {
  0%   { background: var(--accent); color: #fff; }
  100% { background: rgba(252, 236, 176, 0.55); color: inherit; }
}
.ql-snow .ql-stroke { stroke: var(--ink-2); }
.ql-snow .ql-fill { fill: var(--ink-2); }
.ql-snow .ql-picker { color: var(--ink-2); }
.ql-snow .ql-picker-label:hover, .ql-snow button:hover { color: var(--accent) !important; }
.ql-snow button:hover .ql-stroke { stroke: var(--accent); }
.ql-snow button:hover .ql-fill { fill: var(--accent); }

/* Custom toolbar buttons (undo / redo) */
.ql-toolbar .ql-undo,
.ql-toolbar .ql-redo {
  font-size: 16px !important;
  line-height: 1 !important;
  color: var(--ink-2) !important;
}
.ql-toolbar .ql-undo:hover,
.ql-toolbar .ql-redo:hover { color: var(--accent) !important; }

/* The ✳ button is the toolbar's primary insert action — blockquote,
   image, ornamental break, page break, footnote and citation all moved
   behind it in v2.65 — so it is a labelled pill rather than a fifteenth
   anonymous glyph in a row of glyphs. Quill fixes its buttons at 28px
   square, hence the width/padding overrides. */
.ql-toolbar .ql-text-feature {
  display: inline-flex !important;
  align-items: center;
  gap: 5px;
  width: auto !important;
  height: 24px;
  padding: 0 9px !important;
  border: 1px solid var(--accent) !important;
  border-radius: 6px;
  background: var(--accent-soft);
  color: var(--accent) !important;
  font-family: var(--font-sans);
  font-size: 12px !important;
  font-weight: 600;
  line-height: 1 !important;
  letter-spacing: .01em;
}
.ql-toolbar .ql-text-feature:hover,
.ql-toolbar .ql-text-feature:focus-visible {
  background: var(--accent);
  color: #fff !important;
  border-color: var(--accent) !important;
}
.ql-tf-glyph { font-size: 13px; line-height: 1; }

/* Group dividers. Quill wraps each toolbar group in
   <span class="ql-formats">, but with only its default right margin the
   groups run together into one undifferentiated strip. A hairline makes
   the structure readable and gives the ✳ pill room of its own. */
.editor-toolbar-host .ql-snow.ql-toolbar .ql-formats { margin-right: 0; }
.editor-toolbar-host .ql-snow.ql-toolbar .ql-formats + .ql-formats {
  margin-left: 10px;
  padding-left: 10px;
  border-left: 1px solid var(--line);
}

/* A manual page break is CONSUMED by the preview renderer, which splits
   the chapter into separate .preview-page articles at it (preview.js
   splitChapterPages). This rule catches a stray one that escaped the
   split — a nested one, which the blot model forbids but markup pasted
   from elsewhere could still carry — so it goes quiet rather than
   drawing an unexplained line next to a real hr.tf-rule. */
.preview-page hr.page-break { display: none; }

/* Page break renders as a labeled pill in the editor.
 * Invisible in the published output — the print/EPUB pipelines
 * convert this into an actual page break (CSS page-break-before).
 * The hr is a void element, so the pill is built from ::before/::after
 * pseudo-elements: ::after draws the dim horizontal line, ::before
 * paints the centered "PAGE BREAK ↓" badge over it.
 */
.ql-editor hr.page-break {
  border: none;
  background: transparent;
  height: 28px;
  margin: 1.6em 0;
  position: relative;
  overflow: visible;
  padding: 0;
}
.ql-editor hr.page-break::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--accent);
  opacity: .25;
}
.ql-editor hr.page-break::before {
  content: 'PAGE BREAK ↓';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: var(--bg-paper);
  border: 1px solid var(--accent);
  color: var(--accent);
  padding: 4px 14px;
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .14em;
  border-radius: 999px;
  z-index: 2;
}

.manuscript-footer {
  height: 40px;
  border-top: 1px solid var(--line);
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 18px;
  font-size: 12px;
  color: var(--ink-3);
}
.manuscript-footer strong { color: var(--ink-1); font-weight: 600; }
.footer-btn {
  background: transparent;
  border: none;
  color: var(--ink-3);
  font-family: inherit;
  font-size: 12.5px;
  padding: 4px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.footer-btn:hover { background: var(--bg-rail); color: var(--ink-1); }

/* ----- Writing-timer footer button + popover ----- */
.footer-timer {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-variant-numeric: tabular-nums;
}
.footer-timer.is-running {
  background: var(--accent-bg);
  color: var(--accent);
  border: 1px solid var(--accent-soft);
}
.footer-timer.is-paused {
  background: var(--bg-rail);
  color: var(--ink-2);
}
.footer-timer.is-goal-met {
  animation: footer-timer-pulse 1.6s ease-in-out infinite;
}
@keyframes footer-timer-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(139,105,20,.4); }
  50%      { box-shadow: 0 0 0 6px rgba(139,105,20,0); }
}
.timer-icon { font-size: 14px; line-height: 1; }

.timer-popover {
  position: absolute;
  right: 18px;
  bottom: 50px;
  z-index: 240;
  width: 280px;
  background: #fff;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  font-size: 13.5px;
  animation: preset-pop-in .12s ease-out;
}
.timer-popover[hidden] { display: none; }
.timer-pop-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px 10px;
  border-bottom: 1px solid var(--line);
}
.timer-pop-title {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--ink-2);
  margin: 0;
}
.timer-pop-body {
  padding: 14px 16px 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.timer-elapsed {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 10px 0 4px;
  border-bottom: 1px solid var(--line-soft);
}
.timer-elapsed-secondary { padding: 0 0 8px; border-bottom: none; }
.timer-elapsed-num {
  font-family: var(--font-display);
  font-size: 28px;
  font-weight: 600;
  color: var(--ink-1);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.timer-elapsed-num.is-goal-met { color: var(--accent); }
.timer-elapsed-secondary .timer-elapsed-num { font-size: 18px; color: var(--accent); }
.timer-elapsed-label {
  font-size: 11px;
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: .08em;
}
.timer-controls {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.timer-discard {
  margin-top: 4px;
  font-size: 12px;
  color: var(--ink-4);
  text-align: center;
}
.timer-discard:hover { color: var(--danger); }
.timer-today {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 10px;
  border-top: 1px solid var(--line-soft);
  font-size: 12.5px;
  color: var(--ink-3);
}
.timer-today-label {
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: 10.5px;
}
.timer-today-value { font-variant-numeric: tabular-nums; color: var(--ink-1); }

/* ---------- Timer mode toggle (Time / Sprint) ---------- */
.timer-mode-toggle {
  display: flex;
  background: var(--bg-paper);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 3px;
  margin-bottom: 4px;
}
.timer-mode-btn {
  flex: 1;
  background: transparent;
  border: none;
  padding: 6px 10px;
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-3);
  cursor: pointer;
  border-radius: 999px;
  transition: background .12s ease, color .12s ease;
}
.timer-mode-btn:hover { color: var(--ink-1); }
.timer-mode-btn.is-active {
  background: var(--accent);
  color: #fff;
}

/* ---------- Sprint progress bar + status ---------- */
.timer-sprint-fields { display: flex; flex-direction: column; gap: 8px; }
.timer-sprint-fields[hidden] { display: none; }
.timer-sprint-progress {
  padding: 6px 0 2px;
}
.timer-sprint-progress[hidden] { display: none; }
.timer-sprint-bar {
  height: 8px;
  background: var(--bg-paper);
  border: 1px solid var(--line);
  border-radius: 999px;
  overflow: hidden;
}
.timer-sprint-bar > span {
  display: block;
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, var(--accent), var(--accent-soft));
  transition: width .3s ease;
}
.timer-sprint-status {
  margin-top: 4px;
  font-family: var(--font-sans);
  font-size: 11.5px;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}

/* Sprint completion toast (also reused for "typing locked" hits) */
.timer-sprint-toast {
  position: fixed;
  bottom: 60px;
  left: 50%;
  transform: translateX(-50%) translateY(8px);
  padding: 10px 18px;
  background: #1d1b18;
  color: #fff;
  border-radius: 999px;
  font-family: var(--font-sans);
  font-size: 13px;
  letter-spacing: .03em;
  z-index: 500;
  pointer-events: none;
  opacity: 0;
  transition: opacity .15s ease, transform .15s ease;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .25);
}
.timer-sprint-toast.is-visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* ---------- Streak counter + heatmap ---------- */
.timer-streak {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--line-soft);
}
.timer-streak-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 8px;
}
.timer-streak-num {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 700;
  color: var(--accent);
  line-height: 1;
}
.timer-streak-label {
  font-family: var(--font-sans);
  font-size: 11.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.timer-streak-best {
  margin-left: auto;
  font-size: 11px;
  color: var(--ink-3);
}
.timer-streak-best strong { color: var(--ink-1); font-weight: 600; }

.timer-streak-heatmap {
  display: grid;
  grid-template-rows: repeat(7, 8px);
  grid-auto-flow: column;
  grid-auto-columns: 8px;
  gap: 2px;
  overflow-x: auto;
  padding: 2px 0;
  scrollbar-width: thin;
}
.heatmap-cell {
  display: block;
  background: var(--line-soft);
  border-radius: 1.5px;
}
.heatmap-cell[data-level="1"] { background: #d9c98a; }
.heatmap-cell[data-level="2"] { background: #c1a85a; }
.heatmap-cell[data-level="3"] { background: var(--accent-soft); }
.heatmap-cell[data-level="4"] { background: var(--accent); }

/* ---------- Read-aloud popover ---------- */
.tts-popover {
  position: fixed;
  top: 60px;
  right: 96px;
  z-index: 240;
  width: 300px;
  background: #fff;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  font-size: 13.5px;
  animation: preset-pop-in .12s ease-out;
  /* Never taller than the viewport: the header stays put and the body
     scrolls, so the custom-voice panel's lower controls stay reachable. */
  max-height: calc(100vh - 76px);
  display: flex;
  flex-direction: column;
}
.tts-popover[hidden] { display: none; }
.tts-pop-head { flex: 0 0 auto; }
.tts-pop-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.tts-pop-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px 10px;
  border-bottom: 1px solid var(--line);
}
.tts-pop-title {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--ink-2);
  margin: 0;
}
.tts-pop-body {
  padding: 14px 16px 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.tts-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 4px 0 6px;
}
.tts-btn {
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-paper);
  border: 1px solid var(--line);
  border-radius: 50%;
  color: var(--ink-2);
  cursor: pointer;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.tts-btn:hover { background: var(--accent-soft); color: var(--ink-1); border-color: var(--accent-soft); }
.tts-btn-primary {
  width: 42px;
  height: 42px;
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.tts-btn-primary:hover { background: #a37a17; border-color: #a37a17; color: #fff; }
.tts-rate-label {
  margin-left: 6px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
  font-weight: 500;
}
.tts-divider { border: none; border-top: 1px solid var(--line); margin: 4px 0 6px; }
.tts-status {
  margin: 0 0 10px;
  padding: 8px 10px;
  background: #fdf3e0;
  border: 1px solid #e3c98a;
  border-radius: var(--radius-sm);
  font-size: 12px;
  color: #6f4f0e;
  line-height: 1.4;
}
.tts-status[hidden] { display: none; }

/* Inter-chunk loading indicator. Sits below the speed slider in
   the read-aloud popover; shows only when Kokoro is mid-generation
   and the gap has crossed the show-after-N-ms threshold set in
   read-aloud.js. The spinner uses the existing accent color so it
   reads as "system is working" rather than an error. */
.tts-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 10px 0 0;
  padding: 6px 12px;
  background: var(--accent-bg);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-radius: 999px;
  font-size: 12px;
  color: var(--ink-2);
}
.tts-loading[hidden] { display: none; }
.tts-loading-spinner {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 2px solid var(--line);
  border-top-color: var(--accent);
  animation: tts-spin 0.7s linear infinite;
  flex-shrink: 0;
}
@keyframes tts-spin {
  to { transform: rotate(360deg); }
}

/* The currently-spoken paragraph (editor + preview): a faint backdrop +
   accent bar marking the paragraph being read, kept subtle so the brighter
   current-sentence band below is the clear focal point. */
.ql-editor .tts-highlight {
  background: rgba(139,105,20,.13);
  border-radius: 3px;
  box-shadow: inset 3px 0 0 var(--accent);
  padding-left: 8px !important;
  transition: background .15s ease;
}
.preview-page .tts-highlight {
  background: rgba(139,105,20,.11);
  border-radius: 3px;
  box-shadow: inset 3px 0 0 var(--accent);
  transition: background .15s ease;
}

/* The exact sentence being spoken — read-along aid for focused reading.
   Painted via the CSS Custom Highlight API (a styled Range, no wrapper
   element), so it's safe over the live Quill editor. Browsers without the
   API simply ignore this rule and fall back to the paragraph backdrop.
   A warm highlighter band so the moving sentence is unmistakable. */
::highlight(tts-sentence) {
  background-color: rgba(255, 209, 71, .60);
  color: inherit;
}
:root[data-theme="dark"] ::highlight(tts-sentence) {
  background-color: rgba(232, 196, 92, .42);
  color: inherit;
}

/* ============================================================
   Reading aid — a personal on-screen reading lens (dyslexia / low-vision).
   Toggled by reading-aid.js via classes + CSS vars on <body>. Scoped to the
   editor surface and the in-app Preview only; never affects saved content or
   the PDF/EPUB exports.
   ============================================================ */

/* OpenDyslexic (weighted-bottom dyslexia typeface) — served from the
   fontsource CDN, consistent with how the app loads Google Fonts / Quill.
   Atkinson Hyperlegible and Lexend come from Google Fonts (see layout.php). */
@font-face {
  font-family: 'OpenDyslexic';
  src: url('https://cdn.jsdelivr.net/fontsource/fonts/opendyslexic@latest/latin-400-normal.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'OpenDyslexic';
  src: url('https://cdn.jsdelivr.net/fontsource/fonts/opendyslexic@latest/latin-700-normal.woff2') format('woff2');
  font-weight: 700; font-style: normal; font-display: swap;
}

/* --- The lens itself: spacing + line-height over editor & preview text --- */
body.reading-aid .ql-editor,
body.reading-aid .preview-page .chapter-body {
  letter-spacing: var(--ra-letter, 0) !important;
  word-spacing: var(--ra-word, 0) !important;
  line-height: var(--ra-line, 1.6) !important;
}
/* Optional narrower measure (shorter lines are easier to track). Centered in
   the page; preview keeps its own page width to avoid layout surprises. */
body.reading-aid .ql-editor {
  max-width: var(--ra-width, none);
  margin-left: auto;
  margin-right: auto;
}

/* --- Reading fonts --- */
body.reading-aid.ra-font-opendyslexic .ql-editor,
body.reading-aid.ra-font-opendyslexic .preview-page .chapter-body {
  font-family: 'OpenDyslexic', 'Comic Sans MS', sans-serif !important;
}
body.reading-aid.ra-font-atkinson .ql-editor,
body.reading-aid.ra-font-atkinson .preview-page .chapter-body {
  font-family: 'Atkinson Hyperlegible', sans-serif !important;
}
body.reading-aid.ra-font-lexend .ql-editor,
body.reading-aid.ra-font-lexend .preview-page .chapter-body {
  font-family: 'Lexend', sans-serif !important;
}

/* --- Background tints (Irlen-style overlay). Light theme only — in dark mode
   a cream page would clash, so the user turns the tint off there. --- */
:root:not([data-theme="dark"]) body.reading-aid.ra-bg-cream .page-sheet,
:root:not([data-theme="dark"]) body.reading-aid.ra-bg-cream .preview-page { background-color: #faf3e1 !important; }
:root:not([data-theme="dark"]) body.reading-aid.ra-bg-peach .page-sheet,
:root:not([data-theme="dark"]) body.reading-aid.ra-bg-peach .preview-page { background-color: #fbeee0 !important; }
:root:not([data-theme="dark"]) body.reading-aid.ra-bg-mint .page-sheet,
:root:not([data-theme="dark"]) body.reading-aid.ra-bg-mint .preview-page { background-color: #e8f3e8 !important; }
:root:not([data-theme="dark"]) body.reading-aid.ra-bg-blue .page-sheet,
:root:not([data-theme="dark"]) body.reading-aid.ra-bg-blue .preview-page { background-color: #e7eef6 !important; }
:root:not([data-theme="dark"]) body.reading-aid.ra-bg-grey .page-sheet,
:root:not([data-theme="dark"]) body.reading-aid.ra-bg-grey .preview-page { background-color: #ecebe8 !important; }

/* Inline scope qualifier inside a .field-label ("(editor only)"), which is
   otherwise uppercase + letter-spaced. Used where a reading aid can't apply
   everywhere, so the control is honest instead of looking broken. */
.field-hint-inline {
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  font-size: 11px;
  font-style: italic;
  color: var(--ink-3);
}
/* Shown only in dark mode — the background tints above are scoped to
   :root:not([data-theme="dark"]), so in dark mode the swatches would appear
   to select while nothing changed. */
.ra-dark-only { display: none; }
:root[data-theme="dark"] .ra-dark-only { display: inline; }

/* --- The control popover (mirrors the read-aloud popover) --- */
.reading-popover {
  position: fixed;
  top: 60px;
  right: 96px;
  z-index: 240;
  width: 300px;
  /* The panel is tall (fonts, spacing, tint, ruler) — cap it to the viewport
     and let the body scroll so nothing runs off the bottom of the screen. */
  max-height: calc(100vh - 76px);
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: 0 12px 40px rgba(0,0,0,.18);
}
.reading-popover[hidden] { display: none; }
.reading-popover .tts-pop-head { flex: 0 0 auto; }   /* header stays put */
.reading-popover .tts-pop-body {
  flex: 1 1 auto;
  min-height: 0;        /* allow the flex child to shrink so overflow scrolls */
  overflow-y: auto;
}

.ra-swatches { display: flex; gap: 6px; }
.ra-swatch {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  color: var(--ink-3);
}
.ra-swatch.is-active {
  box-shadow: 0 0 0 2px var(--accent);
  border-color: var(--accent);
}
.ra-reset {
  margin-top: 2px;
  align-self: flex-start;
  background: none;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 6px 12px;
  font-size: 12.5px;
  color: var(--ink-2);
  cursor: pointer;
}
.ra-reset:hover { background: var(--bg-paper); }

/* --- Reading ruler: a clear band that follows the mouse, dimming the rest of
   the page so one line stays in focus. The overlay never blocks interaction
   (pointer-events:none) and sits below the control popovers (z 240) so the
   panel itself isn't dimmed while you adjust it. --- */
.ra-ruler {
  position: fixed;
  inset: 0;
  z-index: 200;
  pointer-events: none;
}
.ra-ruler[hidden] { display: none; }
.ra-ruler-dim {
  position: fixed;
  left: 0;
  right: 0;
  background: rgba(12, 14, 22, var(--ra-ruler-dim, .18));
}
.ra-ruler-top    { top: 0; height: 0; }       /* height set inline from JS */
.ra-ruler-bottom { bottom: 0; top: 0; }       /* top set inline from JS */
.ra-ruler-band {
  position: fixed;
  left: 0;
  right: 0;
  border-top: 1.5px solid rgba(139, 105, 20, .55);
  border-bottom: 1.5px solid rgba(139, 105, 20, .55);
}

/* ---------- Typewriter focus mode ---------- */
.editor-shell.is-typewriter-focus .ql-editor {
  /* Generous top/bottom padding so any line can scroll to vertical center */
  padding-top: 40vh !important;
  padding-bottom: 40vh !important;
}
.editor-shell.is-typewriter-focus .ql-editor > * {
  transition: opacity .25s ease;
  opacity: .35;
}
/* The active block is tagged from JS on every selection-change. */
.editor-shell.is-typewriter-focus .ql-editor > .is-typewriter-active,
.editor-shell.is-typewriter-focus .ql-editor > .tts-highlight {
  opacity: 1;
}

/* ---------- Smart typography toast (transient feedback) ---------- */
.smart-typo-toast {
  position: fixed;
  top: 64px;
  left: 50%;
  transform: translateX(-50%) translateY(-12px);
  padding: 9px 18px;
  background: #1d1b18;
  color: #fff;
  border-radius: 999px;
  font-family: var(--font-sans);
  font-size: 12.5px;
  letter-spacing: .04em;
  z-index: 500;
  pointer-events: none;
  opacity: 0;
  transition: opacity .15s ease, transform .15s ease;
  box-shadow: 0 6px 20px rgba(0,0,0,.18);
}
.smart-typo-toast.is-visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* ---------- Prose checker (CSS Highlight API + side panel) ---------- */
::highlight(prose-weasel) {
  background-color: #fff4d4;
  text-decoration: underline wavy #c2a644;
  text-underline-offset: 2px;
}
::highlight(prose-wordy) {
  background-color: #e2ecff;
  text-decoration: underline wavy #5a7fd6;
  text-underline-offset: 2px;
}
::highlight(prose-repeat) {
  background-color: #fde2e2;
  text-decoration: underline wavy #c25555;
  text-underline-offset: 2px;
}
/* Transient flash applied for ~1.5s after clicking a panel entry, so
   the user can spot the clicked finding among many same-colored
   weasel/repeat highlights. Sits on top of the persistent layers. */
::highlight(prose-flash) {
  background-color: #2c70f5;
  color: #fff;
}
.prose-panel {
  position: fixed;
  top: 56px;
  right: 16px;
  width: 360px;
  max-height: calc(100vh - 80px);
  background: #fff;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  z-index: 230;
  display: flex;
  flex-direction: column;
  animation: preset-pop-in .12s ease-out;
}
.prose-panel[hidden] { display: none; }
.prose-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px;
  border-bottom: 1px solid var(--line);
}
.prose-panel-title {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--ink-2);
}
.prose-panel-actions { display: flex; align-items: center; gap: 6px; }
.prose-panel-actions .btn-link {
  font-size: 14px;
  background: transparent;
  border: none;
  color: var(--ink-3);
  cursor: pointer;
  padding: 2px 6px;
}
.prose-panel-actions .btn-link:hover { color: var(--accent); }
.prose-panel-summary {
  display: flex;
  gap: 16px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--line-soft);
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--ink-2);
}
.prose-stat strong {
  font-weight: 600;
  color: var(--ink-1);
  font-variant-numeric: tabular-nums;
}
.prose-panel-list {
  list-style: none;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  flex: 1;
}
.prose-empty,
.prose-degraded {
  padding: 18px 14px;
  margin: 0;
  font-size: 12.5px;
  color: var(--ink-3);
  font-style: italic;
}
.prose-row {
  padding: 10px 14px;
  border-bottom: 1px solid var(--line-soft);
  cursor: pointer;
  display: flex;
  gap: 10px;
  align-items: flex-start;
  transition: background .12s ease;
}
.prose-row:hover { background: var(--bg-paper); }
.prose-row-badge {
  font-family: var(--font-sans);
  font-size: 9.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .1em;
  padding: 2px 6px;
  border-radius: 999px;
  flex-shrink: 0;
}
.prose-row-weasel .prose-row-badge { background: #fff4d4; color: #6a4d0a; }
.prose-row-wordy  .prose-row-badge { background: #e2ecff; color: #1d3a6a; }
.prose-row-repeat .prose-row-badge { background: #fde2e2; color: #6a1d1d; }
.prose-row-text {
  font-family: var(--font-serif, Georgia, serif);
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-1);
}
.prose-ctx { color: var(--ink-3); }
.prose-row-text strong { color: var(--ink-1); background: rgba(255,235,170,.5); padding: 0 2px; border-radius: 2px; }
/* Suggested tighter replacement on a wordy-phrase row ("→ because"). */
.prose-suggest {
  display: block;
  margin-top: 3px;
  font-family: var(--font-sans);
  font-size: 11.5px;
  font-weight: 600;
  color: var(--accent);
}

/* ---------- Find & Replace (rail panel) ----------
 * The find UI lives inside the writing-view's right rail panel
 * (.rail-panel-body[data-panel="find"]). Layout is column-flow,
 * narrow column, so input rows and action buttons stack tightly.
 */
.rail-find-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.find-tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--line);
  margin: -4px -4px 6px;
  padding: 0 4px;
}
.find-tab {
  background: transparent;
  border: none;
  padding: 8px 14px;
  font: inherit;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-3);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color .12s ease, border-color .12s ease;
}
.find-tab:hover { color: var(--ink-2); }
.find-tab.is-active {
  color: var(--ink-1);
  border-bottom-color: var(--accent);
}
.find-tab-context {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 11.5px;
  color: var(--ink-3);
  font-style: italic;
}
.find-input-row {
  display: flex;
  align-items: center;
  gap: 6px;
}
.find-input-row input {
  flex: 1;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 14px;
  background: var(--bg-paper);
}
.find-input-row input:focus { outline: none; border-color: var(--accent); background: #fff; }
.find-nav-btn {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--ink-2);
  cursor: pointer;
  flex-shrink: 0;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.find-nav-btn:hover:not(:disabled) {
  background: var(--accent-soft);
  color: var(--ink-1);
  border-color: var(--accent-soft);
}
.find-nav-btn:disabled {
  opacity: .35;
  cursor: not-allowed;
}
.find-options {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.find-options label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--ink-2);
  cursor: pointer;
}
.find-actions {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap;
}
.find-actions .btn { padding: 6px 12px; font-size: 12.5px; }

/* Inline highlights for chapter-mode matches (CSS Custom Highlight API). */
::highlight(find-match) {
  background-color: rgba(255, 220, 100, 0.55);
  border-radius: 2px;
}
/* The match the author is currently on (since the editor cursor no longer
   moves to it) — a stronger tint so it stands out from the other matches. */
::highlight(find-current) {
  background-color: rgba(255, 150, 40, 0.75);
  border-radius: 2px;
}
.find-summary {
  font-family: var(--font-sans);
  font-size: 12.5px;
  color: var(--ink-3);
  min-height: 18px;
}
.find-summary[data-kind="ok"]    { color: #2d5a1f; }
.find-summary[data-kind="error"] { color: var(--danger); }
.find-summary[data-kind="busy"]  { color: var(--accent); font-style: italic; }
.find-results {
  overflow-y: auto;
  max-height: 50vh;
  border-top: 1px solid var(--line-soft);
  padding-top: 10px;
}

/* Quick-replacements section under the regular find/replace controls.
   Honors the Chapter / Book scope tabs at the top of the panel. */
.find-quick-replacements {
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--line-soft);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.find-quick-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.find-quick-status {
  font-size: 12px;
  color: var(--ink-3);
  min-height: 16px;
}
.find-empty {
  padding: 14px;
  margin: 0;
  color: var(--ink-3);
  font-style: italic;
}
.find-chapter { margin-bottom: 18px; }
.find-chapter-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px solid var(--line-soft);
  flex-wrap: wrap;
}
.find-chapter-title {
  background: transparent;
  border: none;
  padding: 0;
  font: inherit;
  font-family: var(--font-display);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-1);
  cursor: pointer;
  text-align: left;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.find-chapter-title:hover { color: var(--accent); }
.find-chapter-count {
  margin-left: auto;
  font-family: var(--font-sans);
  font-size: 11px;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.find-truncated {
  margin: 6px 0;
  color: var(--ink-3);
  font-size: 11.5px;
  font-style: italic;
}
.find-snippets { list-style: none; padding: 0; margin: 6px 0 0; }
.find-snippet {
  font-family: var(--font-serif, Georgia, serif);
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--ink-2);
  padding: 4px 0;
}
.find-snippet mark {
  background: rgba(255,235,170,.7);
  color: var(--ink-1);
  padding: 0 2px;
  border-radius: 2px;
  font-weight: 500;
}
.find-ctx { color: var(--ink-3); }
/* Book-mode snippets are clickable — each jumps to its specific occurrence. */
.find-snippet.is-jumpable { cursor: pointer; border-radius: 4px; padding: 4px 6px; margin: 0 -6px; transition: background .12s ease; }
.find-snippet.is-jumpable:hover { background: var(--accent-soft); }

/* Replacement preview inside book-mode snippets — pre [old → new] post.
   Old gets the same yellow tint as a regular match, new gets a green
   tint so the user reads it as additive at a glance. */
.find-snippets.has-preview .find-snippet { font-size: 13px; }
.find-old {
  background: rgba(255,235,170,.7);
  color: var(--ink-2);
  text-decoration: line-through;
  padding: 0 2px;
  border-radius: 2px;
}
.find-arrow {
  display: inline-block;
  margin: 0 4px;
  color: var(--ink-3);
  font-weight: 600;
}
.find-new {
  background: rgba(170, 220, 170, .55);
  color: #1f4a1f;
  padding: 0 2px;
  border-radius: 2px;
  text-decoration: none;
  font-weight: 500;
}

/* Book-mode "Where" select — sub-scope under the Book tab. Hidden by
   the [hidden] attribute when in chapter mode. */
.find-kind-scope {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0 8px;
}
.find-kind-scope-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-3);
}
#find-kind-scope-select {
  flex: 1;
  padding: 4px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-paper);
  font: inherit;
  font-size: 12.5px;
  color: var(--ink-1);
}

/* Disabled visual state on the whole-word label when regex mode is on
   (the input itself is also disabled via JS). */
.find-options label.is-disabled { opacity: .55; cursor: not-allowed; }
.find-options label.is-disabled input { pointer-events: none; }

/* Undo-last-replace bar. Sits between the summary and the results so
   the user sees it immediately after a successful replace. */
.find-undo-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 0;
  border-top: 1px dashed var(--line);
  border-bottom: 1px dashed var(--line);
  margin: 6px 0;
}
.find-undo-meta { font-size: 11.5px; color: var(--ink-3); }

/* ---------- Distraction-free focus mode ----------
 *
 * Strategy:
 *   - Keep the topbar visible but compress it into a small floating
 *     horizontal pill at the top-right with only the action icons
 *     (exit focus, find, prose, TTS, preview, export). The book title,
 *     view tabs, save status, admin link, and avatar are hidden.
 *   - Hide the chapter sidebar, manuscript footer, page-sheet gear,
 *     and all transient popovers / floaters.
 *   - Center the manuscript and remove the page-sheet's paper border
 *     for a cleaner reading frame.
 */
.editor-shell.is-focus-mode .chapter-sidebar,
.editor-shell.is-focus-mode .rail-panel,
.editor-shell.is-focus-mode .manuscript-footer,
.editor-shell.is-focus-mode .comment-floater,
.editor-shell.is-focus-mode .comment-composer,
.editor-shell.is-focus-mode .chapter-settings-btn,
.editor-shell.is-focus-mode .chapter-settings-popover,
.editor-shell.is-focus-mode .tts-popover,
.editor-shell.is-focus-mode .timer-popover,
.editor-shell.is-focus-mode .bible-mention-popover,
.editor-shell.is-focus-mode .editor-topbar {
  display: none !important;
}
/* The escape hatch (v2.16, focus toggle now lives on the rail): the rail
   itself becomes a small floating pill holding ONLY the focus button —
   position:fixed lifts it out of the 1fr grid below. Ghosted so it never
   competes with the prose; brightens on approach. Esc and Ctrl+Shift+D
   still work, but a mode you can't SEE your way out of is a trap. */
.editor-shell.is-focus-mode .icon-rail {
  position: fixed;
  top: 14px;
  right: 14px;
  z-index: 240;
  padding: 4px;
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--line);
  border-radius: 999px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
  opacity: .45;
  transition: opacity .15s ease;
}
.editor-shell.is-focus-mode .icon-rail:hover,
.editor-shell.is-focus-mode .icon-rail:focus-within { opacity: 1; }
.editor-shell.is-focus-mode .rail-btn:not(#focus-mode-btn),
.editor-shell.is-focus-mode .rail-sep { display: none !important; }
:root[data-theme="dark"] .editor-shell.is-focus-mode .icon-rail {
  background: rgba(38, 34, 29, 0.94);
}
.editor-shell.is-focus-mode .editor-body-grid {
  grid-template-columns: 1fr !important;
}
.editor-shell.is-focus-mode .manuscript {
  padding-top: 36px;
}
.editor-shell.is-focus-mode .page-stage {
  max-width: 820px;
  margin: 0 auto;
}
.editor-shell.is-focus-mode .page-sheet {
  box-shadow: none !important;
  border: none !important;
}
.topbar-iconbtn.is-active {
  color: var(--accent);
  background: var(--accent-soft);
}
/* Theme toggle: show the icon that represents the *target* theme. In
   light mode, show the moon (click to go dark). In dark mode, show
   the sun (click to go light). The button stores the *current* theme
   in data-theme so we just CSS-swap which child SVG is visible. */
.theme-toggle .icon-sun  { display: none; }
.theme-toggle .icon-moon { display: inline-block; }
.theme-toggle[data-theme="dark"] .icon-sun  { display: inline-block; }
.theme-toggle[data-theme="dark"] .icon-moon { display: none; }

.manuscript { position: relative; } /* anchor for the absolute-positioned popover */
.footer-meta { display: flex; align-items: center; gap: 22px; }
.footer-stat { font-variant-numeric: tabular-nums; }
/* Genre-aware "Novel-length" chip in the footer. Click opens the modal
   below with the standard word-count buckets for the book's genre. */
.footer-length-chip {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: 999px;
  background: rgba(139,105,20,.14);
  color: var(--accent);
  border: 1px solid rgba(139,105,20,.28);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  cursor: pointer;
  font-family: var(--font-sans);
  line-height: 1.4;
}
.footer-length-chip:hover { background: rgba(139,105,20,.22); }

/* Variant tints for the additional footer chips. Each gets a unique
   hue so the row reads as a row of distinct stats rather than four
   identical pills. Hover state inherits the deeper tint. */
.footer-length-chip.is-today {
  background: rgba(46,125,50,.14);
  color: #2e7d32;
  border-color: rgba(46,125,50,.30);
}
.footer-length-chip.is-today:hover { background: rgba(46,125,50,.22); }
.footer-length-chip.is-pages {
  background: rgba(48,79,154,.12);
  color: #304f9a;
  border-color: rgba(48,79,154,.28);
}
.footer-length-chip.is-pages:hover { background: rgba(48,79,154,.20); }
.footer-length-chip.is-readtime {
  background: rgba(120,55,160,.12);
  color: #783aa0;
  border-color: rgba(120,55,160,.28);
}
.footer-length-chip.is-readtime:hover { background: rgba(120,55,160,.20); }

/* The shared chip modal re-uses .length-modal-card chrome but its body
   is dynamic so it doesn't need a fixed table. Just give it some
   breathing room and let length-modal-* styles handle the rest. */
.chip-modal-body {
  padding: 0 0 18px;
}
.chip-modal-body > .length-modal-sub:first-child { margin-top: 16px; }

/* Length-info modal */
.length-modal-card { max-width: 460px; padding: 0; }
.length-modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 22px 10px;
  border-bottom: 1px solid var(--line-soft);
}
.length-modal-head .modal-title { margin: 0; }
.length-modal-sub {
  margin: 14px 22px 8px;
  font-size: 13px;
  color: var(--ink-3);
  line-height: 1.5;
}
.length-modal-table {
  width: calc(100% - 44px);
  margin: 8px 22px 0;
  border-collapse: collapse;
  font-size: 14px;
}
.length-modal-table th,
.length-modal-table td {
  text-align: left;
  padding: 8px 10px;
  border-bottom: 1px solid var(--line-soft);
}
.length-modal-table th {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.length-modal-table td:last-child {
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}
.length-modal-table tr.is-current td {
  background: rgba(139,105,20,.10);
  color: var(--accent);
  font-weight: 600;
}
.length-modal-foot {
  margin: 16px 22px 22px;
  font-size: 12px;
  color: var(--ink-4);
  font-style: italic;
}
.goal-progress { display: flex; align-items: center; gap: 10px; }
.goal-bar {
  width: 160px;
  height: 4px;
  background: var(--line);
  border-radius: 2px;
  overflow: hidden;
}
.goal-fill {
  display: block;
  height: 100%;
  background: var(--accent);
  transition: width .25s ease;
}
.goal-label { font-variant-numeric: tabular-nums; }

/* ----- Right icon rail ----- */
.icon-rail {
  background: #fff;
  border-left: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 14px 0;
  gap: 6px;
  order: 2; /* always rightmost — rail-panel slides in to its left */
}
.chapter-sidebar { order: 0; }
.manuscript      { order: 1; }
.rail-panel      { order: 1; } /* sits between manuscript and icon-rail when shown */
.rail-btn {
  width: 36px; height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--ink-3);
  cursor: pointer;
  padding: 0;
}
.rail-btn:hover { background: var(--bg-rail); color: var(--ink-1); }
/* Divider between the panel rail buttons and the reading/checking tools. */
.rail-sep { width: 20px; height: 1px; background: var(--line); margin: 4px 0; flex: none; }
.rail-btn.is-active {
  background: var(--accent-bg);
  color: var(--accent);
  border-color: var(--accent-soft);
}
.rail-glyph {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
}

/* ----- Right rail slide-out panel ----- */
.rail-panel {
  background: #fff;
  border-left: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.rail-panel[hidden] { display: none; }
.rail-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px 12px;
  border-bottom: 1px solid var(--line);
}
.rail-panel-title {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--ink-2);
  margin: 0;
}
.rail-panel-close {
  width: 24px; height: 24px;
  border: none; background: transparent;
  font-size: 20px; line-height: 1;
  color: var(--ink-3); cursor: pointer; padding: 0;
  border-radius: 4px;
}
.rail-panel-close:hover { background: var(--bg-rail); color: var(--ink-1); }
.rail-panel-body {
  flex: 1;
  overflow-y: auto;
  padding: 16px 16px 24px;
}

/* Backdrop chooser panel (v2.24): a card per uploaded variation.
   Panel chrome is token-driven — the thumbnails are the only art. */
.backdrop-choice-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.backdrop-choice {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 6px 6px 7px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 11.5px;
  color: var(--ink-3);
  cursor: pointer;
  text-align: left;
  transition: border-color .12s ease, color .12s ease;
}
.backdrop-choice img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: 4px;
  display: block;
}
.backdrop-choice:hover {
  border-color: var(--line-strong);
  color: var(--ink-1);
}
.backdrop-choice.is-active {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
  color: var(--ink-1);
}
/* The "Account default" card in Book details' per-book picker (3.14):
   no image of its own — a quiet initial on the card's own tones, the
   same 16:9 footprint as the thumbnails beside it. */
.backdrop-choice-default {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 4px;
  background: var(--bg-rail);
  border: 1px dashed var(--line);
  font-size: 18px;
  font-weight: 600;
  color: var(--ink-4);
}
/* In the details pane the grid gets room — wider cards than the rail's
   two-up. */
.details-tab-pane .backdrop-choice-grid {
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}

/* Per-chapter notes panel. The textarea fills the panel below the
   help line so the author has as much room as possible without the
   panel itself scrolling. */
.chapter-notes-panel-body {
  display: flex;
  flex-direction: column;
}
.chapter-notes-head {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-bottom: 8px;
}
.chapter-notes-head .rail-panel-help { flex: 1; min-width: 0; }
.chapter-notes-clear {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  color: var(--ink-3);
  cursor: pointer;
  transition: color .12s ease, background .12s ease, border-color .12s ease;
}
.chapter-notes-clear:hover {
  color: var(--danger, #b04646);
  background: rgba(176, 70, 70, .08);
  border-color: rgba(176, 70, 70, .25);
}
.chapter-notes-textarea {
  flex: 1;
  width: 100%;
  min-height: 240px;
  resize: none;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-paper);
  font-family: var(--font-serif, Georgia, serif);
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink-1);
  outline: none;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.chapter-notes-textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(139,105,20,.12);
  background: #fff;
}
.chapter-notes-textarea::placeholder {
  color: var(--ink-3);
  font-style: italic;
}

/* Small dot on the rail's Notes icon — only shown when the current
   chapter has notes, so the author can spot it from across the
   editor without opening the panel. */
.rail-btn { position: relative; }
.rail-notes-dot {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 2px var(--bg-rail);
}
.rail-panel-help {
  font-size: 12.5px;
  color: var(--ink-3);
  line-height: 1.55;
  margin: 0 0 14px;
}

.find-results { margin-top: 6px; font-size: 12.5px; color: var(--ink-3); }

/* ---------- Comments rail icon + count badge ---------- */
.rail-comments-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.rail-comments-count {
  position: absolute;
  top: -6px;
  right: -8px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  background: var(--accent);
  color: #fff;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  letter-spacing: 0;
}

/* ---------- Comments rail panel ---------- */
.comments-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 0 0 8px;
  border-bottom: 1px solid var(--line-soft);
  margin-bottom: 10px;
}
.comments-nav {
  display: inline-flex;
  gap: 2px;
}
.comments-nav-btn {
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  font-size: 14px;
  line-height: 1;
  color: var(--ink-2);
  font-weight: 600;
  padding: 0;
}
.comments-nav-btn:hover { background: var(--bg-rail); color: var(--accent); }
.comments-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.comments-empty {
  padding: 14px 0;
  font-size: 12px;
  color: var(--ink-4);
  font-style: italic;
  text-align: center;
}
.comment-row {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  background: #fff;
  font-size: 13px;
}
.comment-row.is-resolved { background: var(--bg-rail); opacity: .75; }
.comment-meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  font-size: 11px;
  color: var(--ink-3);
  margin-bottom: 4px;
}
.comment-author { font-weight: 600; color: var(--ink-2); }
.comment-time   { font-variant-numeric: tabular-nums; }
.comment-badge {
  display: inline-block;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 999px;
  line-height: 1.4;
}
.comment-badge-beta {
  background: rgba(139,105,20,.14);
  color: var(--accent);
  border: 1px solid rgba(139,105,20,.25);
}
.comment-row.is-from-share { border-left: 3px solid var(--accent); }
.comment-quote {
  margin: 4px 0 6px;
  padding: 6px 10px;
  background: rgba(139,105,20,.06);
  border-left: 2px solid var(--accent);
  font-style: italic;
  font-size: 13px;
  color: var(--ink-2);
  line-height: 1.4;
  max-height: 5.5em;
  overflow: hidden;
}
.comment-body   {
  color: var(--ink-1);
  line-height: 1.45;
  white-space: pre-wrap;
  word-break: break-word;
}
.comment-row.is-resolved .comment-body { text-decoration: line-through; color: var(--ink-3); }
.comment-row.is-editing { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(139,105,20,.10); }
.comment-edit-body {
  width: 100%;
  min-height: 60px;
  padding: 6px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 13px;
  resize: vertical;
  background: var(--bg-paper);
  margin-top: 2px;
}
.comment-edit-body:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(139,105,20,.12);
  background: #fff;
}
.comment-actions {
  display: flex;
  gap: 12px;
  margin-top: 8px;
  align-items: center;
}
.comment-row.is-editing .comment-actions { justify-content: flex-end; }
.comment-actions .btn-link {
  font-size: 12px;
  color: var(--ink-3);
}
.comment-actions .btn-link:hover { color: var(--ink-1); }
.comment-danger:hover { color: var(--danger) !important; }

/* Author reply on a beta-reader comment. Indented + accent-tinted to
   read as a response to the body above, not as a sibling note. */
.comment-reply {
  margin: 8px 0 0 8px;
  padding: 8px 10px;
  border-left: 2px solid var(--accent);
  background: var(--bg-rail);
  border-radius: 0 4px 4px 0;
}
.comment-reply-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  color: var(--ink-3);
  margin-bottom: 4px;
}
.comment-reply-from {
  font-weight: 600;
  color: var(--accent);
  letter-spacing: .04em;
}
.comment-reply-remove {
  margin-left: auto;
  font-size: 11px;
  color: var(--ink-3);
}
.comment-reply-remove:hover { color: var(--danger); }
.comment-reply-body {
  color: var(--ink-1);
  font-size: 13px;
  line-height: 1.45;
  white-space: pre-wrap;
  word-break: break-word;
}
.comment-reply-composer {
  margin-top: 8px;
  padding: 8px 10px 10px;
  border-left: 2px solid var(--accent);
  background: var(--bg-rail);
  border-radius: 0 4px 4px 0;
}
.comment-reply-edit {
  width: 100%;
  min-height: 60px;
  padding: 6px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 13px;
  resize: vertical;
  background: #fff;
}
.comment-reply-edit:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(139,105,20,.12);
}
.comment-reply-composer .comment-actions { margin-top: 6px; justify-content: flex-end; }

/* ---------- Floating comment button + composer ---------- */
.comment-floater {
  position: absolute;
  z-index: 220;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 10px;
  background: var(--ink-1);
  color: #fff;
  border: 1px solid var(--ink-1);
  border-radius: 6px;
  font-family: inherit;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,.2);
  animation: preset-pop-in .12s ease-out;
}
.comment-floater:hover { background: #000; }
.comment-floater[hidden] { display: none; }

.comment-composer {
  position: absolute;
  z-index: 230;
  width: 280px;
  background: #fff;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  padding: 12px;
  animation: preset-pop-in .12s ease-out;
}
.comment-composer[hidden] { display: none; }
.comment-composer textarea {
  width: 100%;
  min-height: 70px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 13px;
  resize: vertical;
  background: var(--bg-paper);
}
.comment-composer textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(139,105,20,.12);
  background: #fff;
}
.comment-composer-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 10px;
}

/* Reused: section headings inside any inspector-style panel */
.section-heading {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-3);
  margin: 0 0 12px;
}
.inspector-section {
  padding-bottom: 18px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--line-soft);
}
.inspector-section:last-child { border-bottom: none; padding-bottom: 0; margin-bottom: 0; }

/* ============================================================
   PHASE 2 — View switching shell
   ============================================================ */
.editor-view {
  flex: 1;
  overflow: hidden;
}
.editor-view[hidden] { display: none; }
.editor-view.is-active { display: flex; }
.editor-view.editor-body-grid {
  display: grid;
}
.editor-view.editor-body-grid[hidden] { display: none; }

/* ============================================================
   PHASE 2 — Format view
   ============================================================ */
.format-view {
  background: var(--bg-page);
  overflow: auto;
}
.format-shell {
  flex: 1;
  display: flex;
  flex-direction: column;
  width: 100%;
}
.format-hero {
  padding: 28px 48px 16px;
  border-bottom: 1px solid var(--line);
  background: #fff;
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.format-hero-title {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 500;
  margin: 0 0 4px;
  letter-spacing: .01em;
  color: var(--ink-2);
}
.format-hero-title #current-theme-name {
  font-weight: 600;
  color: var(--ink-1);
}
.format-hero-sub {
  margin: 0;
  color: var(--ink-3);
  font-size: 13.5px;
}
.format-hero-actions { display: flex; gap: 10px; }
.select-sm { width: auto; min-width: 130px; padding: 7px 10px; font-size: 13px; }

/* Gallery mode: theme gallery + device preview side-by-side.
   Editor mode: header bar (top) + left nav + main pane (with sections) + device preview. */
.format-body {
  display: grid;
  gap: 28px;
  padding: 28px 48px 60px;
  align-items: start;
}
/* The preview column is wide in EVERY device mode (print proof and the
   three e-readers alike) — a real render in a 380px column was a postage
   stamp on a large monitor. minmax() keeps it usable on small laptops. */
.format-body[data-mode="gallery"] {
  grid-template-columns: 1fr minmax(560px, 46%);
  grid-template-areas:
    "bar       side"
    "gallery   side";
}
.format-body[data-mode="editor"] {
  /* minmax(0, 1fr) keeps the middle column from sizing to its content's
     intrinsic min-width — without this, switching sections would shift
     the column wider or narrower based on what's inside. */
  grid-template-columns: 200px minmax(0, 1fr) minmax(560px, 46%);
  grid-template-areas:
    "bar  bar      side"
    "nav  editor   side";
}
.format-body[data-mode="editor"] > .format-editor {
  min-width: 0;
  width: 100%;
}
.format-body > .theme-gallery       { grid-area: gallery; }
.format-body > .format-editor       { grid-area: editor; margin-top: 0; }
.format-body > .format-editor-bar   { grid-area: bar; }
.format-body > .format-editor-nav   { grid-area: nav; }
.format-body > .format-side         { grid-area: side; }

/* Hide gallery + the editor pieces depending on the active mode.
   The editor BAR stays visible in gallery mode — clicking a theme card
   autosaves, so the save pill ("Saving… / Saved") must be visible there
   too; only the breadcrumb pieces that make no sense outside the editor
   are hidden. */
.format-body[data-mode="gallery"] > .format-editor,
.format-body[data-mode="gallery"] > .format-editor-nav {
  display: none;
}
.format-body[data-mode="gallery"] > .format-editor-bar > .format-editor-back,
.format-body[data-mode="gallery"] > .format-editor-bar > .format-editor-bar-divider,
.format-body[data-mode="gallery"] > .format-editor-bar > .format-editor-bar-title,
.format-body[data-mode="gallery"] > .format-editor-bar > #format-editor-create {
  display: none;
}
.format-body[data-mode="gallery"] > .format-editor-bar {
  border-bottom: none;
  padding-bottom: 0;
}
.format-body[data-mode="editor"] > .theme-gallery {
  display: none;
}

/* ---------- Format-side preview: real renders ---------- */
/* Two modes on .format-side[data-preview-mode]: 'ebook' shows the device
   bezel with the EPUB-CSS iframe; 'print' shows the embedded paperback
   previewer (real mPDF proof) with Refresh. */
.format-side[data-preview-mode="ebook"] .format-preview-print  { display: none; }
.format-side[data-preview-mode="print"] .format-preview-ebook  { display: none; }
.device-epub-frame {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  background: #fff;
}
/* Kindle's e-ink paper tint — applied as a filter over the REAL render
   instead of overriding the theme's background color. */
.device-frame[data-device="kindle"] .device-epub-frame {
  filter: sepia(.18) saturate(.85) contrast(.98);
}
/* A PILL, not loose text. This sits under the device on top of whatever
   backdrop image the author chose, so it carries its own contrast rather
   than borrowing the page's — a text-shadow was doing that job before and
   only worked on dark images.

   `max-width: fit-content` with auto side margins is what centres it: it
   stays a block (so it can be hidden by the .format-preview-ebook /
   -print switch) while shrinking to its text. */
.format-preview-caption {
  margin: 10px auto 0;
  max-width: fit-content;
  padding: 5px 12px;
  border-radius: 999px;
  font-family: var(--font-sans);
  font-size: 11.5px;
  line-height: 1.35;
  text-align: center;
  color: #efeade;
  background: rgba(28, 24, 18, .62);
  border: 1px solid rgba(255, 255, 255, .14);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.print-proof-stage {
  position: relative;
}
.print-proof-frame {
  display: block;
  width: 100%;
  /* --preview-h (set on .format-side) is the shared preview height, so the
     print proof and the three e-reader bezels are all the same size. */
  height: var(--preview-h, 620px);
  border: 1px solid var(--line, #d9d4c9);
  border-radius: 10px;
  background: #eceef1;
}
.print-proof-loading {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  background: rgba(250, 250, 249, .88);
  border-radius: 10px;
  font-family: var(--font-sans);
  font-size: 13.5px;
  color: var(--ink-2, #4a463e);
}
.print-proof-loading[hidden] { display: none; }
.print-proof-loading p { margin: 0; }
.print-proof-spinner {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 3px solid var(--line, #d9d4c9);
  border-top-color: var(--accent, #8b6914);
  animation: proof-spin .8s linear infinite;
}
@keyframes proof-spin { to { transform: rotate(360deg); } }
.print-proof-actions {
  display: flex;
  gap: 8px;
  justify-content: center;
  margin-top: 10px;
}
.print-proof-locked {
  border: 1px dashed var(--line, #d9d4c9);
  border-radius: 10px;
  padding: 28px 20px;
  text-align: center;
  font-family: var(--font-sans);
  font-size: 13.5px;
  color: var(--ink-2, #4a463e);
}
.print-proof-locked p { margin: 0 0 14px; line-height: 1.55; }

/* "Modified" pill on the active theme card — this book carries per-book
   tweaks on top of the theme (kebab → Reset clears them). */
.theme-card-modified {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 2;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--accent-bg, #f6efdb);
  border: 1px solid var(--accent, #8b6914);
  color: var(--accent, #8b6914);
  font-family: var(--font-sans);
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: .04em;
  text-transform: uppercase;
  pointer-events: none;
}

/* Editor breadcrumb bar */
.format-editor-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0 12px;
  border-bottom: 1px solid var(--line-soft);
  font-family: var(--font-sans);
  font-size: 13px;
}
.format-editor-back {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: transparent;
  border: 1px solid var(--line);
  padding: 6px 12px 6px 8px;
  border-radius: var(--radius-sm);
  color: var(--ink-2);
  cursor: pointer;
  font: inherit;
  font-size: 13px;
}
.format-editor-back:hover { background: var(--bg-paper); color: var(--ink-1); }
.format-editor-bar-divider { color: var(--ink-3); }
.format-editor-bar-title { color: var(--ink-3); }
.format-editor-bar-title strong { color: var(--ink-1); font-weight: 600; }
.format-editor-bar-spacer { flex: 1 1 auto; }

/* Save status pill in the customize bar. Color follows the lifecycle:
   gray (idle/saved), accent (dirty/saving), red (error). */
.format-save-status {
  font-size: 12px;
  color: var(--ink-3);
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--bg-rail);
  border: 1px solid var(--line-soft);
  white-space: nowrap;
}
.format-save-status[data-state="dirty"]  { color: var(--accent); border-color: rgba(139,105,20,.30); background: rgba(139,105,20,.08); }
.format-save-status[data-state="saving"] { color: var(--accent); border-color: rgba(139,105,20,.30); background: rgba(139,105,20,.08); font-style: italic; }
.format-save-status[data-state="saved"]  { color: #2a7d2a; border-color: rgba(42,125,42,.25); background: rgba(42,125,42,.08); }
.format-save-status[data-state="error"]  { color: #b03030; border-color: rgba(176,48,48,.30); background: rgba(176,48,48,.08); }

/* Editor left nav */
.format-editor-nav {
  position: sticky;
  top: 16px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 8px 0;
  align-self: start;
}
.format-editor-nav ul { list-style: none; margin: 0; padding: 0; }
.format-editor-nav li { margin: 0; }
.format-editor-nav-btn {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  padding: 9px 16px;
  font: inherit;
  font-size: 13.5px;
  color: var(--ink-2);
  cursor: pointer;
  border-left: 3px solid transparent;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.format-editor-nav-btn:hover { background: var(--bg-paper); color: var(--ink-1); }
.format-editor-nav-btn.is-active {
  background: var(--accent-bg);
  color: var(--ink-1);
  border-left-color: var(--accent);
  font-weight: 500;
}

/* In editor mode, only the active section is visible inside the
   format-advanced-body container. The grid layout collapses since we
   only ever show one section at a time. */
.format-body[data-mode="editor"] .format-advanced-body {
  display: block;
}
.format-body[data-mode="editor"] .inspector-section[data-section] {
  display: none;
}
.format-body[data-mode="editor"] .inspector-section.is-active-section {
  display: block;
  /* Even sections that span both columns in 2-column mode show as a
     single block here. */
  grid-column: auto;
}

/* Theme gallery */
.theme-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 18px;
}
.theme-card {
  position: relative;
  display: flex;
  flex-direction: column;
  border: 2px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
  padding: 0;
  cursor: pointer;
  overflow: hidden;
  transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
  font-family: inherit;
  text-align: left;
}
.theme-card:hover {
  border-color: var(--accent-soft);
  transform: translateY(-2px);
  box-shadow: 0 6px 18px rgba(60,52,32,.10);
}
.theme-card.is-selected {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(139,105,20,.15);
}
.theme-card-preview {
  display: block;
  padding: 22px 22px 18px;
  min-height: 130px;
  border-bottom: 1px solid var(--line-soft);
}
.theme-card-heading {
  display: block;
  font-size: 16px;
  font-weight: 600;
  margin-bottom: 8px;
  letter-spacing: .02em;
}
.theme-card-body {
  display: block;
  font-size: 12px;
  line-height: 1.5;
}
.theme-card-dropcap {
  font-size: 28px;
  line-height: .9;
  font-weight: 700;
  float: left;
  margin: 2px 6px 0 0;
  font-family: var(--font-display);
}
.theme-card-name {
  display: block;
  padding: 10px 16px;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink-2);
  letter-spacing: .02em;
}
.theme-card-check {
  position: absolute;
  top: 10px; right: 10px;
  width: 22px; height: 22px;
  background: var(--accent);
  color: #fff;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
  opacity: 0;
  transition: opacity .15s ease;
}
.theme-card.is-selected .theme-card-check { opacity: 1; }

/* Theme card wrapper holds the card itself + the kebab menu button.
 * The wrapper participates in the gallery grid the same way a bare
 * card did before, so layout is unchanged. */
.theme-card-wrap {
  position: relative;
  display: flex;
}
.theme-card-wrap .theme-card { flex: 1; }

/* "Default for new books" badge — shows on the user's chosen default
   theme so they can see at a glance which one new books inherit from.
   Pinned to the bottom-left so it doesn't compete with the selected
   check (top-right) or the kebab menu. */
.theme-card-wrap.is-default::after {
  content: 'Default';
  position: absolute;
  bottom: 38px;
  left: 8px;
  padding: 2px 7px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-family: var(--font-sans);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  pointer-events: none;
  box-shadow: 0 1px 3px rgba(0,0,0,.15);
}
.theme-card-menu {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, .92);
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--ink-3);
  cursor: pointer;
  opacity: 0;
  transition: opacity .12s ease, background .12s ease, color .12s ease;
}
.theme-card-wrap:hover .theme-card-menu,
.theme-card-menu:focus-visible { opacity: 1; }
.theme-card-menu:hover {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

/* "+ Create new theme" card — accent-tinted call-to-action at the
 * start of the gallery. */
.theme-card-new {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  text-align: center;
  cursor: pointer;
  min-height: 200px;
}
.theme-card-new:hover {
  background: var(--accent-soft);
  border-color: var(--accent-soft);
  color: #fff;
}
.theme-card-new-icon {
  font-size: 36px;
  font-weight: 300;
  line-height: 1;
}
.theme-card-new-label {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .12em;
  line-height: 1.4;
}

/* Theme actions popover (anchored next to the kebab button) */
.theme-action-pop {
  position: absolute;
  z-index: 240;
  min-width: 180px;
  background: #fff;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  padding: 4px;
  display: flex;
  flex-direction: column;
  animation: preset-pop-in .12s ease-out;
}
.theme-action-pop[hidden] { display: none; }
.theme-action-pop-item {
  background: transparent;
  border: none;
  font: inherit;
  font-size: 13px;
  text-align: left;
  padding: 8px 12px;
  color: var(--ink-1);
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.theme-action-pop-item:hover { background: var(--accent-soft); color: var(--ink-1); }
.theme-action-pop-item.theme-action-pop-danger { color: var(--danger); }
.theme-action-pop-item.theme-action-pop-danger:hover { background: var(--danger); color: #fff; }
/* On a built-in theme, only "Duplicate" is meaningful — hide the rest. */
.theme-action-pop.is-built-in .theme-action-pop-user { display: none; }

/* Format side column (device preview + export + advanced typography) */
.format-side {
  position: sticky;
  top: 16px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  /* ONE height for every preview surface — the print proof iframe and all
     three e-reader bezels read it, so switching devices doesn't resize the
     panel. Fills the viewport minus the app chrome, never comically short
     or tall (a device taller than ~1000px stops reading as a device). */
  --preview-h: clamp(620px, calc(100vh - 330px), 1000px);
}

/* Device shell centers and bounds the frame so it doesn't stretch with content. */
.device-shell {
  display: flex;
  justify-content: center;
}

/* ---- Device frame: realistic device chrome with a scrollable screen. ----
   Every device is exactly --preview-h tall (the same height as the print
   proof iframe), with its width derived from its own aspect ratio. All the
   bezel trim below is expressed in `--bezel-unit` = 460ths of that height —
   the size these values were originally hand-tuned at — so a device scales
   as one piece instead of growing a hairline bezel around a huge screen.
   max-width is a guard for very tall/narrow windows: the frame squats
   slightly rather than overflowing the column. */
.device-frame {
  position: relative;
  background: #1d1b18;
  box-shadow: 0 6px 24px rgba(0,0,0,.18);
  flex-shrink: 0;
  --bezel-unit: calc(var(--preview-h, 620px) / 460);
  height: var(--preview-h, 620px);
  width: auto;
  max-width: 100%;
  min-width: 0;
  /* Per-device ratios live below — this is a reasonable default. */
  aspect-ratio: 320 / 460;
  border-radius: calc(var(--bezel-unit) * 22);
  padding: calc(var(--bezel-unit) * 14);
}
/* iPad: rounded corners, even bezel, tiny camera dot at the top */
.device-frame[data-device="ipad"] {
  aspect-ratio: 340 / 460;
  border-radius: calc(var(--bezel-unit) * 22);
  padding: calc(var(--bezel-unit) * 18) calc(var(--bezel-unit) * 14);
  background: #1d1b18;
}
.device-frame[data-device="ipad"]::before {
  content: '';
  position: absolute;
  top: calc(var(--bezel-unit) * 7); left: 50%;
  width: calc(var(--bezel-unit) * 5); height: calc(var(--bezel-unit) * 5);
  background: #3a3733;
  border-radius: 50%;
  transform: translateX(-50%);
}
/* Kindle Oasis: thicker bezel on the left for the page-turn buttons */
.device-frame[data-device="kindle"] {
  aspect-ratio: 320 / 460;
  border-radius: calc(var(--bezel-unit) * 8);
  padding: calc(var(--bezel-unit) * 14) calc(var(--bezel-unit) * 14) calc(var(--bezel-unit) * 14) calc(var(--bezel-unit) * 38);
  background: #2a2723;
}
.device-frame[data-device="kindle"]::before,
.device-frame[data-device="kindle"]::after {
  content: '';
  position: absolute;
  left: calc(var(--bezel-unit) * 12);
  width: calc(var(--bezel-unit) * 14);
  height: calc(var(--bezel-unit) * 4);
  background: #555048;
  border-radius: calc(var(--bezel-unit) * 2);
}
.device-frame[data-device="kindle"]::before { top: 38%; }
.device-frame[data-device="kindle"]::after  { top: 56%; }

/* Phone: tall, very rounded corners, a notch at the top */
.device-frame[data-device="phone"] {
  aspect-ratio: 240 / 470;
  border-radius: calc(var(--bezel-unit) * 28);
  padding: calc(var(--bezel-unit) * 36) calc(var(--bezel-unit) * 12);
  background: #1d1b18;
}
.device-frame[data-device="phone"]::before {
  content: '';
  position: absolute;
  top: calc(var(--bezel-unit) * 10); left: 50%;
  width: calc(var(--bezel-unit) * 70); height: calc(var(--bezel-unit) * 14);
  background: #000;
  border-radius: 0 0 calc(var(--bezel-unit) * 10) calc(var(--bezel-unit) * 10);
  transform: translateX(-50%);
}

/* Paperback: tan cover with a flat spine. (Print mode now shows the real
   embedded proof instead of this bezel; kept for the legacy markup path.) */
.device-frame[data-device="paperback"] {
  aspect-ratio: 320 / 460;
  background: #c9bfa5;
  border-radius: calc(var(--bezel-unit) * 4);
  padding: calc(var(--bezel-unit) * 8) calc(var(--bezel-unit) * 8) calc(var(--bezel-unit) * 8) calc(var(--bezel-unit) * 14);
  box-shadow: 0 6px 24px rgba(60,52,32,.22), inset 6px 0 0 rgba(0,0,0,.12);
}

.device-screen {
  width: 100%;
  height: 100%;
  background: #fdfbf6;
  border-radius: 4px;
  overflow-y: auto;
  font-family: var(--font-serif);
}
/* Kindle's e-ink paper stock, visible only for the instant before the
   preview iframe paints (the tint itself is a filter on .device-epub-frame). */
.device-frame[data-device="kindle"] .device-screen { background: #f4ecd8; }

/* RETIRED (prune completed): every device-mock rule — .device-page,
   .device-chapter-*, .device-running-head, .device-folio*, .device-opening-bg,
   .device-opening-banner, .device-plate-image — is gone, both the block that
   lived here and the scattered remainder that used to sit in the 7700-8300
   range. They styled a hand-rolled DOM approximation of an e-reader page,
   replaced by an iframe of the REAL EPUB exporter output (editor.php's
   #device-epub-frame, fed by format.js from
   /api/books/{id}/epub-preview/{chapterId}). An iframe's document does not
   inherit this stylesheet at all, so no `.device-*` content rule can ever
   apply again — the EPUB carries its own CSS.

   STILL LIVE — do not remove. These ten are the only device-* names any PHP
   or JS file references, and they are the bezel and controls around that
   iframe: .device-shell, .device-frame, .device-screen, .device-epub-frame,
   .device-nav, .device-nav-btn, .device-prev, .device-next, .device-select,
   .device-width. Before deleting any `.device-*` rule, grep assets/js and src
   for the bare class name; if it appears nowhere, it cannot match. */

/* Subtle scrollbar inside the screen */
.device-screen::-webkit-scrollbar       { width: 6px; }
.device-screen::-webkit-scrollbar-thumb { background: rgba(0,0,0,.18); border-radius: 3px; }
.device-screen::-webkit-scrollbar-track { background: transparent; }
/* (More retired device-mock rules were removed from here — see the note
   above. The two CSS vars they referenced, --pv-body-pt and
   --pv-paragraph-spacing, had no setter anywhere in the repo, and the old
   comment claimed paragraph spacing came "directly from the user's theme
   settings" when nothing set it.) */
.device-nav {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  padding: 4px 0 0;
  /* Centered and bounded: the preview column is wide now, and two
     column-width chapter buttons under a centered device looked adrift. */
  width: 100%;
  max-width: 340px;
  margin-inline: auto;
}
.device-nav-btn {
  background: #fff;
  color: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 8px 12px;
  font-family: inherit;
  font-size: 12px;
  cursor: pointer;
}
.device-nav-btn:hover { background: var(--bg-rail); color: var(--ink-1); border-color: var(--line-strong); }

.btn-block { width: 100%; }

/* Disabled state on export anchors during generation. <a> doesn't honor
   the `disabled` attribute, so this gives us the visual + pointer cue. */
.btn[aria-busy="true"] {
  opacity: .65;
  pointer-events: none;
  cursor: progress;
}
.btn[aria-busy="true"]::after {
  content: "";
  display: inline-block;
  width: 12px;
  height: 12px;
  margin-left: 8px;
  vertical-align: -2px;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-right-color: transparent;
  animation: scrived-spin .7s linear infinite;
}

/* (The export progress toast is the shared Scrived.toast since 2.90 —
   export.js had grown a private #export-toast widget beside it.) */

@keyframes scrived-spin { to { transform: rotate(360deg); } }

/* ----- Format editor (the inspector, now in the main column) ----- */
.format-editor {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  margin-top: 24px;
  box-shadow: var(--shadow-paper);
  overflow: hidden;
  /* Hold a minimum height so switching from a tall section (e.g.
     Chapter heading) to a short one (Notes & footnotes) doesn't
     reflow the page or shift the device-preview column up. The
     value is roughly the height of the device preview at default
     viewport size — they stay shoulder-to-shoulder either way. */
  min-height: 540px;
}
.format-editor-head {
  padding: 16px 22px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--bg-paper);
}
.format-editor-title {
  margin: 0 0 4px;
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 600;
  color: var(--ink-1);
}
.format-editor-sub {
  margin: 0;
  font-size: 12.5px;
  color: var(--ink-3);
}
.format-advanced-body {
  padding: 22px 22px 26px;
  display: grid;
  /* Two-column form layout when the main area is wide enough,
     single column when it isn't. */
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 24px 32px;
}
.format-advanced-body .inspector-section {
  margin: 0;
  /* Sections that contain wide controls (radio grids) span both columns
     so options aren't squeezed. Mark them by adding class .inspector-section-wide
     in the markup if needed; default keeps one column. */
}
.format-advanced-body .inspector-section .section-heading {
  margin-top: 0;
}
/* Print layout has lots of toggles; let it span full width by default. */
.format-advanced-body .inspector-section:has(input[name="fmt-layout-priority"]),
.format-advanced-body .inspector-section.inspector-section-wide {
  grid-column: 1 / -1;
}
/* Header / Footer 2-column inner grid for the dropdowns */
.header-footer-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px 18px;
  margin-bottom: 14px;
}

/* Chapter heading "Fine-tune" details + nested subtitle/title blocks */
.format-subsection > summary {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-2);
  text-transform: uppercase;
  letter-spacing: .12em;
  cursor: pointer;
  padding: 6px 0;
  list-style: revert;
}
.format-subsection > summary::marker { color: var(--ink-3); }
.ch-heading-block { margin-top: 14px; }
.ch-heading-block-title {
  margin: 0 0 8px;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: .12em;
}

/* Inline footnote reference (rendered by Quill's FootnoteBlot).
   In the editor we show a tiny pill that hints at footnote presence;
   the actual text lives in data-fn-text. */
.ql-editor sup.fn-ref {
  background: var(--accent-soft);
  color: #fff;
  font-size: 0.7em;
  padding: 0 5px;
  border-radius: 999px;
  cursor: help;
  font-weight: 600;
  vertical-align: super;
}
.ql-editor sup.fn-ref:hover {
  background: var(--accent);
}

/* In the published view, the Quill marker gets replaced with a
   numbered superscript by the renderer. The inline pill style is
   editor-only; print/EPUB use a plain superscript number. */
.preview-page .chapter-body sup.fn-ref,
.print-page .chapter-body sup.fn-ref {
  background: transparent;
  color: var(--accent);
  padding: 0;
  border-radius: 0;
  font-weight: 600;
}

/* Footnotes block at end of chapter / end of book */
.footnotes-section {
  margin-top: 2em;
  padding-top: 1em;
  border-top: 1px solid var(--line);
  font-size: var(--pv-fn-size, 0.85em);
  color: var(--ink-2);
}
.footnotes-section .footnotes-heading {
  font-family: var(--pv-heading-font);
  font-size: 1em;
  font-weight: 600;
  margin: 0 0 .8em;
  text-transform: uppercase;
  letter-spacing: .1em;
}
.footnotes-list { list-style: decimal; padding-left: 2em; margin: 0; }
.footnotes-list li { margin-bottom: .4em; line-height: 1.45; }

/* Scene-break ornament image upload + preview */
.scene-break-image-row {
  margin-top: 10px;
  padding: 12px;
  background: var(--bg-paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
}
.scene-break-image-row[hidden] { display: none; }
.scene-break-image-preview {
  background: #fff;
  border: 1px dashed var(--line);
  border-radius: var(--radius-sm);
  min-height: 60px;
  padding: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 8px;
}
.scene-break-image-preview img {
  max-width: 240px;
  max-height: 80px;
  display: block;
}
.scene-break-image-empty {
  color: var(--ink-3);
  font-size: 12px;
  font-style: italic;
}
.scene-break-image-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* When the rendered scene-break uses an image, the ::before glyph
   should not show; the <img> is the visual instead. */
.scene-break-image .scene-break-img {
  display: block;
  margin: 0 auto;
  /* SceneBreak::IMAGE_MAX_HEIGHT parity: 50% / 58px, same as every export. */
  max-width: 50%;
  max-height: 58px;
  height: auto;
}
.scene-break.scene-break-image::before { content: none !important; }
.preview-page .scene-break.scene-break-image::before { content: none !important; }

/* H2-H6 subheading rows: badge | font dropdown | size number */
.subheading-grid {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.subheading-row {
  display: grid;
  grid-template-columns: 40px 1fr 130px;
  align-items: end;
  gap: 12px;
}
.subheading-label {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 13px;
  color: var(--ink-2);
  height: 36px;
}
.subheading-row .field { margin: 0; }
@media (max-width: 720px) {
  .subheading-row {
    grid-template-columns: 40px 1fr;
  }
  .subheading-row .subheading-size { grid-column: 2; }
}

.radio-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
}
.radio-grid-sm { grid-template-columns: repeat(5, 1fr); }

/* A text feature's SECONDARY SCALE (v2.73) — a second axis on the same
   feature, sitting under its style picker because that is what it
   modifies, rather than as a section of its own competing with it.

   auto-fit rather than a fixed column count: the number of options comes
   from the catalog, so a scale that ships three or five of them lays out
   correctly without a CSS change. That is the same reason the picker
   itself is a foreach over the config. */
/* ---- Text-feature style grid (v2.76 as a carousel; unrolled 3.12) ----

   The cards are LIVE: each holds the feature's canned sample rendered by
   the same compiled stylesheet the book uses, selected by the same data
   attribute. Nothing here describes what a style looks like — it shows
   it, which is the difference between a list of names and a choice.

   Every card is visible at once, wrapped to the rail's width. An
   ordinary .radio-grid with real radio inputs, so format.js saves it
   and the keyboard reaches every option. */
.tf-swatch-grid {
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 8px;
  padding: 2px;
}

.tf-swatch-card {
  gap: 7px;
  padding: 8px;
}
/* The stage the sample renders on. Paper-coloured and clipped, so a
   swatch reads as a scrap of the page rather than as another control. */
.tf-swatch {
  width: 100%;
  height: 76px;
  overflow: hidden;
  padding: 7px 8px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--bg-paper);
  font-family: var(--font-serif, Georgia, serif);
  /* 8px is the whole trick: every rule the compiler emits is in em, so
     the entire feature — insets, bubbles, ornaments — scales down as one
     piece instead of being re-specified at swatch size. */
  font-size: 8px;
  line-height: 1.45;
  text-align: left;
  pointer-events: none;
  /* The colour tokens forBrowserAllPresets() emitted as var() references.
     Same aliases .page-sheet and #preview-stage define, so a swatch is
     tinted by the book's own accent rather than a fixed gold. */
  --tf-accent: var(--accent);
  --tf-text: var(--ink-1);
  --tf-bg: var(--bg-paper);
}
.tf-swatch .tf { margin: 0 !important; }
.tf-swatch p { margin: 0; }
.tf-swatch-card .radio-card-label { font-size: 11px; }

.tf-scale-row { margin-top: 12px; }
.tf-scale-label {
  display: block;
  margin-bottom: 6px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--ink-2);
}
.tf-scale-row .radio-grid {
  grid-template-columns: repeat(auto-fit, minmax(68px, 1fr));
  gap: 6px;
}
.tf-scale-row .radio-card { padding: 7px 6px; }
.tf-scale-row .radio-card-label { font-size: 11px; }
.radio-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 10px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  cursor: pointer;
  background: var(--bg-paper);
  transition: border-color .12s ease, background .12s ease;
}
.radio-card:hover { border-color: var(--accent-soft); }
.radio-card.is-selected {
  border-color: var(--accent);
  background: #fdf7e8;
  box-shadow: inset 0 0 0 1px var(--accent);
}
.radio-card input[type=radio] { position: absolute; opacity: 0; pointer-events: none; }
.radio-card-preview {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 14px;
  color: var(--ink-2);
}
.radio-card-label {
  font-size: 11px;
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: .08em;
}
.radio-card-glyph {
  font-size: 18px;
  color: var(--accent);
}

/* Mini chapter heading previews */
.chapter-preview-classic { letter-spacing: .14em; text-transform: uppercase; font-size: 11px; }
.chapter-preview-modern  { font-size: 13px; font-weight: 500; }
.chapter-preview-ornate  { font-style: italic; font-family: var(--font-serif); font-size: 13px; }
.chapter-preview-banner  { font-weight: 700; font-size: 14px; padding: 4px 8px; background: var(--ink-1); color: #fff; border-radius: 2px; }
.chapter-preview-minimal { font-weight: 400; font-size: 13px; color: var(--ink-3); }
.chapter-preview-caps    { text-transform: uppercase; letter-spacing: .12em; font-weight: 600; font-size: 11px; }
.chapter-preview-framed  { font-style: italic; font-size: 13px; border-top: 1px solid var(--accent); border-bottom: 1px solid var(--accent); padding: 2px 0; }
.chapter-preview-editorial { font-weight: 600; font-size: 13px; border-bottom: 1px solid var(--accent); padding-bottom: 2px; }
.chapter-preview-grand   { font-weight: 400; font-size: 15px; letter-spacing: .03em; }
.chapter-preview-double-rule { font-weight: 600; font-size: 12px; border-top: 3px double var(--accent); border-bottom: 3px double var(--accent); padding: 2px 6px; }
.chapter-preview-accent-bar { font-weight: 600; font-size: 13px; border-left: 3px solid var(--accent); padding-left: 6px; }

/* ============================================================
   PHASE 2 — Preview view
   ============================================================ */
.preview-view {
  background: #e7e2d6;
  display: flex !important;
  flex-direction: column;
  overflow: hidden;
}
.preview-view[hidden] { display: none !important; }

/* ============================================================
   Story bible view
   ============================================================ */
/* RETIRED: the `.editor-view.bible-view` container rules that lived here
   (display:grid, 320px + 1fr columns, the auto row for a sub-tab strip) are
   gone. The Codex is no longer an editor VIEW — it moved into a slide-in
   drawer (editor.php's #codex-drawer / .codex-drawer-panel, styled with the
   .codex-drawer-* rules), so nothing has emitted the `bible-view` class
   since and those rules could never match.

   Everything below is STILL LIVE: the sidebar, search, list and entry form
   all render inside the drawer body and keep their historical `.bible-*`
   names (BibleController, bible.js). Only the outer container changed. */
.bible-sidebar {
  display: flex;
  flex-direction: column;
  background: #fff;
  border-right: 1px solid var(--line);
  overflow: hidden;
}
/* v2.55: the head is a toolbar — the old "Story codex" heading was
   redundant under the drawer's own CODEX chrome. All three controls
   share the quiet pill look (user request); New entry just stretches
   to fill. Theme tokens only: the sidebar flips dark via the
   :root[data-theme] reset above. */
.bible-sidebar-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 14px 10px;
  border-bottom: 1px solid var(--line);
}
.bible-new-btn { flex: 1; }
.bible-tool-pills { display: flex; gap: 6px; flex-shrink: 0; }
.codex-tool-pill {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .02em;
  padding: 5px 12px;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: transparent;
  color: var(--ink-2);
  cursor: pointer;
  transition: color .12s ease, border-color .12s ease, background .12s ease;
}
.codex-tool-pill:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-bg);
}
.bible-sidebar-search {
  padding: 10px 14px;
  border-bottom: 1px solid var(--line-soft);
}
.bible-sidebar-search input {
  width: 100%;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 13px;
  background: var(--bg-paper);
}
.bible-sidebar-search input:focus { outline: none; border-color: var(--accent); }
.bible-sidebar-scroll {
  flex: 1;
  overflow-y: auto;
  padding: 8px 0 16px;
}
.bible-empty {
  padding: 18px 16px;
  margin: 0;
  font-size: 13px;
  color: var(--ink-3);
  font-style: italic;
}
.bible-group { margin: 12px 0 6px; }
.bible-group-head {
  margin: 0;
  /* Padding lives on the toggle button so the whole header row is the
     collapse/expand hit target. */
  padding: 0;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-3);
  display: flex;
  align-items: center;
}
.bible-group-toggle {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 6px 18px;
  background: none;
  border: none;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: left;
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.bible-group-toggle:hover { color: var(--ink-1); }
.bible-group-chevron {
  font-size: 9px;
  transform: rotate(90deg);   /* expanded; points right when collapsed */
  transition: transform .15s ease;
}
.bible-group.is-collapsed .bible-group-chevron { transform: none; }
.bible-group-head .bible-count {
  margin-left: auto;
  font-weight: 500;
  color: var(--ink-3);
  font-size: 10.5px;
}
.bible-group-list {
  list-style: none;
  margin: 4px 0;
  padding: 0;
}
.bible-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 16px 6px 15px;
  border-left: 3px solid transparent;
  cursor: pointer;
  transition: background .12s ease;
}
.bible-row:hover { background: var(--bg-paper); }
.bible-row.is-selected {
  background: #fdf7e8;
  border-left-color: var(--accent);
}
/* Thumbnail: the entry image, else its kind emoji on a colour tint. */
.bible-row-thumb {
  position: relative;   /* anchors the deceased-strike ::after (134) */
  flex: 0 0 36px;
  width: 36px;
  height: 36px;
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid var(--line-soft);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-rail);
}
.bible-row-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.bible-row-thumb-empty { font-size: 16px; }
.bible-row-text { min-width: 0; }
.bible-row-name {
  font-weight: 500;
  color: var(--ink-1);
  font-size: 13.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.bible-row-aliases {
  font-size: 11.5px;
  color: var(--ink-3);
  font-style: italic;
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ----- Bible main / form ----- */
.bible-main {
  overflow-y: auto;
  padding: 32px 48px;
}
.bible-empty-state {
  max-width: 520px;
  margin: 80px auto;
  text-align: center;
}
.bible-empty-state .empty-mark {
  font-size: 36px;
  color: var(--accent);
  margin-bottom: 12px;
}
.bible-empty-state .empty-heading {
  font-family: var(--font-display);
  font-size: 28px;
  margin: 0 0 12px;
}
.bible-empty-state .empty-sub {
  color: var(--ink-3);
  margin: 0 0 24px;
  line-height: 1.55;
}

.bible-form {
  max-width: 820px;
  margin: 0 auto;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-paper);
  overflow: hidden;
}
/* Detail head (v2.16): portrait card left, identity fields right.
   v2.20: an always-dark band in both themes (the library-band family
   look). Optional artwork arrives as an inline background-image from
   editor.php (scrimmed); this gradient is the no-art state AND the
   paint-behind while art decodes. ⚠ Same law as every dark band: the
   light text below uses scoped literals, never flipped --ink-* tokens. */
.bible-form-head {
  display: grid;
  grid-template-columns: 180px 1fr auto;
  gap: 18px;
  align-items: start;
  padding: 18px 22px;
  border-bottom: 1px solid rgba(255,255,255,.12);
  background:
    radial-gradient(85% 130% at 88% 0%,  rgba(200,160,66,.16) 0%, rgba(200,160,66,0) 55%),
    linear-gradient(175deg, #2b251d 0%, #1e1a15 55%, #15120e 100%);
  background-size: cover;
  background-position: center;
}
.bible-form-portrait {
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: center;
}
.bible-form-image {
  width: 180px;
  height: 240px;
  border-radius: var(--radius);
  background: #f0eadb;
  /* Pinned: sits on the always-dark head band in both themes. */
  border: 1px solid rgba(255,255,255,.3);
  box-shadow: 0 10px 26px rgba(0,0,0,.4);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  position: relative;
}
.bible-form-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Kind emoji on a tint of the entry colour (bible.js paints the tint). */
.bible-image-placeholder {
  font-size: 44px;
  line-height: 1;
  opacity: .75;
}
@media (max-width: 560px) {
  .bible-form-head { grid-template-columns: 1fr auto; }
  .bible-form-portrait { grid-column: 1 / -1; }
}
.bible-form-head-fields {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}
/* Head-only controls: pinned light literals (the band is dark in both
   themes — tokens here would flip the wrong way). */
.bible-name-input {
  width: 100%;
  border: none;
  outline: none;
  background: transparent;
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 600;
  color: #f3eee1;
  text-shadow: 0 1px 8px rgba(0,0,0,.4);
  padding: 4px 0;
  border-bottom: 1px solid transparent;
  transition: border-color .12s ease;
}
.bible-name-input:focus { border-bottom-color: #e8c882; }
.bible-name-input::placeholder { color: rgba(243,238,225,.5); font-weight: 400; }
.bible-form-row {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
}
.bible-mini-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 11px;
  color: #b3a993;
  text-transform: uppercase;
  letter-spacing: .08em;
}
.bible-mini-field select,
.bible-mini-field input[type=color] {
  font: inherit;
  font-size: 13px;
  text-transform: none;
  letter-spacing: 0;
  padding: 4px 8px;
  border: 1px solid rgba(255,255,255,.24);
  border-radius: var(--radius-sm);
  color: #f3eee1;
  background: rgba(255,255,255,.10);
}
/* The <select>'s dropdown popup is native-rendered on a light sheet. */
.bible-mini-field select option { color: #1a1a1a; background: #fff; }
.bible-mini-field input[type=color] {
  width: 50px;
  padding: 2px;
  height: 30px;
}
.bible-image-buttons {
  display: flex;
  gap: 6px;
  align-items: center;
}
/* Head action buttons (print + delete) stack in the grid's third column.
   Same dark-band law as everything in the head: pinned light literals. */
.bible-form-head-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-self: start;
}
.bible-form-print,
.bible-form-delete {
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(255,255,255,.08);
  border: 1px solid rgba(255,255,255,.22);
  border-radius: var(--radius-sm);
  color: #b3a993;
  cursor: pointer;
  align-self: start;
}
/* Print is a neutral action — gold lift, not the delete red. */
.bible-form-print:hover { color: #e8c882; border-color: #e8c882; }
/* Lifted red — the theme --danger values don't read on the dark band. */
.bible-form-delete:hover { color: #e0897b; border-color: #e0897b; }

/* Head-scoped variants of the shared field/button classes (the same
   classes render on the light body below, so scope, don't restyle). */
.bible-form-head .bible-field-label { color: #d8d0bf; }
.bible-form-head .bible-field-hint { color: #b3a993; }
.bible-form-head .bible-field input {
  background: rgba(255,255,255,.10);
  border-color: rgba(255,255,255,.24);
  color: #f3eee1;
}
.bible-form-head .bible-field input::placeholder { color: rgba(243,238,225,.45); }
.bible-form-head .bible-field input:focus {
  background: rgba(255,255,255,.16);
  border-color: #e8c882;
  box-shadow: 0 0 0 3px rgba(232,200,130,.25);
}
.bible-form-head .btn-ghost { color: #f3eee1; }
.bible-form-head .btn-ghost:hover {
  background: rgba(255,255,255,.12);
  border-color: rgba(255,255,255,.22);
}
.bible-form-head .btn-link { color: #b3a993; }
.bible-form-head .btn-link:hover { color: #f3eee1; }

.bible-form-body {
  padding: 22px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.bible-field { display: flex; flex-direction: column; gap: 6px; }
.bible-field-label {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-2);
}
.bible-field-hint {
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink-3);
  margin-left: 6px;
  font-size: 11px;
}
.bible-field input,
.bible-field textarea {
  font: inherit;
  font-size: 14px;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-paper);
  color: var(--ink-1);
  resize: vertical;
}
.bible-field input:focus,
.bible-field textarea:focus { outline: none; border-color: var(--accent); background: var(--bg-card); }
.bible-field textarea {
  font-family: var(--font-serif, Georgia, serif);
  line-height: 1.5;
  min-height: 240px;
}

/* Notes mini-Quill (v2.28): toolbar + editor styled to read as one field
   in the drawer's light body (tokens, not literals — the dark-band law
   only covers the head). Quill stamps ql-toolbar/ql-container/ql-snow
   onto these at boot, so the selectors pair our class with Quill's to
   beat quill.snow.css's own border rules. */
.bible-notes-toolbar.ql-toolbar.ql-snow {
  border: 1px solid var(--line);
  border-bottom: none;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  background: var(--bg-paper);
  padding: 5px 8px;
}
.bible-notes-editor.ql-container.ql-snow {
  border: 1px solid var(--line);
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  background: var(--bg-paper);
  font-family: var(--font-serif, Georgia, serif);
  font-size: 14px;
}
.bible-notes-editor .ql-editor {
  min-height: 240px;
  line-height: 1.5;
  color: var(--ink-1);
  padding: 9px 12px;
}
.bible-notes-editor .ql-editor.ql-blank::before {
  color: var(--ink-3);
  font-style: normal;
  left: 12px;
  right: 12px;
}
.bible-notes-toolbar .ql-stroke { stroke: var(--ink-2); }
.bible-notes-toolbar .ql-fill { fill: var(--ink-2); }
.bible-notes-toolbar button:hover .ql-stroke,
.bible-notes-toolbar button.ql-active .ql-stroke { stroke: var(--accent); }
.bible-notes-toolbar button:hover .ql-fill,
.bible-notes-toolbar button.ql-active .ql-fill { fill: var(--accent); }

.bible-form-foot {
  padding: 12px 22px;
  border-top: 1px solid var(--line);
  background: var(--bg-paper);
  display: flex;
  justify-content: flex-end;
}
.bible-save-status {
  font-size: 12px;
  color: var(--ink-3);
  font-style: italic;
}

/* ----- Codex relationships (118): rows in the detail form ----- */
.bible-links-list {
  list-style: none;
  margin: 0 0 8px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.bible-link-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  background: var(--bg-paper);
  font-size: 13px;
}
.bible-link-glyph { color: var(--ink-3); font-size: 12px; }
.bible-link-name {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-weight: 600;
  color: var(--ink-1);
  cursor: pointer;
}
.bible-link-name:hover { color: var(--accent); text-decoration: underline; }
.bible-link-label {
  color: var(--ink-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.bible-link-spacer { flex: 1; }
.bible-link-edit, .bible-link-del { flex: 0 0 auto; }
.bible-link-del:hover { color: var(--danger); }
.bible-link-none {
  font-size: 12.5px;
  color: var(--ink-3);
  font-style: italic;
  padding: 2px 0;
}

/* The shared relationship modal (drawer + Map). It opens FROM the codex
   drawer (z-index 1000), so the generic .modal level (100) buries it —
   it must stack above the drawer or "+ Add relationship" types into a
   panel the user can't see. */
#bible-link-modal { z-index: 1200; }
.bible-link-card { max-width: 460px; }
.bible-link-endpoints {
  display: flex;
  gap: 10px;
  align-items: end;
}
.bible-link-endpoints .field { flex: 1; min-width: 0; }
.bible-link-endpoints select { width: 100%; }
.bible-link-and {
  padding-bottom: 9px;
  font-size: 12.5px;
  color: var(--ink-3);
}
.bible-link-actions { align-items: center; }
.bible-link-actions-spacer { flex: 1; }
.bible-link-modal-delete { color: var(--danger); }

/* ----- Body tabs (v2.23): Details | Relationships | Notes ----- */
/* Same pill pattern as .kdp-pane-tab; one section visible at a time so
   the form doesn't demand vertical scrolling. */
.bible-pane-tabs {
  display: flex;
  gap: 6px;
}
.bible-pane-tab {
  font: inherit;
  font-size: 13px;
  padding: 6px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg-card);
  color: var(--ink-3);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.bible-pane-tab:hover { background: var(--bg-rail); color: var(--ink-1); }
.bible-pane-tab.is-active {
  background: var(--accent-bg);
  border-color: var(--accent-soft);
  color: var(--ink-1);
  font-weight: 600;
}
.bible-pane-count {
  font-size: 10.5px;
  font-weight: 600;
  line-height: 1;
  padding: 3px 7px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  color: var(--ink-2);
}
.bible-pane {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.bible-pane[hidden] { display: none; }

/* Single-pane types (v2.41). A lore note has only Notes, so bible.js
   hides the whole tab strip (the global [hidden] rule at the top of this
   file beats the flex display) and marks the body — the Notes editor
   then takes the room the strip and the other panes freed. Individual
   .bible-pane-tab elements are hidden the same way when a type drops one
   tab but keeps two. */
.bible-form-body.is-single-pane .bible-pane,
.bible-form-body.is-single-pane .bible-notes-field,
.bible-form-body.is-single-pane .bible-notes-editor.ql-container.ql-snow {
  flex: 1 1 auto;
}
.bible-form-body.is-single-pane .bible-notes-editor .ql-editor {
  min-height: 320px;
}

/* ----- Details rows (126): label/value quick facts above Notes ----- */
.bible-details-rows {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 10px;
}
.bible-details-rows:empty { margin-bottom: 0; }
.bible-detail-row {
  display: grid;
  grid-template-columns: 130px 1fr auto;
  gap: 8px;
  align-items: center;
}
.bible-detail-row .bible-detail-label { font-weight: 600; }
/* Both row buttons share the grid's third (auto) column, so the × keeps the
   same right edge whether or not the row also offers Copy. */
.bible-detail-actions {
  display: flex;
  align-items: center;
  gap: 2px;
}
/* Copy — shown only on rows whose label is one of COPYABLE_LABELS in
   bible.js (today: "Image prompt"), whose value is written to be pasted into
   another tool and is far longer than the input can show. */
.bible-detail-copy {
  border: 1px solid var(--line);
  background: none;
  color: var(--ink-2);
  font-size: 11.5px;
  line-height: 1;
  padding: 5px 8px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  white-space: nowrap;
}
.bible-detail-copy:hover:not(:disabled) {
  color: var(--ink-1);
  background: var(--bg-rail);
  border-color: var(--line-strong);
}
.bible-detail-copy:disabled { cursor: default; opacity: .85; }
.bible-detail-del {
  border: none;
  background: none;
  color: var(--ink-3);
  font-size: 15px;
  line-height: 1;
  padding: 4px 6px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.bible-detail-del:hover {
  color: var(--danger, #b04646);
  background: var(--bg-rail);
}

/* ----- Ambiguous-mention chooser + deceased strikes (v2.29) ----------- */
/* Chooser: second body state of the mention popover — a row per entry
   sharing the hovered name/alias. Light popover surface → tokens. */
.bible-mention-choices {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.bible-mention-choices[hidden] { display: none; }
.bible-mention-choice {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 8px;
  background: none;
  border: none;
  border-left: 3px solid var(--line-soft);
  border-radius: 6px;
  font: inherit;
  color: var(--ink-1);
  text-align: left;
  cursor: pointer;
}
.bible-mention-choice:hover { background: var(--bg-paper); }
.bible-mention-choice-thumb {
  position: relative;
  flex: 0 0 24px;
  width: 24px;
  height: 24px;
  border-radius: 6px;
  overflow: hidden;
  border: 1px solid var(--line-soft);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.bible-mention-choice-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.bible-mention-choice-thumb-empty { font-size: 13px; }
.bible-mention-choice-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
}
.bible-mention-choice-kind {
  margin-left: auto;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--ink-3);
}

/* Deceased mark (134): a big red X across the portrait, pinned #d05548
   — reads on the dark band, on light thumbs, and in both themes. Each
   line is centred, rotated to THAT box's own corner angle, and made
   over-long: the container's overflow:hidden clips it, so the X lands
   corner to corner at any size without per-box length math. The popover
   img needs its wrapper span (a pseudo-element can't attach to a
   replaced element). */
#bible-form-image.is-deceased::before,
#bible-form-image.is-deceased::after,
.bible-row-thumb.is-deceased::before,
.bible-row-thumb.is-deceased::after,
.bible-mention-imgwrap.is-deceased::before,
.bible-mention-imgwrap.is-deceased::after,
.bible-mention-choice-thumb.is-deceased::before,
.bible-mention-choice-thumb.is-deceased::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  background: #d05548;
  border-radius: 2px;
  pointer-events: none;
  z-index: 2;
}
/* Drawer portrait — 180×240, so the corner angle is atan(240/180). */
#bible-form-image.is-deceased::before,
#bible-form-image.is-deceased::after {
  width: 320px;
  height: 6px;
  box-shadow: 0 0 3px rgba(0,0,0,.35);
}
#bible-form-image.is-deceased::before { transform: translate(-50%, -50%) rotate(53.13deg); }
#bible-form-image.is-deceased::after  { transform: translate(-50%, -50%) rotate(-53.13deg); }
/* Sidebar thumb (36×36), popover image (56×56) and chooser thumb
   (24×24) are square → 45°. */
.bible-row-thumb.is-deceased::before,
.bible-row-thumb.is-deceased::after { width: 60px; height: 3px; }
.bible-mention-imgwrap.is-deceased::before,
.bible-mention-imgwrap.is-deceased::after { width: 90px; height: 4px; }
.bible-mention-choice-thumb.is-deceased::before,
.bible-mention-choice-thumb.is-deceased::after { width: 44px; height: 2.5px; }
.bible-row-thumb.is-deceased::before,
.bible-mention-imgwrap.is-deceased::before,
.bible-mention-choice-thumb.is-deceased::before { transform: translate(-50%, -50%) rotate(45deg); }
.bible-row-thumb.is-deceased::after,
.bible-mention-imgwrap.is-deceased::after,
.bible-mention-choice-thumb.is-deceased::after { transform: translate(-50%, -50%) rotate(-45deg); }

.bible-mention-imgwrap {
  position: relative;
  display: block;
  flex-shrink: 0;
  line-height: 0;      /* no inline descender gap under the image */
  overflow: hidden;    /* clips the over-long X arms to the image box */
  border-radius: 6px;
}
.bible-mention-imgwrap[hidden] { display: none; }
.bible-mention-deceased {
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: #c0453a;
  font-weight: 600;
}

/* Deceased flag lives in the head band's Type/Color row — the same
   mini-field shape (caption above, control below) so the three captions
   line up; the when/how field drops in under the row and inherits the
   head's pinned-light .bible-field chrome. Always-dark band ⇒ literals. */
.bible-deceased-mini { cursor: pointer; }
.bible-deceased-mini input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin: 6px 0 0;
  padding: 0;
  accent-color: #d05548;
  cursor: pointer;
}
.bible-deceased-mini input[type="checkbox"]:disabled { opacity: .45; cursor: default; }
/* Caption + note label lift red once the flag is on. */
.bible-deceased-mini:has(input:checked) span { color: #e8938a; }
.bible-deceased-note-field .bible-field-label { color: #e8938a; }
/* .bible-field sets display:flex, which beats [hidden]'s UA display:none
   — without this the when/how field would never hide. */
.bible-deceased-note-field[hidden] { display: none; }

/* ----- Dark form body (v2.28) ----------------------------------------
   The pane area below the head band goes dark too — a study-brown a
   couple of steps LIGHTER than the head, so head / body / light fields
   read as three layers instead of a white sheet under a dark band.
   Same law as every dark surface: pinned literals in both themes,
   never flipped --ink-* tokens. The FIELDS deliberately stay light —
   that contrast is the point — so their chrome is pinned light too
   (tokens would flip them dark in dark mode). */
.bible-form-body { background: #373026; }
.bible-form-body .bible-field-label { color: #d8d0bf; }
.bible-form-body .bible-field-hint  { color: #b3a993; }

/* Light fields on the dark body (details rows + any future body input). */
.bible-form-body .bible-field input {
  background: #fdfbf6;
  color: #1a1a1a;
  border-color: #cfc7b4;
}
.bible-form-body .bible-field input::placeholder { color: #9a9384; }
.bible-form-body .bible-field input:focus {
  background: #fff;
  border-color: #e8c882;
  box-shadow: 0 0 0 3px rgba(232,200,130,.25);
}

/* Tab pills on the dark surface; the active tab flips to solid gold. */
.bible-form-body .bible-pane-tab {
  background: rgba(255,255,255,.08);
  border-color: rgba(255,255,255,.22);
  color: #d8d0bf;
}
.bible-form-body .bible-pane-tab:hover {
  background: rgba(255,255,255,.15);
  color: #f3eee1;
}
.bible-form-body .bible-pane-tab.is-active {
  background: #e8c882;
  border-color: #e8c882;
  color: #241d12;
}
.bible-form-body .bible-pane-tab .bible-pane-count {
  background: rgba(255,255,255,.18);
  color: inherit;
}

/* Details row × */
.bible-form-body .bible-detail-del { color: #b3a993; }
.bible-form-body .bible-detail-del:hover {
  color: #e0897b;
  background: rgba(255,255,255,.08);
}
/* Same band-scoped palette as the rest of the dark form body — the global
   --ink-* tokens are light-on-dark wrong in here. */
.bible-form-body .bible-detail-copy {
  color: #e8e1d2;
  border-color: rgba(255,255,255,.28);
}
.bible-form-body .bible-detail-copy:hover:not(:disabled) {
  color: #fff;
  background: rgba(255,255,255,.15);
  border-color: rgba(255,255,255,.45);
}

/* Ghost buttons (+ Add detail / + Add relationship) — head's treatment. */
.bible-form-body .btn-ghost {
  color: #f3eee1;
  background: rgba(255,255,255,.06);
  border-color: rgba(255,255,255,.28);
}
.bible-form-body .btn-ghost:hover {
  background: rgba(255,255,255,.15);
  border-color: rgba(255,255,255,.4);
  color: #fff;
}

/* Relationship rows read as light field cards, like the inputs. */
.bible-form-body .bible-link-row {
  background: #fdfbf6;
  border-color: #cfc7b4;
}
.bible-form-body .bible-link-glyph { color: #6a665e; }
.bible-form-body .bible-link-name  { color: #1a1a1a; }
.bible-form-body .bible-link-name:hover { color: #8b6914; }
.bible-form-body .bible-link-label { color: #3a3733; }
.bible-form-body .bible-link-row .btn-link { color: #6a665e; }
.bible-form-body .bible-link-row .btn-link:hover { color: #1a1a1a; }
.bible-form-body .bible-link-row .bible-link-del:hover { color: #a93b2a; }
.bible-form-body .bible-link-none { color: #b3a993; }

/* Notes mini-Quill: the whole block is one light field. Pins beat the
   token-based rules above in both themes. */
.bible-form-body .bible-notes-toolbar.ql-toolbar.ql-snow {
  background: #f3efe6;
  border-color: #cfc7b4;
}
.bible-form-body .bible-notes-editor.ql-container.ql-snow {
  background: #fdfbf6;
  border-color: #cfc7b4;
}
.bible-form-body .bible-notes-editor .ql-editor { color: #1a1a1a; }
.bible-form-body .bible-notes-editor .ql-editor.ql-blank::before { color: #9a9384; }
.bible-form-body .bible-notes-toolbar .ql-stroke { stroke: #5a5648; }
.bible-form-body .bible-notes-toolbar .ql-fill { fill: #5a5648; }
.bible-form-body .bible-notes-toolbar button:hover .ql-stroke,
.bible-form-body .bible-notes-toolbar button.ql-active .ql-stroke { stroke: #8b6914; }
.bible-form-body .bible-notes-toolbar button:hover .ql-fill,
.bible-form-body .bible-notes-toolbar button.ql-active .ql-fill { fill: #8b6914; }

/* Footer joins the dark surface, one step deeper. */
.bible-form-foot {
  background: #2c251e;
  border-top-color: rgba(255,255,255,.12);
}
.bible-form-foot .bible-save-status { color: #b3a993; }
.bible-form-foot .btn-ghost {
  color: #f3eee1;
  background: rgba(255,255,255,.06);
  border-color: rgba(255,255,255,.28);
}
.bible-form-foot .btn-ghost:hover {
  background: rgba(255,255,255,.15);
  color: #fff;
}

/* ----- Full-size portrait viewer ----- */
/* Same stacking need as the link modal: it opens from inside the codex
   drawer (z 1000), so the generic .modal level (100) would bury it. */
#bible-lightbox { z-index: 1200; }
.bible-lightbox-card {
  margin: 0;                      /* figure default margin */
  width: auto;
  max-width: min(860px, 92vw);
  padding: 14px 14px 12px;
  text-align: center;
}
.bible-lightbox-card img {
  display: block;
  max-width: 100%;
  max-height: min(78vh, 900px);
  width: auto;
  height: auto;
  margin: 0 auto;
  border-radius: 6px;
}
.bible-lightbox-name {
  margin: 10px 4px 2px;
  font-family: var(--font-display);
  font-size: 15px;
  color: var(--ink-1);
}
.bible-lightbox-close {
  position: absolute;
  top: -14px;
  right: -14px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--bg-card);
  color: var(--ink-2);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--shadow-1);
  display: flex;
  align-items: center;
  justify-content: center;
}
.bible-lightbox-close:hover {
  color: var(--ink-1);
  background: var(--bg-rail);
}
/* The portrait doubles as the viewer's opener when an image exists.
   Gold focus ring — it sits on the dark head band in both themes. */
.bible-form-image.is-zoomable { cursor: zoom-in; }
.bible-form-image.is-zoomable:hover img { opacity: .92; }
.bible-form-image.is-zoomable:focus-visible {
  outline: 2px solid #e8c882;
  outline-offset: 2px;
}

/* ----- Bible mention popover (writing view hover) ----- */
/* Codex mentions in the manuscript: every entry name/alias gets a quiet
   dotted underline so the writer can see what's in the codex at a glance.
   Painted by bible.js via the CSS Custom Highlight API — pure ranges,
   no markup injected into Quill's document. Only color/decoration
   properties are valid inside ::highlight. */
::highlight(codex-mentions) {
  text-decoration: underline dotted 1px;
  text-decoration-color: color-mix(in srgb, var(--accent) 60%, transparent);
}

.bible-mention-popover {
  position: fixed;
  z-index: 260;
  width: 280px;
  background: #fff;
  border: 1px solid var(--line-strong);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  padding: 12px 14px;
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--ink-1);
  pointer-events: auto;
  animation: preset-pop-in .12s ease-out;
}
.bible-mention-popover[hidden] { display: none; }
.bible-mention-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 8px;
}
.bible-mention-kind {
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--accent);
  font-weight: 600;
}
.bible-mention-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 15px;
  color: var(--ink-1);
}
.bible-mention-body {
  display: flex;
  gap: 10px;
  align-items: flex-start;
}
.bible-mention-image {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border-radius: 6px;
  flex-shrink: 0;
  border: 1px solid var(--line);
}
.bible-mention-text { flex: 1; min-width: 0; }
.bible-mention-summary {
  margin: 0 0 6px;
  line-height: 1.5;
  color: var(--ink-2);
  font-size: 12.5px;
}
.bible-mention-summary.is-empty { color: var(--ink-3); font-style: italic; }
.bible-mention-open {
  font-size: 12px;
  color: var(--accent);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
}
.bible-mention-open:hover { text-decoration: underline; }

/* ============================================================
   Resources view
   Same shell as every other Workshop tab now (v3.19): a
   publishing-head banner up top, then the resource list and its
   detail pane side by side below it, all in one scrolling column
   — no longer a standalone full-height grid. `.editor-view.is-active`
   (elsewhere in this file) supplies `display: flex` when the view is
   shown; this rule only needs to add the column direction.
   ============================================================ */
.editor-view.resources-view {
  flex-direction: column;
  background: var(--bg-page);
  overflow: hidden;
  position: relative;
}
.resources-view[hidden] { display: none !important; }

.resources-columns {
  display: flex;
  align-items: flex-start;
  gap: 24px;
}

/* Workshop page picker (v3.19) — replaces the old subview-tabs pill
   row (ten buttons, one per Workshop page) with a popover menu opened
   from the top-nav "Workshop" tab itself (editor.js), mirroring the
   editor's ✳ Insert menu (.tf-menu) look — its own class family since
   the two features are unrelated and will likely diverge. */
.wm-menu {
  position: fixed;
  z-index: 220;
  min-width: 262px;
  max-width: 340px;
  max-height: calc(100vh - 16px);
  overflow-y: auto;
  padding: 6px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 12px 34px rgba(0,0,0,.24), 0 2px 8px rgba(0,0,0,.14);
}
.wm-menu[hidden] { display: none; }
@media (min-width: 900px) {
  .wm-menu { width: 660px; max-width: 660px; column-count: 2; column-gap: 10px; }
}
.wm-menu-item {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  width: 100%;
  padding: 7px 10px;
  border: 0;
  border-radius: 7px;
  background: none;
  font: inherit;
  font-size: 13px;
  text-align: left;
  color: var(--ink-1);
  cursor: pointer;
  break-inside: avoid;
}
.wm-menu-item:hover { background: var(--accent-soft); }
/* .is-active is kept in sync for free by activateView()'s existing
   `[data-subview]` re-sync (editor.js) — the same mechanism that lit
   up the current pill in the old subview-tabs row. */
.wm-menu-item.is-active .wm-menu-text strong,
.wm-menu-item.is-active .wm-menu-glyph { color: var(--accent); }
.wm-menu-glyph {
  flex: 0 0 20px;
  font-size: 15px;
  line-height: 1.35;
  color: var(--accent);
  text-align: center;
}
.wm-menu-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
/* Explicit color, not inherited (168): the two menu items are <a>
   elements (ARC team / Author page link out rather than switching an
   in-editor subview), and the global `a:hover { color: var(--accent-soft) }`
   (assets/css/scrived.css:197) has a stronger selector than
   .wm-menu-item's own color — so on hover it silently won, painting the
   title the same color as its own :hover background and making it
   disappear. .wm-menu-text span (the description) already dodged this
   by having its own explicit color; give the title the same treatment. */
.wm-menu-text strong { font-size: 13.5px; font-weight: 600; color: var(--ink-1); }
.wm-menu-text span { font-size: 11.5px; line-height: 1.4; color: var(--ink-3); }

.resources-sidebar {
  display: flex;
  flex-direction: column;
  flex: 0 0 320px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius, 10px);
  overflow: hidden;
}
.resources-sidebar-head {
  padding: 16px 18px 8px;
  border-bottom: 1px solid var(--line);
}
.resources-sidebar-title {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--ink-2);
}
.resources-actions {
  padding: 12px 14px 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  border-bottom: 1px solid var(--line-soft);
}
.res-action-btn {
  width: 100%;
  justify-content: center;
}
.resources-filter {
  display: flex;
  gap: 6px;
  padding: 10px 14px 8px;
  flex-wrap: wrap;
}
.res-chip {
  background: var(--bg-paper);
  color: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 12px;
  font: inherit;
  font-size: 12px;
  cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.res-chip:hover { background: var(--accent-soft); }
.res-chip.is-active {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}
.resources-search {
  padding: 0 14px 10px;
  border-bottom: 1px solid var(--line-soft);
}
.resources-search input {
  width: 100%;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 13px;
  background: var(--bg-paper);
}
.resources-search input:focus { outline: none; border-color: var(--accent); }
.resources-sidebar-scroll {
  flex: 1;
  overflow-y: auto;
  padding: 8px 0 16px;
}
.res-empty {
  padding: 18px 16px;
  margin: 0;
  font-size: 13px;
  color: var(--ink-3);
  font-style: italic;
}
.res-group { margin: 12px 0 6px; }
.res-group-head {
  margin: 0;
  padding: 6px 18px;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-3);
  display: flex;
  align-items: center;
  gap: 6px;
}
.res-group-head .res-count { margin-left: auto; font-weight: 500; font-size: 10.5px; }
.res-group-list { list-style: none; margin: 4px 0; padding: 0; }
.res-row {
  padding: 8px 16px 8px 18px;
  border-left: 3px solid transparent;
  cursor: pointer;
  display: flex;
  gap: 10px;
  align-items: flex-start;
  transition: background .12s ease;
}
.res-row:hover { background: var(--bg-paper); }
.res-row.is-selected {
  background: #fdf7e8;
  border-left-color: var(--accent);
}
.res-row-icon { font-size: 18px; flex-shrink: 0; line-height: 1.2; }
.res-row-text { flex: 1; min-width: 0; }
.res-row-title {
  font-weight: 500;
  color: var(--ink-1);
  font-size: 13.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.res-row-sub {
  font-size: 11.5px;
  color: var(--ink-3);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Publisher Rocket companion panels (migrations 081/083, admin test phase). */
.kdp-rocket-runs,
.eaz-harvested {
  margin-top: 28px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}
.eaz-harvested .publishing-head { margin-bottom: 10px; }
.eaz-harv-group td {
  padding-top: 14px;
  border-bottom: 1px solid var(--line-strong);
  background: var(--bg-rail);
}
.eaz-harv-group .snap-link { margin-left: 10px; }

/* Rocket pane (083): queue harvests from the app. */
.kdp-rocket-form {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}
.kdp-rocket-form:first-of-type { border-top: none; padding-top: 0; }
.kdp-rocket-controls { align-items: end; gap: 12px; }
.kdp-rocket-controls .btn { margin-bottom: 2px; }
#rocket-keywords { font-family: var(--mono, monospace); font-size: 13px; }
.kdp-rocket-job {
  margin-top: 20px;
  padding: 14px 16px;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  background: var(--bg-rail);
}
.kdp-rocket-job-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.kdp-rocket-elapsed { font-size: 12px; color: var(--ink-3); }
.kdp-rocket-progress {
  margin: 10px 0 0;
  font-family: var(--mono, monospace);
  /* Wraps instead of truncating — a cut-off progress line is unreadable,
     and this text is the only live view of what the agent is doing. */
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-2);
  overflow-wrap: anywhere;
}
.kdp-rocket-error { margin: 8px 0 0; font-size: 13px; line-height: 1.5; color: var(--danger, #c0392b); overflow-wrap: anywhere; }
/* Running job whose agent heartbeat has gone quiet — visibly "not fine"
   without claiming failure yet. Same amber family as .pub-pill-paused. */
.kdp-rocket-stale {
  background: #fbf1cf;
  color: #6b5210;
  border-color: #efdf95;
}
.kdp-rocket-agent-hint { font-size: 12px; color: var(--ink-3); margin-left: 8px; }

/* Tool-pushed report chip (Publisher Rocket companion, migration 081). */
.res-rocket-chip {
  display: inline-block;
  margin-left: 6px;
  padding: 0 6px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  font-size: 10px;
  font-weight: 600;
  color: var(--ink-2);
  background: var(--bg-rail);
  vertical-align: 1px;
}

/* In-flight upload row. Subdued so it doesn't compete with finished
   files; the spinner gives unambiguous "I'm working on it" feedback. */
.res-row.is-uploading {
  cursor: progress;
  opacity: .85;
  background: var(--bg-rail);
}
.res-row.is-uploading:hover { background: var(--bg-rail); }
.res-row.is-uploading .res-row-title { color: var(--ink-2); font-style: italic; }
.res-row-spinner {
  display: inline-block;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid var(--line-strong);
  border-top-color: var(--accent);
  animation: res-spinner-rot .9s linear infinite;
}
@keyframes res-spinner-rot { to { transform: rotate(360deg); } }

/* ----- Resources main pane ----- */
.resources-main {
  flex: 1;
  min-width: 0;
  position: relative;
}
.resources-empty-state {
  max-width: 440px;
  margin: 48px auto;
  text-align: center;
}
.resources-empty-state .empty-heading {
  font-size: 15px;
  color: var(--ink-1);
  margin: 0 0 6px;
}
.resources-empty-state .empty-sub {
  font-size: 13px;
  color: var(--ink-3);
  margin: 0 0 18px;
  line-height: 1.55;
}

.resource-detail {
  max-width: 880px;
  margin: 0 auto;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-paper);
  overflow: hidden;
}
.resource-detail-head {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 14px;
  align-items: center;
  padding: 16px 22px;
  border-bottom: 1px solid var(--line);
  background: var(--bg-paper);
}
.resource-kind-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-family: var(--font-sans);
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .12em;
  background: var(--accent);
  color: #fff;
}
.resource-kind-badge[data-kind="link"] { background: #4a78c0; }
.resource-kind-badge[data-kind="note"] { background: #6b7a4a; }
.resource-title-input {
  width: 100%;
  border: none;
  outline: none;
  background: transparent;
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 600;
  color: var(--ink-1);
  padding: 4px 0;
  border-bottom: 1px solid transparent;
  transition: border-color .12s ease;
}
.resource-title-input:focus { border-bottom-color: var(--accent-soft); }
.resource-detail-delete {
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--ink-3);
  cursor: pointer;
}
.resource-detail-delete:hover { color: var(--danger); border-color: var(--danger); }

.resource-detail-body {
  padding: 22px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.resource-preview {
  background: #f8f5ec;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 120px;
}
.resource-preview-image {
  max-width: 100%;
  max-height: 520px;
  object-fit: contain;
  display: block;
}
.resource-preview-pdf {
  width: 100%;
  height: 600px;
  border: none;
  background: #fff;
}
.resource-preview-audio,
.resource-preview-video {
  width: 100%;
  max-height: 480px;
}
.resource-preview-text {
  width: 100%;
  max-height: 480px;
  overflow: auto;
  background: #fff;
  border: 1px solid var(--line);
  padding: 12px 14px;
  font-family: var(--font-mono, ui-monospace, Menlo, Consolas, monospace);
  font-size: 12.5px;
  line-height: 1.55;
  white-space: pre-wrap;
  word-wrap: break-word;
  color: var(--ink-1);
  margin: 0;
  border-radius: var(--radius-sm);
}
.resource-preview-generic {
  text-align: center;
  padding: 24px 10px;
}
.resource-icon { font-size: 48px; margin-bottom: 8px; }
.resource-preview-msg { color: var(--ink-3); margin: 0; font-size: 13px; }

.resource-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  font-size: 12px;
  color: var(--ink-3);
}
.resource-meta .dot { opacity: .5; }

.resource-file-actions {
  display: flex;
  gap: 8px;
}

.resource-link-block,
.resource-note-block {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.resource-detail-foot {
  padding: 12px 22px;
  border-top: 1px solid var(--line);
  background: var(--bg-paper);
  display: flex;
  justify-content: flex-end;
}

/* ----- Drag-and-drop overlay ----- */
.resource-drop-overlay {
  position: absolute;
  inset: 0;
  background: rgba(139, 105, 20, 0.08);
  border: 3px dashed var(--accent);
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  border-radius: 0;
}
.resource-drop-overlay[hidden] { display: none; }
.resource-drop-card {
  background: #fff;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: 28px 40px;
  text-align: center;
  box-shadow: var(--shadow-2);
}
.resource-drop-icon {
  font-size: 36px;
  color: var(--accent);
  margin-bottom: 8px;
}
.resource-drop-msg {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 600;
  color: var(--ink-1);
}

/* ----- Link composer popover ----- */
.res-link-composer {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 280;
  width: 420px;
  background: #fff;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  animation: preset-pop-in .12s ease-out;
}
.res-link-composer[hidden] { display: none; }
.res-link-composer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid var(--line);
}
.res-link-composer-head h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
}
.res-link-composer-body {
  padding: 18px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.res-link-composer-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 6px;
}

/* ============================================================
   Corkboard view
   ============================================================ */
.corkboard-view {
  display: flex;
  flex-direction: column;
  background: var(--bg-page);
  overflow: hidden;
  position: relative; /* hosts the optional .editor-backdrop layer */
}
.corkboard-view[hidden] { display: none !important; }
/* With the backdrop artwork on, everything lifts above the z0 layer and
   the bare text sitting directly on the board goes light. Cards stay
   opaque — they're the content islands, same as the page sheet. */
.corkboard-view.has-backdrop .corkboard-toolbar,
.corkboard-view.has-backdrop .corkboard-bulk-bar,
.corkboard-view.has-backdrop .corkboard-stage {
  position: relative;
  z-index: 1;
}
.corkboard-view.has-backdrop .corkboard-section-head h3 {
  color: #f3eee1;
  text-shadow: 0 1px 8px rgba(0,0,0,.5);
}
.corkboard-view.has-backdrop .corkboard-section-count { color: #b3a993; }
.corkboard-view.has-backdrop .corkboard-section-empty { color: #d8d0bf; }
.corkboard-view.has-backdrop .corkboard-empty { color: #d8d0bf; }

/* ============================================================
   Corkboard skins (v2.24) — Default / Cork / Linen / Dark.
   Pure-CSS textures on the stage (no image assets); the Default
   skin adds NO rules so today's look is byte-identical. This block
   must stay BELOW the .has-backdrop rules above: the per-skin
   section-head colors tie them on specificity and win by source
   order (a skin may be active while has-backdrop is still on the
   view — the skin's texture is what's actually visible).
   ============================================================ */
/* A real skin replaces the backdrop artwork on THIS view only —
   the backdrop preference is untouched everywhere else. */
.corkboard-view.skin-cork  > .editor-backdrop,
.corkboard-view.skin-linen > .editor-backdrop,
.corkboard-view.skin-dark  > .editor-backdrop,
.corkboard-view.skin-image > .editor-backdrop { display: none; }

/* --- Cork: warm tan, layered speckle + soft vignette --- */
.corkboard-view.skin-cork .corkboard-stage {
  background-color: #c09566;
  background-image:
    radial-gradient(rgba(92,61,30,.18) 1px, transparent 1.5px),
    radial-gradient(rgba(255,231,190,.14) 1px, transparent 1.5px),
    radial-gradient(rgba(60,38,16,.12) 1.5px, transparent 2px),
    radial-gradient(ellipse at center, transparent 55%, rgba(66,42,18,.28) 100%);
  background-size: 11px 11px, 17px 17px, 29px 29px, 100% 100%;
  background-position: 0 0, 5px 9px, 13px 4px, 0 0;
}
/* Mid-light tan → dark-pinned text (never --ink-*: theme-independent). */
.corkboard-view.skin-cork .corkboard-section-head h3 {
  color: #2e2519;
  text-shadow: none;
}
.corkboard-view.skin-cork .corkboard-section-count { color: #5c4f3c; }
.corkboard-view.skin-cork .corkboard-section-empty,
.corkboard-view.skin-cork .corkboard-empty { color: #453a29; }
/* Pushpin: red sphere overhanging the card's top edge (grid gap is
   14px — clears the row above). z3 sits over the z2 select checkbox. */
.corkboard-view.skin-cork .corkboard-card::before {
  content: '';
  position: absolute;
  top: -7px;
  left: 50%;
  margin-left: -6px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #ff8f8a, #c92f28 60%, #7e150f);
  box-shadow: 0 2px 3px rgba(0,0,0,.35);
  z-index: 3;
}
/* No tilt on the cards: even a 0.4deg rotate makes the browser resample
   the text and most of the board goes soft (reported 2026-08-27). The
   pins alone sell the effect; hover keeps the base card's lift. */

/* --- Linen: light woven crosshatch --- */
.corkboard-view.skin-linen .corkboard-stage {
  background-color: #ece7da;
  background-image:
    repeating-linear-gradient(0deg,  rgba(120,110,90,.07) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(90deg, rgba(120,110,90,.07) 0 1px, transparent 1px 3px);
}
.corkboard-view.skin-linen .corkboard-section-head h3 {
  color: #3f392c;
  text-shadow: none;
}
.corkboard-view.skin-linen .corkboard-section-count { color: #6f6753; }
.corkboard-view.skin-linen .corkboard-section-empty,
.corkboard-view.skin-linen .corkboard-empty { color: #5a5240; }

/* --- Dark: deep charcoal board, faint grain + vignette --- */
.corkboard-view.skin-dark .corkboard-stage {
  background-color: #23201c;
  background-image:
    radial-gradient(rgba(255,255,255,.028) 1px, transparent 1.4px),
    radial-gradient(ellipse at center, transparent 60%, rgba(0,0,0,.35) 100%);
  background-size: 13px 13px, 100% 100%;
}
.corkboard-view.skin-dark .corkboard-section-head h3 {
  color: #f3eee1;
  text-shadow: 0 1px 8px rgba(0,0,0,.5);
}
.corkboard-view.skin-dark .corkboard-section-count { color: #b3a993; }
.corkboard-view.skin-dark .corkboard-section-empty,
.corkboard-view.skin-dark .corkboard-empty { color: #d8d0bf; }

/* --- Photo texture (154): admin-uploaded image via --cork-texture,
   set inline on the stage by corkboard.js. cover-crop; NEVER
   background-attachment:fixed (the stage is the scroller). Cork
   photos read mid-dark, so text follows the skin-cork trio
   (hard-coded, theme-independent) with a touch of lift. --- */
.corkboard-view.skin-image .corkboard-stage {
  background-color: #a58054;   /* cork-ish flash while the image loads */
  background-image: var(--cork-texture);
  background-size: cover;
  background-position: center;
}
.corkboard-view.skin-image .corkboard-section-head h3 {
  color: #fff;
  text-shadow: 0 1px 6px rgba(0,0,0,.45);
}
.corkboard-view.skin-image .corkboard-section-count { color: #f0e8d8; text-shadow: 0 1px 4px rgba(0,0,0,.4); }
.corkboard-view.skin-image .corkboard-section-empty,
.corkboard-view.skin-image .corkboard-empty { color: #fdf8ec; text-shadow: 0 1px 4px rgba(0,0,0,.4); }
/* Same pushpin + tilt as the CSS cork — a photo board reads as a real
   corkboard, so the cards should hang from pins on it too. */
.corkboard-view.skin-image .corkboard-card::before {
  content: '';
  position: absolute;
  top: -7px;
  left: 50%;
  margin-left: -6px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #ff8f8a, #c92f28 60%, #7e150f);
  box-shadow: 0 2px 3px rgba(0,0,0,.35);
  z-index: 3;
}

.corkboard-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 32px;
  background: #fff;
  border-bottom: 1px solid var(--line);
  gap: 24px;
  flex-wrap: wrap;
}
.corkboard-title {
  font-family: var(--font-display);
  margin: 0 0 2px;
  font-size: 22px;
  font-weight: 600;
  color: var(--ink-1);
}
.corkboard-sub { color: var(--ink-3); font-size: 12.5px; }
.corkboard-toolbar-right {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  /* When the toolbar wraps onto two lines, the filter rows should stack
     left-aligned instead of justifying to the right edge. */
  justify-content: flex-end;
}
.corkboard-filter { display: flex; gap: 6px; flex-wrap: wrap; }

/* Second-row filters (POV / Location) — same accent palette as the
   status chips, but using compact <select>s since the values are
   free text and can have many distinct entries. */
.corkboard-filter-extra {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}
.corkboard-filter-group {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.corkboard-filter-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-3);
}
.corkboard-filter-select {
  padding: 4px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-paper);
  font: inherit;
  font-size: 12.5px;
  color: var(--ink-1);
  max-width: 160px;
}
.corkboard-filter-reset {
  font-size: 12px;
  color: var(--ink-3);
}
.corkboard-filter-reset:hover { color: var(--accent); }

.corkboard-stage {
  flex: 1;
  overflow-y: auto;
  padding: 24px 32px 60px;
}

.corkboard-empty {
  max-width: 480px;
  margin: 80px auto;
  text-align: center;
}
.corkboard-empty .empty-mark { font-size: 36px; margin-bottom: 12px; }
.corkboard-empty .empty-heading {
  font-family: var(--font-display);
  font-size: 24px;
  margin: 0 0 8px;
}
.corkboard-empty .empty-sub { color: var(--ink-3); }

.corkboard-section { margin-bottom: 28px; }
.corkboard-section-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin: 0 0 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--line-soft);
}
.corkboard-section-head h3 {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--ink-2);
}
.corkboard-section-count { font-size: 11.5px; color: var(--ink-3); }
.corkboard-section-empty {
  color: var(--ink-3);
  font-style: italic;
  font-size: 13px;
  padding: 8px 0;
  margin: 0;
  grid-column: 1 / -1;
}

.corkboard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 14px;
}

.corkboard-card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0;
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-paper);
  cursor: grab;
  transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease;
  /* Pinned to a corkboard look — subtle paper texture */
  background-image:
    linear-gradient(180deg, rgba(255,255,255,1), rgba(252,247,232,.4));
  position: relative;
  border-top-width: 3px;
  border-top-color: var(--line);
}
.corkboard-card:hover {
  transform: translateY(-1px);
  box-shadow: 0 6px 18px rgba(0,0,0,.06);
}
.corkboard-card.is-dragging { opacity: .5; }
.corkboard-card.drag-over { outline: 2px dashed var(--accent); outline-offset: 2px; }

/* Bulk-edit selection. The checkbox sits in the top-right corner so it
   doesn't fight with the chapter number on the left of the header.
   Hover surfaces it; once any card is selected, every checkbox stays
   visible site-wide so the user can keep building / pruning the set. */
.corkboard-card-select {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 18px;
  height: 18px;
  border: 1.5px solid var(--ink-4);
  border-radius: 4px;
  background: rgba(255,255,255,.92);
  cursor: pointer;
  padding: 0;
  opacity: .35;
  transition: opacity .12s ease, background .12s ease, border-color .12s ease;
  z-index: 2;
}
.corkboard-card:hover .corkboard-card-select { opacity: 1; }
.corkboard-card-select:hover { border-color: var(--accent); }
.corkboard-card.is-selected .corkboard-card-select {
  opacity: 1;
  background: var(--accent);
  border-color: var(--accent);
}
.corkboard-card.is-selected .corkboard-card-select::after {
  content: '';
  position: absolute;
  inset: 0;
  background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M3.5 8l3 3 6-6.5' fill='none' stroke='%23fff' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/></svg>") center / 12px 12px no-repeat;
}
.corkboard-card.is-selected {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* When any card is selected, hint that all the others are pickable too
   by raising the checkboxes globally. */
.corkboard-stage:has(.corkboard-card.is-selected) .corkboard-card-select { opacity: 1; }

/* ---------- Bulk action bar ---------- */
.corkboard-bulk-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 10px 18px;
  background: var(--bg-card);
  border-bottom: 1px solid var(--line-soft);
  position: sticky;
  top: 0;
  z-index: 5;
  box-shadow: 0 1px 3px rgba(0,0,0,.04);
}
.corkboard-bulk-bar.is-busy { opacity: .7; pointer-events: none; }
.corkboard-bulk-count {
  font-weight: 600;
  font-size: 13px;
  color: var(--ink-1);
  letter-spacing: .02em;
}
.corkboard-bulk-group {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.corkboard-bulk-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-3);
  margin-right: 2px;
}
.corkboard-bulk-divider {
  width: 1px;
  height: 18px;
  background: var(--line);
  margin: 0 4px;
}
.corkboard-bulk-spacer { flex: 1; }
.corkboard-bulk-btn {
  background: var(--bg-paper);
  color: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 4px 11px;
  font: inherit;
  font-size: 12px;
  cursor: pointer;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.corkboard-bulk-btn:hover { background: var(--accent-soft); border-color: var(--line-strong); }
.corkboard-bulk-danger { color: var(--danger); border-color: var(--danger); }
.corkboard-bulk-danger:hover { background: var(--danger); color: #fff; border-color: var(--danger); }
.corkboard-bulk-close {
  width: 24px; height: 24px;
  border: none;
  background: transparent;
  color: var(--ink-3);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  border-radius: 4px;
  padding: 0;
}
.corkboard-bulk-close:hover { color: var(--ink-1); background: var(--bg-rail); }

/* Status-coded top edge */
.corkboard-card[data-status="draft"]    { border-top-color: #6b7480; }
.corkboard-card[data-status="revising"] { border-top-color: #d18a3d; }
.corkboard-card[data-status="revised"]  { border-top-color: #7a52a3; }
.corkboard-card[data-status="final"]    { border-top-color: #4a8b4d; }

.corkboard-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
  /* Right padding reserves room for the corner checkbox so the
     status pill never sits underneath it. The checkbox lives at
     top:8/right:8/width:18, so it occupies roughly the rightmost
     26px of the card. 32px right padding gives the pill clearance. */
  padding: 12px 32px 6px 14px;
}
.corkboard-card-head-left {
  display: flex;
  /* flex-start so the chapter number lines up with the first row
     (title) rather than centering between title + subtitle. */
  align-items: flex-start;
  gap: 8px;
  flex: 1;
  min-width: 0;
}
.corkboard-card-num {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--accent);
  font-size: 13px;
  flex-shrink: 0;
  padding: 4px 4px 0;
}
.corkboard-card-titles {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}
.corkboard-card-title {
  border: none;
  background: transparent;
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  color: var(--ink-1);
  padding: 2px 0;
  border-bottom: 1px solid transparent;
  transition: border-color .12s ease;
  min-width: 0;
  outline: none;
}
.corkboard-card-title:focus { border-bottom-color: var(--accent-soft); }
.corkboard-card-subtitle {
  border: none;
  background: transparent;
  font-family: var(--font-serif, Georgia, serif);
  font-style: italic;
  font-size: 12px;
  color: var(--ink-3);
  padding: 1px 0;
  border-bottom: 1px solid transparent;
  transition: border-color .12s ease, color .12s ease;
  outline: none;
  min-width: 0;
}
.corkboard-card-subtitle::placeholder { color: var(--ink-4); font-style: italic; }
.corkboard-card-subtitle:focus { border-bottom-color: var(--accent-soft); color: var(--ink-2); }

.corkboard-status-pill {
  background: var(--bg-paper);
  color: var(--ink-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  font-family: var(--font-sans);
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .1em;
  padding: 3px 9px;
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
}
.corkboard-status-pill:hover { background: var(--accent-soft); }
.corkboard-status-pill[data-current="draft"]    { background: #eceef1; color: #4b5560; }
.corkboard-status-pill[data-current="revising"] { background: #fbe4cb; color: #8a4d18; }
.corkboard-status-pill[data-current="revised"]  { background: #ece1f2; color: #5b3680; }
.corkboard-status-pill[data-current="final"]    { background: #d8ebd8; color: #2f6633; }

.corkboard-card-body {
  padding: 6px 14px 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.corkboard-synopsis {
  border: none;
  background: transparent;
  font: inherit;
  font-family: var(--font-serif, Georgia, serif);
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-1);
  resize: none;
  padding: 0;
  outline: none;
  min-height: 60px;
}
.corkboard-synopsis::placeholder { color: var(--ink-3); font-style: italic; }
.corkboard-synopsis:focus {
  background: #fffefa;
  outline: 1px solid var(--accent-soft);
  outline-offset: 4px;
  border-radius: 2px;
}

.corkboard-meta-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  min-width: 0;
}
.corkboard-mini-field {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 10.5px;
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: .08em;
  /* Grid items default to min-content; allow shrinking so the input
     doesn't push past the card edge. */
  min-width: 0;
}
.corkboard-mini-field input {
  font: inherit;
  font-size: 12.5px;
  text-transform: none;
  letter-spacing: 0;
  padding: 4px 6px;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  color: var(--ink-1);
  background: #fff;
  outline: none;
  transition: border-color .12s ease;
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}
.corkboard-mini-field input:focus { border-color: var(--accent); }
.corkboard-mini-field input::placeholder { color: var(--ink-3); }

.corkboard-card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 14px;
  border-top: 1px solid var(--line-soft);
  background: rgba(0,0,0,.015);
  font-size: 11.5px;
  color: var(--ink-3);
}
.corkboard-card-stat strong {
  color: var(--ink-1);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.corkboard-card-open {
  background: transparent;
  color: var(--accent);
  border: none;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  padding: 2px 4px;
}
.corkboard-card-open:hover { text-decoration: underline; }

/* ============================================================
   Plot board (116) — lines × chapters grid
   ============================================================ */
.plot-view {
  display: flex;
  flex-direction: column;
  background: var(--bg-page);
  overflow: hidden;
  position: relative; /* hosts the optional .editor-backdrop layer */
}
.plot-view[hidden] { display: none !important; }
/* Backdrop artwork on: lift everything above the z0 layer; the text
   and hairlines that sit BARE on the stage go light (beats and row
   heads are opaque cards and stay as they are). Same pinned-literals
   rule as every dark-band surface — the art is dark in both themes. */
.plot-view.has-backdrop .plot-toolbar,
.plot-view.has-backdrop .plot-stage {
  position: relative;
  z-index: 1;
}
/* The chapter rail is sticky (v2.32), so it needs a real surface for the
   beats sliding under it — but an opaque one would paint a pale bar
   across the art. Same dark scrim chip the corner uses. */
.plot-view.has-backdrop .plot-col-head {
  color: #d8d0bf;
  background: rgba(20,16,12,.72);
  border-radius: 6px;
}
.plot-view.has-backdrop .plot-col-num { color: #b3a993; }
.plot-view.has-backdrop .plot-col-head .plot-col-title { text-shadow: 0 1px 6px rgba(0,0,0,.45); }
/* The sticky corner masks column heads sliding beneath it — it must
   stay opaque-ish, so it becomes a dark scrim chip over the art. */
.plot-view.has-backdrop .plot-corner {
  background: rgba(20,16,12,.72);
  color: #d8d0bf;
  border-radius: 6px;
}
.plot-view.has-backdrop .plot-cell { border-color: rgba(255,255,255,.24); }
.plot-view.has-backdrop .plot-cell-add { color: #d8d0bf; opacity: 0; }
.plot-view.has-backdrop .plot-cell:hover .plot-cell-add,
.plot-view.has-backdrop .plot-cell-add:focus-visible { opacity: 1; }
.plot-view.has-backdrop .plot-cell-add:hover {
  background: rgba(255,255,255,.14);
  color: #f3eee1;
}
.plot-view.has-backdrop .plot-empty { color: #d8d0bf; }

/* ============================================================
   Backdrop — Formatting / Export / Workshop family
   Same recipe as the corkboard/plot layers: absolute art layer
   at z0 inside the view, content lifted to z1 under
   .has-backdrop, bare-on-stage text pinned light. Cards, tables,
   the white hero bands and the sub-tab strip stay opaque —
   they're the content, the art is the room.
   ============================================================ */

/* Formatting + Export scroll THEMSELVES (unlike corkboard/plot, where an
   inner stage scrolls) — so under a backdrop the scroller moves inward to
   the shell, otherwise the absolute layer would ride up and out of view
   with the content. No-art installs keep the original scroller. */
.format-view,
.export-view { position: relative; }
.format-view.has-backdrop,
.export-view.has-backdrop { overflow: hidden; }
.format-view.has-backdrop > .format-shell,
.export-view.has-backdrop > .export-shell {
  position: relative;
  z-index: 1;
  overflow: auto;
}

/* Formatting: everything lives in cards except the customize breadcrumb
   bar, the preview captions, and the members-only proof placeholder.
   The caption needs no backdrop variant any more — it is a pill with its
   own background, which is the point of making it one. */
.format-view.has-backdrop .format-editor-bar { border-bottom-color: rgba(255,255,255,.18); }
.format-view.has-backdrop .format-editor-bar-divider,
.format-view.has-backdrop .format-editor-bar-title { color: #d8d0bf; }
.format-view.has-backdrop .format-editor-bar-title strong { color: #f3eee1; }
.format-view.has-backdrop .format-editor-back {
  border-color: rgba(255,255,255,.35);
  color: #e9e2d2;
  background: rgba(20,16,12,.4);
}
.format-view.has-backdrop .format-editor-back:hover {
  background: rgba(255,255,255,.14);
  color: #fff;
}
.format-view.has-backdrop .print-proof-locked {
  border-color: rgba(255,255,255,.35);
  color: #d8d0bf;
  background: rgba(20,16,12,.45);
}
/* Ghost buttons are transparent by design, which makes them invisible
   the moment they sit directly on the art ("Open full previewer" beside
   Refresh proof, "+ Create new theme" in the customize bar). Give them
   a real glass button face here — same treatment as the codex head's
   controls. Ghosts inside white cards keep their normal look. */
.format-view.has-backdrop .print-proof-actions .btn-ghost,
.format-view.has-backdrop .format-editor-bar .btn-ghost {
  background: rgba(20,16,12,.66);
  border-color: rgba(255,255,255,.30);
  color: #f3eee1;
}
.format-view.has-backdrop .print-proof-actions .btn-ghost:hover,
.format-view.has-backdrop .format-editor-bar .btn-ghost:hover {
  background: rgba(255,255,255,.16);
  border-color: rgba(255,255,255,.45);
  color: #fff;
}

/* Workshop tabs sharing .publishing-view (Dashboard, Releases, Marketing,
   Amazon Ads, KDP, Beta readers): lift every real child, whatever each
   tab's internal chrome is — the layer itself stays at z0. */
.editor-view.publishing-view { position: relative; }
/* Fixed-position direct children — the keyword library drawer, the
   modals, the resources link composer / drop message — must NOT be
   lifted: this selector's specificity (0,3,0) beats their own single-
   class rules, so `position: relative` here silently overrode their
   `position: fixed`. Under a backdrop the KDP drawer then became an
   in-flow flex child of the column, squashed .publishing-main to a
   sliver, rendered on the LEFT and couldn't scroll (v2.99 fix). */
.publishing-view.has-backdrop > :not(.editor-backdrop):not(.modal):not(.kdp-library),
.resources-view.has-backdrop > :not(.editor-backdrop):not(.modal):not(.res-link-composer):not(.resource-drop-msg):not(.resource-drop-overlay),
.sources-view.has-backdrop > :not(.editor-backdrop):not(.modal):not(.res-link-composer):not(.resource-drop-msg):not(.resource-drop-overlay) {
  position: relative;
  z-index: 1;
}
/* Every .publishing-head sits bare at the top of a .has-backdrop view,
   directly on the backdrop art. Pinning the text light with a glow
   shadow (the old approach, still below) reads fine on a dark backdrop
   but ghosts into illegibility on a pale one — the same problem the
   KDP listing body already had and got ONE OPAQUE SHEET for instead of
   per-element light pins (see #view-kdp .kdp-body below). Give the
   head the same treatment: one card, matching the plain white
   .publishing-table-wrap that already sits bare on the art right below
   it, so every element inside (title, sub, ghost buttons, the KDP
   section-title reused by the harvested-ad-targets header) just needs
   its normal, unpinned styling again. Backdrop-gated: no-art installs
   keep the original flat, borderless header.
   Sources and Resources (v3.19) reuse the same .publishing-head markup
   but keep their own .sources-view/.resources-view base class rather
   than joining the .publishing-view family, so they need this rule
   spelled out for them too rather than inheriting it. */
.publishing-view.has-backdrop .publishing-head,
.sources-view.has-backdrop .publishing-head,
.resources-view.has-backdrop .publishing-head {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius, 10px);
  padding: 18px 22px;
}
/* The ARC campaign-detail header is injected INSIDE #arc-detail, which
   is already its own opaque card (v2.9x) — boxing it again would nest
   a card in a card with padding it was never designed for. Cancel the
   box there; #arc-detail's own ink-color overrides (below) are correct
   as-is since that header was never pinned light to begin with. */
#arc-detail .publishing-head {
  background: none;
  border: none;
  border-radius: 0;
  padding: 0;
}
/* KDP tab's empty state sits bare on the art — pin it light. */
.publishing-view.has-backdrop .kdp-empty-title {
  color: #f3eee1;
  text-shadow: 0 1px 10px rgba(0,0,0,.45);
}
.publishing-view.has-backdrop .kdp-empty-hint { color: #d8d0bf; }
/* The listing machinery (bar, pane pills, panes — including the Rocket
   pane, whose section titles/subs/labels sat BARE on the art and were
   unreadable over dark artwork) gets one opaque sheet instead of
   per-element light pins (v2.60). Same card face as .kdp-select-card
   above it. Capped at 1200px — the description pane's wide measure —
   so the 860px panes still center inside it; the cap must NOT be 860
   or it would clip the description editor+preview (see the column-cap
   comment at the #view-kdp rules). Backdrop-gated: no-art installs
   keep the original flat page. */
#view-kdp.has-backdrop .kdp-body {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px 24px 26px;
  max-width: 1200px;
  margin-left: auto;
  margin-right: auto;
}
/* (The old light pins on .kdp-listing-bar are gone deliberately — the
   bar now sits INSIDE the opaque sheet, where light-on-light would be
   exactly the unreadability this sheet exists to fix.) */

/* Resources + Sources (sidebar stays an opaque white column; only the
   main pane's centered empty state sits bare on the art). */
.editor-view.sources-view { position: relative; }
.resources-view.has-backdrop .resources-empty-state .empty-heading,
.sources-view.has-backdrop .sources-empty-state .empty-heading {
  color: #f3eee1;
  text-shadow: 0 1px 12px rgba(0,0,0,.45);
}
.resources-view.has-backdrop .resources-empty-state .empty-sub,
.sources-view.has-backdrop .sources-empty-state .empty-sub { color: #d8d0bf; }

.plot-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 22px;
  border-bottom: 1px solid var(--line-soft, var(--line));
  background: var(--bg-card);
  flex-wrap: wrap;
}
/* The toolbar's right-hand group had no rule at all, so its children were
   INLINE boxes aligned on the text baseline. An inline-flex button takes its
   baseline from its first flex item: for the text-only buttons that's a real
   text baseline, but for the Print button it's the <svg>, a replaced element
   with no baseline — so its baseline became the icon's bottom edge and the
   whole pill floated above its neighbours. Flex + align-items:center drops
   baseline alignment entirely, so nothing here depends on what a child
   happens to start with. Spacing moves to the gap (inline layout was also
   adding a whitespace text node between every button). */
.plot-toolbar-right {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}
.plot-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 18px;
}
.plot-sub { font-size: 12.5px; color: var(--ink-3); }
/* The stage owns BOTH scroll axes — wide grids scroll here, never the page. */
.plot-stage {
  flex: 1;
  overflow: auto;
  padding: 18px 22px 26px;
}
.plot-grid {
  display: grid;
  /* Row-head column + one column per chapter. min-content keeps a
     40-chapter grid from compressing cells below usefulness — the stage
     scrolls instead. Density (v2.32) retunes the two widths; "Fit all"
     drops the minimum to 0 so every chapter lands on screen. */
  --plot-head-w: 220px;
  --plot-col-min: 150px;
  grid-template-columns: var(--plot-head-w) repeat(var(--plot-cols, 1), minmax(var(--plot-col-min), 1fr));
  gap: 6px;
  min-width: min-content;
  align-items: stretch;
}
.plot-grid.is-compact { --plot-head-w: 168px; --plot-col-min: 104px; gap: 4px; }
.plot-grid.is-compact .plot-beat { font-size: 11px; padding: 4px 6px; -webkit-line-clamp: 2; }
.plot-grid.is-compact .plot-cell { padding: 4px; min-height: 38px; }
.plot-grid.is-compact .plot-line-name { font-size: 12px; }
.plot-grid.is-compact .plot-col-head { font-size: 10.5px; }

/* Fit all: the whole architecture at a glance. Beats stop being readable
   cards and become solid colour chips — the title attribute still carries
   the full text on hover, and clicking still opens the beat. */
.plot-grid.is-fit {
  --plot-head-w: 150px;
  --plot-col-min: 0px;
  gap: 3px;
  min-width: 0;
}
.plot-grid.is-fit .plot-cell { padding: 3px; min-height: 30px; gap: 3px; }
.plot-grid.is-fit .plot-beat {
  font-size: 0;
  line-height: 0;
  min-height: 15px;
  padding: 0;
  border: 1px solid rgba(0,0,0,.22);
  background: var(--beat-color, var(--accent));
  border-radius: 3px;
}
.plot-grid.is-fit .plot-beat:hover {
  outline: 2px solid var(--ink-1);
  outline-offset: 1px;
  background: var(--beat-color, var(--accent));
}
.plot-grid.is-fit .plot-beat-refs-n,
.plot-grid.is-fit .plot-col-title,
.plot-grid.is-fit .plot-line-kind { display: none; }
/* The + target is smaller than a fingertip at this width — beats are
   added in Roomy/Compact, this mode is for reading. */
.plot-grid.is-fit .plot-cell-add { display: none; }
.plot-grid.is-fit .plot-col-head { font-size: 9.5px; padding: 2px; align-items: center; }
.plot-grid.is-fit .plot-line-name { font-size: 11.5px; }
.plot-grid.is-fit .plot-row-head { padding: 6px 6px 6px 4px; gap: 6px; }
.plot-grid.is-fit .plot-swatch { width: 14px; height: 14px; }

/* Beat hover card (v2.32) — replaces the native title tooltip.
   Lives on document.body (position: fixed, placed from viewport rects)
   because .plot-stage is the scroll container and would clip it. That
   also puts it OUTSIDE .plot-view, so none of the .has-backdrop rules
   reach it: it has to be a complete, opaque surface on its own, in both
   themes. */
.plot-hovercard {
  position: fixed;
  z-index: 70;
  max-width: 380px;
  padding: 12px 14px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 10px 34px rgba(0,0,0,.28), 0 2px 8px rgba(0,0,0,.16);
  /* Never eat the pointer: the card can overlap the very beat that spawned
     it, and a hoverable card would flicker itself in and out. */
  pointer-events: none;
  opacity: 0;
  transform: translateY(-3px);
  transition: opacity .12s ease, transform .12s ease;
}
.plot-hovercard.is-open { opacity: 1; transform: none; }
.plot-hovercard-head {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 7px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-3);
}
.plot-hovercard-dot {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  flex: 0 0 auto;
  border: 1px solid rgba(0,0,0,.18);
}
.plot-hovercard-line {
  font-weight: 700;
  color: var(--ink-2);
  max-width: 46%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.plot-hovercard-ch {
  margin-left: auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.plot-hovercard-title {
  margin: 0;
  font-size: 15px;
  line-height: 1.35;
  font-weight: 600;
  color: var(--ink-1);
}
.plot-hovercard-body {
  margin: 7px 0 0;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--ink-2);
}
.plot-hovercard-refs {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 10px;
  padding-top: 9px;
  border-top: 1px solid var(--line);
}
.plot-hovercard-ref {
  font-size: 11.5px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--bg-rail);
  border: 1px solid var(--line);
  color: var(--ink-2);
}

/* Density switch — segmented, same shape as the app's other pill groups. */
.plot-density {
  display: inline-flex;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg-card);
}
.plot-density-btn {
  border: none;
  background: none;
  font: inherit;
  font-size: 11.5px;
  color: var(--ink-3);
  padding: 4px 9px;
  cursor: pointer;
  border-right: 1px solid var(--line);
}
.plot-density-btn:last-child { border-right: none; }
.plot-density-btn:hover { background: var(--bg-rail); color: var(--ink-1); }
.plot-density-btn.is-active { background: var(--accent); color: #fff; }
.plot-view.has-backdrop .plot-density {
  background: rgba(20,16,12,.66);
  border-color: rgba(255,255,255,.30);
}
.plot-view.has-backdrop .plot-density-btn { color: #d8d0bf; border-right-color: rgba(255,255,255,.22); }
.plot-view.has-backdrop .plot-density-btn:hover { background: rgba(255,255,255,.14); color: #fff; }
.plot-view.has-backdrop .plot-density-btn.is-active { background: var(--accent); color: #fff; }
.plot-corner {
  position: sticky;
  left: 0;
  top: 0;
  /* Above both rails: it's the one cell they cross. */
  z-index: 4;
  background: var(--bg-page);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--ink-3);
  align-self: end;
  padding: 4px 6px;
}
/* The chapter rail stays put on a tall board — otherwise scrolling to the
   twelfth plot line leaves you counting columns to find the chapter. */
.plot-col-head {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--bg-page);
  font-size: 11.5px;
  color: var(--ink-2);
  padding: 4px 6px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.plot-col-num { font-weight: 700; color: var(--ink-3); font-size: 10px; }
.plot-col-title {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.plot-row-head {
  position: sticky;
  left: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 8px 8px 4px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: 8px;
  min-width: 0;
  /* The whole card toggles thread focus. */
  cursor: pointer;
  box-shadow: var(--shadow-paper);
}
.plot-row-head:hover { border-color: var(--line-strong, var(--line)); }
/* Affirmative focused state — not merely "the row that isn't dimmed". */
.plot-row-head.is-focused {
  border-color: var(--accent);
  background: var(--accent-bg);
  box-shadow: 0 0 0 1px var(--accent), var(--shadow-paper);
}
.plot-swatch {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  border: 2px solid rgba(0,0,0,.15);
  border-radius: 5px;
  cursor: pointer;
  padding: 0;
}
.plot-swatch[aria-pressed="true"] { outline: 2px solid var(--ink-1); outline-offset: 1px; }
.plot-line-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}
.plot-line-name:hover { color: var(--accent); }
.plot-line-kind { font-size: 10px; color: var(--ink-4); text-transform: uppercase; letter-spacing: .05em; cursor: pointer; }
.plot-line-delete {
  margin-left: auto;
  background: none;
  border: none;
  color: var(--ink-4);
  font-size: 15px;
  cursor: pointer;
  padding: 0 4px;
  border-radius: 4px;
}
.plot-line-delete:hover { color: var(--danger, #b3261e); background: var(--bg-rail); }
.plot-cell {
  border: 1px dashed var(--line);
  border-radius: 8px;
  padding: 5px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-height: 46px;
}
.plot-beat {
  text-align: left;
  font-size: 12px;
  line-height: 1.35;
  padding: 5px 8px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--beat-color, var(--accent));
  border-radius: 6px;
  background: var(--bg-card);
  color: var(--ink-1);
  cursor: pointer;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}
.plot-beat:hover { border-color: var(--line-strong, var(--line)); background: var(--bg-paper, #fbfaf7); }
.plot-beat-refs-n {
  display: inline-block;
  margin-left: 6px;
  font-size: 10px;
  font-weight: 700;
  color: var(--ink-3);
  background: var(--bg-rail);
  border-radius: 999px;
  padding: 0 6px;
}
.plot-cell-add {
  border: none;
  background: none;
  color: var(--ink-4);
  font-size: 14px;
  cursor: pointer;
  border-radius: 6px;
  padding: 2px;
  opacity: 0;
  transition: opacity .12s ease;
}
.plot-cell:hover .plot-cell-add,
.plot-cell-add:focus-visible { opacity: 1; }
.plot-cell-add:hover { background: var(--bg-rail); color: var(--ink-2); }
/* Thread focus: everything but the focused line steps back. */
.plot-grid.has-focus .is-dimmed { opacity: .28; }
.plot-empty {
  max-width: 480px;
  margin: 60px auto;
  text-align: center;
  color: var(--ink-2);
  font-size: 14px;
}
.plot-beat-card { max-width: 480px; }
.plot-beat-refs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  max-height: 160px;
  overflow-y: auto;
  padding: 4px 0;
}
.plot-ref-check {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12.5px;
  color: var(--ink-2);
  cursor: pointer;
}
.plot-ref-none { font-size: 12px; color: var(--ink-3); margin: 0; }
.plot-beat-actions { align-items: center; }
.plot-beat-actions-spacer { flex: 1; }
.plot-beat-delete { color: var(--danger, #b3261e); }

/* Focus chip in the plot toolbar — visible only while a thread is focused. */
.plot-focus-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 12px;
  border: 1px solid var(--accent);
  border-radius: 999px;
  background: var(--accent-bg);
  color: var(--ink-1);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
}
.plot-focus-chip:hover { background: var(--bg-card); }
.plot-focus-dot {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  flex: 0 0 auto;
}
.plot-focus-show-all { font-weight: 400; color: var(--ink-2); }

/* Line editor modal: the colour picker. */
.plot-line-card { max-width: 420px; }
.plot-line-swatches { display: flex; gap: 8px; flex-wrap: wrap; padding: 2px 0; }
.plot-line-swatch {
  width: 26px;
  height: 26px;
  border: 2px solid rgba(0,0,0,.15);
  border-radius: 7px;
  cursor: pointer;
  padding: 0;
}
.plot-line-swatch.is-selected {
  outline: 2px solid var(--ink-1);
  outline-offset: 2px;
}

/* Board load failure (v2.31). The old copy blamed migration 116 for every
   failure including expired sessions; the detail line now carries the real
   status so the cause is visible, not guessed. */
.plot-load-error { text-align: center; }
.plot-load-error-detail {
  margin: 6px 0 14px;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: .78rem;
  color: var(--ink-3);
  word-break: break-word;
}
.plot-view.has-backdrop .plot-load-error-detail { color: #b3a993; }

/* ---- Print the board as a report (v2.39) ---- */
/* .btn already supplies inline-flex + align-items + gap + line-height:1, so
   the only thing to control here is the icon. Sizing it in em pins the flex
   line to the text height, so this pill is exactly as tall as Import… and
   + Plot line; .plot-toolbar-right's align-items handles where it sits. */
.plot-print-btn svg {
  flex: 0 0 auto;
  display: block;
  width: 1em;
  height: 1em;
}

/* ---- Admin plot import (v2.31) ---- */
/* Spacing comes from .plot-toolbar-right's gap — a margin here would add to
   it rather than replace it. */
/* A full report (lines + problems + every beat) easily outgrows the
   viewport, and the house .modal centres its card with flex — an over-tall
   card overflows both edges. Scrolling the CARD wasn't enough either: the
   footer scrolled away with everything else, so a long preview left Apply
   below the reachable area. So the card is a flex column — fixed title,
   one scrolling body, fixed footer. */
/* Shared by the Map import modal (#map-import-modal / map_import.js) —
   generic import-modal chrome despite the plot- name. */
.plot-import-card {
  max-width: 720px;
  max-height: min(86vh, 900px);
  display: flex;
  flex-direction: column;
  overflow: hidden;          /* the body scrolls, never the card */
}
.plot-import-card .modal-title { flex: 0 0 auto; }
.plot-import-scroll {
  flex: 1 1 auto;
  /* Without min-height:0 a flex child refuses to shrink below its content,
     so it would never scroll — it would just push the footer off instead. */
  min-height: 0;
  overflow-y: auto;
  /* Bleed to the card's edges so the scrollbar rides the side, then put
     the padding back inside. */
  margin: 0 -32px;
  padding: 0 32px;
}
.plot-import-actions {
  flex: 0 0 auto;
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}
/* Collapse toggle in the step-3 heading. */
.plot-import-step h3 { display: flex; align-items: center; gap: 8px; }
.plot-import-collapse {
  margin-left: auto;
  background: none;
  border: none;
  font: inherit;
  font-size: 11px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--accent);
  cursor: pointer;
  padding: 2px 4px;
}
.plot-import-collapse:hover { text-decoration: underline; }
.plot-import-step {
  padding: 14px 0;
  border-top: 1px solid var(--line);
}
.plot-import-step:first-of-type { border-top: none; padding-top: 4px; }
.plot-import-step h3 {
  margin: 0 0 6px;
  font-size: .82rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.plot-import-note { margin: 0 0 8px; font-size: .85rem; color: var(--ink-2); }
.plot-import-meta { margin: 0 0 10px; font-size: .8rem; color: var(--ink-3); }
.plot-import-details { margin-top: 10px; }
.plot-import-details summary { font-size: .8rem; color: var(--ink-3); cursor: pointer; }
.plot-import-prompt,
.plot-import-json {
  width: 100%;
  margin-top: 8px;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: .78rem;
  line-height: 1.45;
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-card);
  color: var(--ink-1);
  resize: vertical;
}
.plot-import-prompt { max-height: 260px; }
.plot-import-json { margin-bottom: 8px; }
/* Import status — the one place the importer reports what it's doing.
   Always present (never toggled hidden) so there is a fixed spot the eye
   learns to check; only its state changes. The old design failed into a
   small red line above the button, which made "your JSON is broken" and
   "the button is dead" indistinguishable. */
.plot-import-status {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0 0 10px;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  background: var(--bg-rail);
  font-size: .84rem;
  line-height: 1.45;
}
.plot-import-status-text { min-width: 0; flex: 1; }
.plot-import-status-msg { margin: 0; color: var(--ink-2); }
.plot-import-status-icon {
  flex: 0 0 auto;
  font-weight: 700;
  line-height: 1.45;
}
.plot-import-status[data-state="idle"]  { border-left-color: var(--line); }
.plot-import-status[data-state="idle"] .plot-import-status-icon::before { content: '○'; color: var(--ink-4); }
.plot-import-status[data-state="busy"]  { border-left-color: var(--accent); }
.plot-import-status[data-state="busy"] .plot-import-status-icon::before { content: '⋯'; color: var(--accent); }
.plot-import-status[data-state="ok"]    { border-left-color: var(--success); }
.plot-import-status[data-state="ok"] .plot-import-status-icon::before { content: '✓'; color: var(--success); }
.plot-import-status[data-state="ok"] .plot-import-status-msg { color: var(--ink-1); }
.plot-import-status[data-state="error"] { border-left-color: var(--danger); }
.plot-import-status[data-state="error"] .plot-import-status-icon::before { content: '✕'; color: var(--danger); }
.plot-import-status[data-state="error"] .plot-import-status-msg { color: var(--danger); font-weight: 600; }
/* The offending slice of JSON, with a HERE marker. Wraps rather than
   scrolls sideways — a horizontal scrollbar hides the very thing it's
   meant to show. */
.plot-import-status-detail {
  margin: 7px 0 0;
  padding: 7px 8px;
  max-height: 120px;
  overflow-y: auto;
  border-radius: 4px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: .74rem;
  line-height: 1.5;
  color: var(--ink-2);
  white-space: pre-wrap;
  word-break: break-word;
}
.plot-import-apply-hint {
  font-size: .78rem;
  color: var(--ink-3);
  font-style: italic;
}
.plot-import-report { margin-bottom: 12px; }
.plot-import-summary { margin: 0 0 10px; font-size: .88rem; color: var(--ink-1); }
.plot-import-report-line {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 0;
  border-bottom: 1px solid var(--line-soft, var(--line));
  font-size: .85rem;
}
.plot-import-report-swatch {
  width: 12px; height: 12px;
  border-radius: 3px;
  flex: 0 0 auto;
}
.plot-import-report-name { font-weight: 600; color: var(--ink-1); }
.plot-import-report-kind { font-size: .75rem; color: var(--ink-3); }
.plot-import-report-count { margin-left: auto; font-size: .75rem; color: var(--ink-3); }
.plot-import-problems-wrap,
.plot-import-beats-wrap { margin-top: 10px; }
.plot-import-problems-wrap summary,
.plot-import-beats-wrap summary {
  font-size: .8rem;
  color: var(--ink-3);
  cursor: pointer;
}
.plot-import-problems,
.plot-import-report-beats {
  max-height: 200px;
  overflow-y: auto;
  margin-top: 6px;
  padding-right: 4px;
}
.plot-import-problem {
  padding: 4px 0;
  font-size: .8rem;
  color: var(--ink-2);
}
.plot-import-problem code {
  font-size: .74rem;
  color: var(--ink-3);
  margin-right: 4px;
}
.plot-import-problem.is-error { color: var(--danger); }
.plot-import-report-beat {
  display: flex;
  gap: 8px;
  padding: 4px 0;
  font-size: .8rem;
  border-bottom: 1px solid var(--line-soft, var(--line));
}
.plot-import-report-ch {
  flex: 0 0 34%;
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.plot-import-report-beat-title { flex: 1 1 auto; color: var(--ink-1); }
.plot-import-report-refs { flex: 0 0 auto; font-size: .74rem; color: var(--ink-3); }
/* Map import: rows the server will skip because the pair already exists. */
.plot-import-exists {
  flex: 0 0 auto;
  align-self: center;
  font-size: .68rem;
  color: var(--ink-3);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 1px 8px;
  white-space: nowrap;
}
.plot-import-modes {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  margin: 0;
}
.plot-import-modes label {
  display: block;
  padding: 3px 0;
  font-size: .85rem;
  cursor: pointer;
}
.plot-import-replace-warn {
  margin: 8px 0 0;
  font-size: .8rem;
  color: var(--danger);
}

/* ============================================================
   Relationship map (118): codex entries as a node graph.
   The stage never scrolls — pan/zoom live inside the SVG transform.
   Everything strokes/fills through tokens, so dark mode is free.
   ============================================================ */
.map-view {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.map-view[hidden] { display: none !important; }
.map-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 14px 22px;
  border-bottom: 1px solid var(--line);
}
.map-toolbar-left { min-width: 0; }
.map-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 600;
}
.map-sub { font-size: 12.5px; color: var(--ink-3); }
.map-toolbar-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.map-kind-filters { display: flex; gap: 6px; flex-wrap: wrap; }
.map-kind-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg-card);
  color: var(--ink-3);
  font: inherit;
  font-size: 11.5px;
  cursor: pointer;
}
.map-kind-chip.is-on {
  border-color: var(--accent-soft);
  background: var(--accent-bg);
  color: var(--ink-1);
}
/* Kind chips step back while the Unconnected focus view is active —
   it ignores them by design (it always shows every waiting entry). */
.map-kind-chip:disabled {
  opacity: .4;
  cursor: default;
}
.map-stage {
  flex: 1;
  position: relative;
  overflow: hidden;
  background: var(--bg-page);
}
.map-stage.is-panning { cursor: grabbing; }
.map-svg { display: block; touch-action: none; }
.map-svg text { user-select: none; -webkit-user-select: none; }

.map-edge line {
  stroke: var(--line-strong, var(--line));
  stroke-width: 1.5;
}
/* Chart-mode connectors (elbows/arcs). fill:none is mandatory — a
   filled elbow path renders as a black polygon. */
.map-edge path {
  fill: none;
  stroke: var(--line-strong, var(--line));
  stroke-width: 1.5;
}
/* Chart rows are algorithmic: nodes open the codex on click but can't
   be dragged, so don't advertise grab. */
.map-stage.is-chart .map-node { cursor: pointer; }

/* Web / Chart layout toggle — a joined pill pair beside the kind chips. */
.map-mode-toggle {
  display: inline-flex;
  border: 1px solid var(--line);
  border-radius: 999px;
  overflow: hidden;
}
.map-mode-btn {
  font: inherit;
  font-size: 11.5px;
  padding: 4px 12px;
  border: none;
  background: var(--bg-card);
  color: var(--ink-3);
  cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.map-mode-btn + .map-mode-btn { border-left: 1px solid var(--line); }
.map-mode-btn:hover { background: var(--bg-rail); color: var(--ink-1); }
.map-mode-btn.is-on {
  background: var(--accent-bg);
  color: var(--ink-1);
  font-weight: 600;
}
.map-mode-btn:disabled { opacity: .4; cursor: default; }
/* Relationship bubble at the line midpoint — hover/focus for the label
   + note (the .map-hovercard below), click to edit. Replaced the full-text
   pills that turned dense graphs into overlapping banners. */
.map-edge-label { cursor: pointer; }
.map-edge-dot {
  fill: var(--bg-card);
  stroke: var(--line-strong);
  stroke-width: 1.5;
}
.map-edge-dot-pip { fill: var(--ink-4); }
.map-edge-label:hover .map-edge-dot,
.map-edge-label:focus-visible .map-edge-dot {
  stroke: var(--accent);
  stroke-width: 2;
}
.map-edge-label:hover .map-edge-dot-pip,
.map-edge-label:focus-visible .map-edge-dot-pip { fill: var(--accent); }

/* Hover card for both map targets — a node's portrait and a relationship
   bubble. Deliberately the plot board's .plot-hovercard in a different
   skin: the two boards sit one tab apart, and a beat and a codex entry
   should not explain themselves in two different visual languages.

   Lives on document.body (position:fixed, placed from viewport rects)
   because .map-stage is overflow:hidden AND a pan/zoom surface — an
   in-stage card would be clipped at the edges and would slide off its
   anchor on the next pan. That also puts it OUTSIDE .map-view, so none
   of the .has-backdrop rules reach it: it has to be a complete, opaque
   surface on its own, in both themes. */
.map-hovercard {
  position: fixed;
  z-index: 70;
  max-width: 380px;
  padding: 12px 14px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 10px 34px rgba(0,0,0,.28), 0 2px 8px rgba(0,0,0,.16);
  /* Never eat the pointer: the card can overlap the very portrait that
     spawned it, and a hoverable card would flicker itself in and out. */
  pointer-events: none;
  opacity: 0;
  transform: translateY(-3px);
  transition: opacity .12s ease, transform .12s ease;
}
.map-hovercard.is-open { opacity: 1; transform: none; }
.map-hovercard-head {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 7px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-3);
}
.map-hovercard-dot {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  flex: 0 0 auto;
  border: 1px solid rgba(0,0,0,.18);
}
.map-hovercard-kind {
  font-weight: 700;
  color: var(--ink-2);
  max-width: 46%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.map-hovercard-meta {
  margin-left: auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.map-hovercard-title {
  margin: 0;
  font-size: 15px;
  line-height: 1.35;
  font-weight: 600;
  color: var(--ink-1);
}
.map-hovercard-body {
  margin: 7px 0 0;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--ink-2);
}
.map-hovercard-refs {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 10px;
  padding-top: 9px;
  border-top: 1px solid var(--line);
}
.map-hovercard-ref {
  font-size: 11.5px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--bg-rail);
  border: 1px solid var(--line);
  color: var(--ink-2);
}

.map-node { cursor: grab; }
.map-node:active { cursor: grabbing; }
.map-node-ring {
  fill: var(--bg-card);
  stroke-width: 2.5;
}
/* Deceased X over the portrait circle (134) — pinned red, matching the
   drawer/sidebar/popover marks. */
.map-node-deceased {
  stroke: #d05548;
  stroke-width: 4;
  stroke-linecap: round;
}
.map-node-initials {
  font-family: var(--font-display);
  font-size: 16px;
  fill: var(--ink-2);
}
/* Name pill tucked against the avatar's bottom edge (rect sized to the
   text by render()'s measurement pass). Token-driven → dark-mode free.
   Type is deliberately small — pills must stay narrower than a grid
   column or the parked rows read as one run-on banner. */
.map-node-name-bg {
  fill: var(--bg-card);
  stroke: var(--line);
}
.map-node-name {
  font-family: var(--font-sans);
  font-size: 10.5px;
  letter-spacing: .01em;
  fill: var(--ink-2);
}
.map-parked-caption {
  font-family: var(--font-sans);
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  fill: var(--ink-4);
}

/* Hover: the node's neighbourhood lights up, the rest steps back
   (same vocabulary as the plot board's thread focus). */
.map-svg.has-focus .map-node:not(.is-lit),
.map-svg.has-focus .map-edge:not(.is-lit) { opacity: .18; }

.map-empty {
  max-width: 480px;
  margin: 60px auto;
  text-align: center;
  color: var(--ink-2);
  font-size: 14px;
  padding: 0 20px;
}
.map-callout {
  position: absolute;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1;
  max-width: 520px;
  padding: 8px 16px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg-card);
  box-shadow: var(--shadow-1);
  font-size: 12.5px;
  color: var(--ink-2);
  text-align: center;
}

/* ============================================================
   Snapshot diff page
   ============================================================ */
.diff-body {
  background: var(--bg-page);
  min-height: 100vh;
}
.diff-shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
.diff-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 28px;
  background: #1d1b18;
  color: #fff;
  gap: 16px;
}
.diff-topbar .topbar-logo {
  background: var(--accent);
  width: 32px;
  height: 32px;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  color: #fff;
  font-weight: 700;
}
.diff-topbar-left { display: flex; align-items: center; gap: 14px; }
.diff-back {
  color: #d4cfc1;
  text-decoration: none;
  font-size: 13px;
  transition: color .12s ease;
}
.diff-back:hover { color: #fff; }
.diff-page-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: .02em;
}
.diff-topbar-right { min-width: 60px; }

.diff-controls {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 18px 32px;
  background: #fff;
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.diff-control {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-sans);
  font-size: 11px;
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: .12em;
}
.diff-control select {
  font: inherit;
  font-size: 13px;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink-1);
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: #fff;
  min-width: 220px;
}
.diff-swap-btn {
  background: var(--bg-paper);
  border: 1px solid var(--line);
  border-radius: 50%;
  width: 32px;
  height: 32px;
  font-size: 14px;
  color: var(--ink-2);
  cursor: pointer;
}
.diff-swap-btn:hover { background: var(--accent-soft); color: var(--ink-1); }
.diff-summary {
  margin-left: auto;
  font-size: 13px;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}

.diff-body-main {
  padding: 24px 32px 60px;
  max-width: 1080px;
  margin: 0 auto;
  width: 100%;
  box-sizing: border-box;
}
.diff-loading {
  text-align: center;
  padding: 80px 0;
  color: var(--ink-3);
  font-style: italic;
}
.diff-empty {
  text-align: center;
  padding: 60px 0;
  color: var(--ink-3);
}
.diff-empty-chapter {
  padding: 14px 18px;
  color: var(--ink-3);
  font-style: italic;
  font-size: 13px;
}

.diff-chapter {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  margin-bottom: 16px;
  box-shadow: var(--shadow-paper);
  overflow: hidden;
}
.diff-chapter-summary {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 20px;
  cursor: pointer;
  background: var(--bg-paper);
  font-family: var(--font-sans);
  list-style: none;
  user-select: none;
}
.diff-chapter-summary::-webkit-details-marker { display: none; }
.diff-chapter-summary::before {
  content: '▸';
  color: var(--ink-3);
  font-size: 12px;
  transition: transform .12s ease;
}
.diff-chapter[open] > .diff-chapter-summary::before { transform: rotate(90deg); }
.diff-chapter-badge {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ink-3);
  flex-shrink: 0;
}
.diff-chapter.is-added    .diff-chapter-badge { background: #2f7c34; }
.diff-chapter.is-removed  .diff-chapter-badge { background: #b53b3b; }
.diff-chapter.is-changed  .diff-chapter-badge { background: var(--accent); }
.diff-chapter.is-unchanged .diff-chapter-badge { background: #cfcabd; }
.diff-chapter-title {
  flex: 1;
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  color: var(--ink-1);
}
.diff-title-renamed {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 500;
  color: var(--ink-3);
  font-style: italic;
  margin-left: 8px;
}
.diff-chapter-sub {
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.diff-chapter-body {
  padding: 16px 20px;
  font-family: var(--font-serif, Georgia, serif);
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--ink-1);
}

.diff-row {
  position: relative;
  padding: 6px 12px 6px 32px;
  margin: 2px 0;
  border-radius: 3px;
  text-indent: 0;
}
.diff-row-text { white-space: pre-wrap; word-wrap: break-word; }
.diff-row-marker {
  position: absolute;
  left: 8px;
  top: 6px;
  font-family: var(--font-mono, ui-monospace, Menlo, Consolas, monospace);
  font-size: 14px;
  color: var(--ink-3);
  font-weight: 600;
}
.diff-row-eq {
  color: var(--ink-2);
}
.diff-row-add {
  background: rgba(46, 124, 52, 0.08);
  border-left: 3px solid #2f7c34;
}
.diff-row-add .diff-row-marker { color: #2f7c34; }
.diff-row-del {
  background: rgba(181, 59, 59, 0.08);
  border-left: 3px solid #b53b3b;
}
.diff-row-del .diff-row-marker { color: #b53b3b; }
.diff-row-del .diff-row-text {
  text-decoration: line-through;
  text-decoration-color: rgba(181, 59, 59, .5);
  color: #6f4444;
}
.diff-row-mod {
  border-left: 3px solid var(--accent);
  background: rgba(139, 105, 20, 0.05);
  padding: 4px 0;
}
.diff-row-mod-old,
.diff-row-mod-new {
  position: relative;
  padding: 4px 12px 4px 32px;
}
.diff-row-mod-old { color: #6f4444; }
.diff-row-mod-old .diff-row-marker { color: #b53b3b; }
.diff-row-mod-new .diff-row-marker { color: #2f7c34; }
.diff-row-mod-old del {
  background: rgba(181, 59, 59, 0.18);
  text-decoration: line-through;
  text-decoration-color: rgba(181, 59, 59, .6);
  border-radius: 2px;
  padding: 0 1px;
}
.diff-row-mod-new ins {
  background: rgba(46, 124, 52, 0.18);
  text-decoration: none;
  border-radius: 2px;
  padding: 0 1px;
}

.diff-collapsed-toggle {
  display: block;
  width: 100%;
  text-align: center;
  background: var(--bg-paper);
  border: 1px dashed var(--line);
  border-radius: var(--radius-sm);
  padding: 8px;
  margin: 8px 0;
  color: var(--ink-3);
  font-family: var(--font-sans);
  font-size: 12px;
  cursor: pointer;
}
.diff-collapsed-toggle:hover { background: var(--accent-soft); color: var(--ink-2); }
.diff-collapsed-body[hidden] { display: none; }

.preview-toolbar {
  background: #fff;
  border-bottom: 1px solid var(--line);
  padding: 10px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  font-size: 12.5px;
  color: var(--ink-3);
}
.preview-meta strong { color: var(--ink-1); font-weight: 600; }

/* Toolbar pager — N / total + Prev / Next buttons */
.preview-pager {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12.5px;
  color: var(--ink-3);
}
.preview-pager-btn {
  background: #fff;
  border: 1px solid var(--line);
  color: var(--ink-2);
  padding: 5px 12px;
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 12.5px;
  cursor: pointer;
  font-variant-numeric: tabular-nums;
}
.preview-pager-btn:hover:not(:disabled) {
  background: var(--bg-rail);
  border-color: var(--line-strong);
  color: var(--ink-1);
}
.preview-pager-btn:disabled {
  opacity: .4;
  cursor: not-allowed;
}
/* "Full book preview" is an <a> reusing the pager-button look — kill the
   link underline and let the lock badge sit inline. Solid ink so the
   print-proof entry point reads as the toolbar's one primary action.
   Compound selector on purpose: the generic `:hover:not(:disabled)` rule
   above is (0,3,0), so a bare `.preview-fullbook:hover` would lose to it
   and flash back to the rail background. */
.preview-pager-btn.preview-fullbook {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 14px;
  text-decoration: none;
  background: var(--ink-1);
  border-color: var(--ink-1);
  color: #fff;
}
.preview-pager-btn.preview-fullbook:hover {
  background: var(--ink-2);
  border-color: var(--ink-2);
  color: #fff;
}
.preview-pager-label strong { color: var(--ink-1); font-weight: 600; }
.preview-pager-title {
  color: var(--ink-3);
  font-style: italic;
  max-width: 220px;
  display: inline-block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}

.preview-stage {
  flex: 1;
  overflow-y: auto;
  padding: 32px 24px 80px;
  display: flex;
  justify-content: center;
  position: relative; /* anchor for the side arrows */

  /* Theme variables, set by preview.js */
  --pv-body-font: 'Lora', serif;
  --pv-heading-font: 'Playfair Display', serif;
  --pv-font-size: 12pt;
  --pv-line-height: 1.45;
  --pv-indent: 0.3in;
  --pv-text-color: #1a1a1a;
  --pv-accent-color: #8b6914;
  --pv-bg-color: #fdfbf6;
  /* Text-feature colour aliases. These carry the REAL book theme —
     preview.js sets the --pv-* trio from the theme row — which is what
     lets a message bubble be tinted with the book's own accent. */
  --tf-accent: var(--pv-accent-color, #8b6914);
  --tf-text:   var(--pv-text-color, #1a1a1a);
  --tf-bg:     var(--pv-bg-color, #fdfbf6);
}
.preview-pages {
  width: 100%;
  max-width: 720px;
  display: block;
}

/* Show only the active chapter — paginated, not infinite-scrolling */
.preview-page { display: none; }
.preview-page.is-active { display: flex; flex-direction: column; }

/* Big side arrows for click-to-flip page turns */
.preview-side-arrow {
  position: sticky;
  top: 50%;
  z-index: 5;
  align-self: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: rgba(255,255,255,.92);
  color: var(--ink-2);
  font-size: 22px;
  line-height: 1;
  font-family: inherit;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,.08);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform .12s ease, background .12s ease;
}
.preview-side-arrow:hover:not(:disabled) {
  background: #fff;
  transform: scale(1.06);
  color: var(--ink-1);
}
.preview-side-arrow:disabled {
  opacity: .25;
  cursor: not-allowed;
}
.preview-side-prev { margin-right: 8px; }
.preview-side-next { margin-left: 8px; }
.preview-loading {
  text-align: center;
  padding: 60px 0;
  color: var(--ink-3);
  font-style: italic;
}

.preview-page {
  background: var(--pv-bg-color);
  color: var(--pv-text-color);
  font-family: var(--pv-body-font);
  font-size: var(--pv-font-size);
  line-height: var(--pv-line-height);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-paper);
  /* The REAL Print-layout margins, scaled to the stage by preview.js
     (3.10) — this was a hardcoded 0.875in/1in before, so the margin
     settings changed exports but never this page. Fallbacks mirror the
     themes schema. */
  padding: var(--pv-margin-top, 0.75in) var(--pv-margin-side, 0.75in) var(--pv-margin-bottom, 0.75in);
  border-radius: 1px;
  min-height: var(--pv-page-min-height, 1000px);
  /* Anchor for the opening-spread art (absolute children below). */
  position: relative;
}

/* ---- Opening-spread art & plates in the in-editor preview ----
   Mirrors the print PDF: the chapter's art page (left half of the spread
   image) renders as its own page before the chapter; the right half sits
   faded behind the chapter's first page-height of text. Opacity is inlined
   per chapter from the fade sliders. */
.preview-page .preview-opening-bg {
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  height: var(--pv-page-min-height, 1000px);
  max-height: 100%;
  object-fit: cover;
  object-position: 100% 50%;
  pointer-events: none;
}
.preview-page.preview-art-page,
.preview-page.preview-plate {
  padding: 0;
  overflow: hidden;
}
.preview-page.preview-art-page img,
.preview-page.preview-plate img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.preview-page.preview-art-page img { object-position: 0% 50%; }
/* Active page uses column flex (set on .is-active above) so the body/auto-* fills the page. */
.preview-page.is-active > * { flex: 0 0 auto; }
.preview-page.is-active > .chapter-body,
.preview-page.is-active > .auto-toc,
.preview-page.is-active > .auto-titlepage { flex: 1 1 auto; }

/* Chapter body paragraph styling */
.preview-page .chapter-body p {
  /* Exactly the theme's Paragraph spacing, like print.php — the preview
     used to add its own .35em on top, spacing every paragraph wider here
     than in any export (dropped 3.10). */
  margin: 0 0 var(--pv-para-space, 0em);
  text-indent: var(--pv-indent);
  text-align: var(--pv-text-align, justify);
  hyphens: var(--pv-hyphens, auto);
  -webkit-hyphens: var(--pv-hyphens, auto);
}
.preview-page:not(.is-continued) .chapter-body p:first-child { text-indent: 0; }
/* Per-paragraph alignment from the editor (Quill writes these classes; its
   own CSS is scoped to .ql-editor, so the preview needs its own copy). */
.preview-page .chapter-body .ql-align-center  { text-align: center !important; text-indent: 0 !important; }
.preview-page .chapter-body .ql-align-right   { text-align: right !important; text-indent: 0 !important; }
.preview-page .chapter-body .ql-align-justify { text-align: justify !important; }
/* Copyright pages set flush left (trade convention) — mirrors the PDF/EPUB.
   Attribute selector outranks the base alignment rule; ql-align-* still wins. */
.preview-page[data-subtype="copyright"] .chapter-body p { text-align: left; }
/* The generated copyright page (3.10): flush left, no indent, and pushed
   toward the foot of the page the way the exports set it. */
.preview-page .auto-copyrights p { text-align: left; text-indent: 0; }
.preview-page.preview-copyright.is-active > .chapter-body { display: flex; flex-direction: column; justify-content: flex-end; }
/* The preview's honest-scope line under the stage (3.10). */
.preview-scope-note {
  margin: 10px auto 0;
  max-width: 720px;
  font-size: 12px;
  color: var(--ink-3);
  text-align: center;
}
.preview-scope-note a { color: var(--accent); }
/* Preview-only explanations ("notes collect at the end of the book on
   export") — muted, clearly not book content. */
.preview-page .pv-note {
  margin: 1.2em 0 0;
  font-family: var(--font-ui, sans-serif);
  font-size: 11px;
  color: var(--ink-3);
  text-align: left !important;
  text-indent: 0 !important;
  border-top: 1px dashed var(--line);
  padding-top: 6px;
}
.preview-page.is-indent-first .chapter-body p:first-child { text-indent: var(--pv-indent); }
/* Theme-level default: indent the first paragraph across every chapter
   (set via the stage's data-indent-first). The per-chapter override above
   still wins on its own; both resolve to the same indent. */
.preview-stage[data-indent-first="1"] .preview-page .chapter-body p:first-child { text-indent: var(--pv-indent); }
.preview-page .chapter-body h1,
.preview-page .chapter-body h2,
.preview-page .chapter-body h3,
.preview-page .chapter-body h4,
.preview-page .chapter-body h5,
.preview-page .chapter-body h6 {
  font-family: var(--pv-heading-font);
  color: var(--pv-text-color);
  margin: 1.4em 0 .5em;
  text-indent: 0;
}
/* H2-H6 size + font overrides driven by theme settings.
   --pv-hN-size is a multiplier of body font-size; --pv-hN-font is an
   optional font-family override. Both fall back to the heading font
   and a typographic default scale when not set. */
.preview-page .chapter-body h2 { font-size: calc(var(--pv-font-size, 12pt) * var(--pv-h2-size, 1.5));  font-family: var(--pv-h2-font, var(--pv-heading-font)); }
.preview-page .chapter-body h3 { font-size: calc(var(--pv-font-size, 12pt) * var(--pv-h3-size, 1.25)); font-family: var(--pv-h3-font, var(--pv-heading-font)); }
.preview-page .chapter-body h4 { font-size: calc(var(--pv-font-size, 12pt) * var(--pv-h4-size, 1.10)); font-family: var(--pv-h4-font, var(--pv-heading-font)); }
.preview-page .chapter-body h5 { font-size: calc(var(--pv-font-size, 12pt) * var(--pv-h5-size, 1.00)); font-family: var(--pv-h5-font, var(--pv-heading-font)); }
.preview-page .chapter-body h6 { font-size: calc(var(--pv-font-size, 12pt) * var(--pv-h6-size, 0.90)); font-family: var(--pv-h6-font, var(--pv-heading-font)); }
.preview-page .chapter-body blockquote {
  border-left: 2px solid var(--pv-accent-color);
  padding: 4px 16px;
  margin: 1em 0;
  font-style: italic;
  color: var(--ink-2);
  text-indent: 0;
}

/* Inline figures (v2.53) in the live preview — canonical book-figure
   block (see the .ql-editor copy for the shape contract). */
.preview-page .chapter-body figure.book-figure {
  width: var(--fig-w, 100%);
  margin: 1em auto;
  text-align: center;
}
.preview-page .chapter-body figure.book-figure img {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  margin: 0;
}
.preview-page .chapter-body figure.book-figure[data-align="left"]  { margin-left: 0; }
.preview-page .chapter-body figure.book-figure[data-align="right"] { margin-right: 0; }
.preview-page .chapter-body figure.book-figure[data-wrap="left"]  { float: left;  margin: .2em 1.4em .8em 0; }
.preview-page .chapter-body figure.book-figure[data-wrap="right"] { float: right; margin: .2em 0 .8em 1.4em; }
.preview-page .chapter-body figure.book-figure figcaption {
  font-size: .85em;
  font-style: italic;
  opacity: .85;
  text-align: center;
  margin-top: .4em;
  text-indent: 0;
}
/* Post-figure paragraph keeps its indent; figure-first chapter keeps a
   flush opener; chapter-end floats are cleared. */
.preview-page .chapter-body figure.book-figure + p { text-indent: var(--pv-indent); }
.preview-page:not(.is-continued) .chapter-body figure.book-figure:first-child + p { text-indent: 0; }
.preview-page .chapter-body::after { content: ""; display: block; clear: both; }

/* ----- Drop cap ----- */
/* :not(.is-continued) — the page after a manual page break continues the
   chapter, so it opens mid-prose and takes no drop cap. */
.preview-stage[data-drop-cap="1"] .preview-page[data-kind="chapter"]:not(.is-continued) .chapter-body p:first-of-type::first-letter {
  font-family: var(--pv-drop-cap-font, var(--pv-body-font));
  font-size: 3.4em;
  line-height: .85;
  float: left;
  padding: .05em .08em 0 0;
  color: var(--pv-accent-color);
  font-weight: 600;
}
/* Drop cap on the paragraph after each scene break, when the
   "every-break" first-sentence mode is selected. */
.preview-stage[data-drop-cap="1"][data-first-sentence-when="every-break"] .preview-page[data-kind="chapter"] .chapter-body .scene-break + p::first-letter {
  font-family: var(--pv-drop-cap-font, var(--pv-body-font));
  font-size: 3.4em;
  line-height: .85;
  float: left;
  padding: .05em .08em 0 0;
  color: var(--pv-accent-color);
  font-weight: 600;
}

/* Lead-in span (rendered server-side / by preview.js when
   drop_cap_lead_small_caps is on). Small-caps + slight letter-spacing
   gives the classic novel-opener look. */
.preview-page .chapter-body .lead-in {
  font-variant: small-caps;
  font-variant-caps: all-small-caps;
  letter-spacing: 0.04em;
}

/* Chapter heading granular overrides — apply on top of the chapter_style preset.
   These read CSS vars set by preview.js / format.js; when not set they
   fall back to the preset's defaults. */
.preview-page .chapter-title {
  font-family: var(--pv-chapter-title-font, var(--pv-heading-font));
  text-align:  var(--pv-chapter-title-align, center);
  font-size:   calc(var(--pv-font-size, 12pt) * var(--pv-chapter-title-size, 1.9));
}
.preview-stage[data-chapter-title-style="italic"]     .preview-page .chapter-title { font-style: italic; font-weight: 500; }
.preview-stage[data-chapter-title-style="bold"]       .preview-page .chapter-title { font-style: normal; font-weight: 700; }
.preview-stage[data-chapter-title-style="small-caps"] .preview-page .chapter-title { font-variant: small-caps; font-variant-caps: all-small-caps; }
.preview-stage[data-chapter-title-style="regular"]    .preview-page .chapter-title { font-style: normal; font-weight: 600; }
/* Width controls the underline / banner width on relevant presets */
.preview-page .chapter-head[data-style="classic"] .chapter-title { width: var(--pv-chapter-title-width, 100%); }
.preview-page .chapter-head[data-style="banner"]  .chapter-title { width: var(--pv-chapter-title-width, 100%); }

.preview-page .chapter-subtitle {
  font-family: var(--pv-chapter-subtitle-font, var(--pv-heading-font));
  text-align:  var(--pv-chapter-subtitle-align, center);
  font-size:   calc(var(--pv-font-size, 12pt) * var(--pv-chapter-subtitle-size, 1.1));
  width:       var(--pv-chapter-subtitle-width, 100%);
}
.preview-stage[data-chapter-subtitle-style="italic"]     .preview-page .chapter-subtitle { font-style: italic; font-weight: 400; }
.preview-stage[data-chapter-subtitle-style="bold"]       .preview-page .chapter-subtitle { font-style: normal; font-weight: 700; }
.preview-stage[data-chapter-subtitle-style="small-caps"] .preview-page .chapter-subtitle { font-variant: small-caps; font-variant-caps: all-small-caps; font-style: normal; }
.preview-stage[data-chapter-subtitle-style="regular"]    .preview-page .chapter-subtitle { font-style: normal; font-weight: 400; }

/* Hide the auto chapter-number prefix when chapter_number_show is off */
.preview-stage[data-chapter-number-show="0"] .preview-page .chapter-num-label { display: none; }

/* ----- Chapter heading styles ----- */
.chapter-head {
  margin-bottom: 1.6em;
  text-indent: 0;
}
.chapter-num-label {
  display: block;
  font-family: var(--pv-heading-font);
  text-transform: uppercase;
  letter-spacing: .25em;
  font-size: .8em;
  color: var(--pv-accent-color);
  margin-bottom: .8em;
}
.chapter-title {
  font-family: var(--pv-heading-font);
  font-weight: 600;
  font-size: 1.9em;
  margin: 0;
  letter-spacing: .01em;
  color: var(--pv-text-color);
}
.chapter-subtitle {
  font-family: var(--pv-heading-font);
  font-style: italic;
  font-size: 1.1em;
  margin: .4em 0 0;
  color: var(--pv-accent-color);
  text-indent: 0;
}

/* Classic — centered, italic large serif title with a thin underline */
.chapter-head[data-style="classic"] { text-align: center; padding-top: 1em; }
.chapter-head[data-style="classic"] .chapter-title {
  font-size: 2em;
  font-style: italic;
  font-weight: 500;
  letter-spacing: .04em;
  display: inline-block;
  padding-bottom: .35em;
  border-bottom: 1px solid var(--pv-accent-color);
}

/* Modern — left-aligned, sans-serif feel, small accent block before */
.chapter-head[data-style="modern"] { text-align: left; padding-top: .4em; }
.chapter-head[data-style="modern"]::before {
  content: '';
  display: block;
  width: 38px;
  height: 3px;
  background: var(--pv-accent-color);
  margin-bottom: .6em;
}
.chapter-head[data-style="modern"] .chapter-title { font-size: 1.6em; font-weight: 600; letter-spacing: -.005em; }

/* Ornate — flourishes above and below the italic title */
.chapter-head[data-style="ornate"] { text-align: center; padding-top: 1.5em; }
.chapter-head[data-style="ornate"]::before {
  content: '❦';
  display: block;
  font-size: 1.4em;
  color: var(--pv-accent-color);
  margin-bottom: 1em;
}
.chapter-head[data-style="ornate"] .chapter-title {
  font-style: italic;
  font-size: 2em;
  font-weight: 500;
}
.chapter-head[data-style="ornate"]::after {
  content: '';
  display: block;
  width: 60px;
  height: 1px;
  background: var(--pv-accent-color);
  margin: .9em auto 0;
}

/* Banner — title rendered as a dark inline-block block (the namesake) */
.chapter-head[data-style="banner"] { text-align: center; padding-top: .5em; }
.chapter-head[data-style="banner"] .chapter-title {
  background: var(--pv-text-color);
  color: var(--pv-bg-color);
  display: inline-block;
  padding: 12px 32px;
  font-size: 1.4em;
  font-weight: 700;
  letter-spacing: .15em;
  text-transform: uppercase;
}

/* Minimal — title only, small + left-aligned */
.chapter-head[data-style="minimal"] { text-align: left; }
.chapter-head[data-style="minimal"] .chapter-title { font-size: 1.4em; font-weight: 500; }

/* Uppercase — centered, all-caps, letter-spaced, no rule */
.chapter-head[data-style="caps"] { text-align: center; padding-top: 1em; }
.chapter-head[data-style="caps"] .chapter-title {
  text-transform: uppercase;
  letter-spacing: .16em;
  font-weight: 600;
  font-size: 1.45em;
}

/* Framed — centered italic title bracketed by hairline rules above + below */
.chapter-head[data-style="framed"] { text-align: center; padding-top: 1em; }
.chapter-head[data-style="framed"] .chapter-title {
  display: inline-block;
  font-style: italic;
  font-weight: 500;
  font-size: 1.7em;
  padding: .3em 0;
  border-top: 1px solid var(--pv-accent-color);
  border-bottom: 1px solid var(--pv-accent-color);
}

/* Editorial — left-aligned title over a full-width hairline rule */
.chapter-head[data-style="editorial"] { text-align: left; padding-top: .4em; border-bottom: 1px solid var(--pv-accent-color); padding-bottom: .4em; }
.chapter-head[data-style="editorial"] .chapter-title { font-size: 1.7em; font-weight: 600; }

/* Grand — oversized upright title at normal weight, wide-tracked eyebrow */
.chapter-head[data-style="grand"] { text-align: center; padding-top: 1em; }
.chapter-head[data-style="grand"] .chapter-title { font-size: 2.2em; font-weight: 400; letter-spacing: .02em; }
.chapter-head[data-style="grand"] .chapter-num-label { letter-spacing: .3em; }

/* Double rule — centered title between full-width double rules */
.chapter-head[data-style="double-rule"] { text-align: center; padding: .5em 0; border-top: 3px double var(--pv-accent-color); border-bottom: 3px double var(--pv-accent-color); }
.chapter-head[data-style="double-rule"] .chapter-title { font-size: 1.6em; font-weight: 600; letter-spacing: .03em; }

/* Accent bar — left-aligned title against a vertical accent rule */
.chapter-head[data-style="accent-bar"] { text-align: left; padding: .2em 0 .2em .7em; border-left: 4px solid var(--pv-accent-color); }
.chapter-head[data-style="accent-bar"] .chapter-title { font-size: 1.6em; font-weight: 600; }

/* Divider rule under the heading — suppressed when chapter_heading_rule
   is off. Three presets draw one: Ornate (the short ::after line beneath
   the title/subtitle), Classic (the underline on the title) and Double
   rule (its bottom border). Scoped to the preview stage's data attribute
   so the toggle is a live preview. */
.preview-stage[data-chapter-heading-rule="0"] .chapter-head[data-style="ornate"]::after { display: none; }
.preview-stage[data-chapter-heading-rule="0"] .chapter-head[data-style="classic"] .chapter-title { border-bottom: none; padding-bottom: 0; }
.preview-stage[data-chapter-heading-rule="0"] .chapter-head[data-style="double-rule"] { border-bottom: none; }

/* Part / section */
.chapter-head[data-style="part"] { text-align: center; padding: 3em 0 2em; }
.chapter-head[data-style="part"] .part-label {
  display: block;
  font-family: var(--pv-heading-font);
  text-transform: uppercase;
  letter-spacing: .35em;
  font-size: .9em;
  color: var(--pv-accent-color);
  margin-bottom: .6em;
}
.chapter-head[data-style="part"] .chapter-title { font-size: 2.6em; font-weight: 600; }

/* ----- Scene breaks ----- */
.scene-break {
  text-align: center;
  margin: 1.4em 0;
  color: var(--pv-accent-color);
  font-family: var(--pv-heading-font);
  text-indent: 0;
}
.scene-break-asterisks::before { content: '*  *  *'; letter-spacing: .25em; }
.scene-break-stars::before { content: '★  ★  ★'; font-size: .8em; letter-spacing: .25em; }
.scene-break-dots::before { content: '•  •  •'; font-size: .9em; letter-spacing: .25em; }
.scene-break-diamonds::before { content: '◆  ◆  ◆'; font-size: .65em; letter-spacing: .3em; }
.scene-break-asterism::before { content: '⁂'; font-size: 1.3em; }
.scene-break-ornament-line::before { content: '─── ❖ ───'; font-size: .9em; letter-spacing: 0; }
.scene-break-thin-line {
  display: block;
  border: none;
  height: 1px;
  background: var(--pv-text-color);
  width: 80px;
  margin: 1.4em auto;
  opacity: .35;
}
.scene-break-double-line {
  display: block;
  border: none;
  border-top: 3px double var(--pv-text-color);
  height: 0;
  width: 120px;
  margin: 1.4em auto;
  opacity: .5;
}
.scene-break-flourish::before { content: '❦'; font-size: 1.4em; }
.scene-break-rune::before { content: '✦'; font-size: 1.3em; }
.scene-break-dingbat::before { content: '❧'; font-size: 1.4em; }
.scene-break-hearts::before { content: '♥  ♥  ♥'; font-size: .7em; letter-spacing: .25em; }
.scene-break-celestial::before { content: '☾ ✦ ☽'; font-size: .9em; letter-spacing: 0; }
.scene-break-daggers::before { content: '†  ‡  †'; font-size: .9em; letter-spacing: .25em; }
.scene-break-florettes::before { content: '❀  ❀  ❀'; font-size: .8em; letter-spacing: .25em; }
.scene-break-fleuron-line::before { content: '── ❦ ──'; font-size: .9em; letter-spacing: 0; }
.scene-break-dotted-line {
  display: block;
  border: none;
  border-top: 2px dotted var(--pv-text-color);
  height: 0;
  width: 120px;
  margin: 1.4em auto;
  opacity: .5;
}

/* Empty preview state */
.preview-empty {
  margin: 0 auto;
}

/* ---------- Auto title page (preview, format, EPUB share class names) ---------- */
.auto-titlepage {
  text-align: center;
  padding: 2.4em 1em 1.4em;
}
.auto-titlepage-title {
  font-family: var(--pv-heading-font, var(--font-display));
  font-size: 2em;
  font-weight: 600;
  margin: 0 0 0.4em;
  line-height: 1.1;
  letter-spacing: .01em;
}
.auto-titlepage-subtitle {
  font-style: italic;
  color: var(--pv-accent-color, var(--accent));
  margin: 0 0 1.4em;
  font-size: 1.05em;
}
.auto-titlepage-author {
  font-family: var(--pv-heading-font, var(--font-display));
  font-size: 1.05em;
  font-weight: 500;
  letter-spacing: .12em;
  margin: 0 0 2.2em;
}
.auto-titlepage-publisher-logo {
  margin: 1.4em auto 0.6em;
  max-width: 60%;
}
.auto-titlepage-publisher-logo img {
  max-width: 100%;
  max-height: 130px;
  height: auto;
  display: block;
  margin: 0 auto;
}
.auto-titlepage-publisher-name {
  font-family: var(--pv-heading-font, var(--font-display));
  font-size: 0.78em;
  letter-spacing: .14em;
  text-transform: uppercase;
  margin: 0;
  color: var(--pv-text-color, var(--ink-1));
}

/* ---------- Auto Copyrights ---------- */
.auto-copyrights {
  padding: 1em;
  max-width: 36em;
  margin: 0 auto;
}
.auto-copyrights .auto-copyrights-line {
  font-size: 0.92em;
  line-height: 1.5;
  margin: 0 0 0.85em;
  text-indent: 0;
  text-align: left;
}
.auto-copyrights .auto-copyrights-line a {
  color: inherit;
  text-decoration: underline;
}

/* ---------- Auto TOC ---------- */
.auto-toc {
  list-style: none;
  margin: 1em 0;
  padding: 0 0.5em;
}
.auto-toc-item {
  padding: 0.4em 0;
  font-size: 0.95em;
  text-indent: 0;
  cursor: pointer;
}
.auto-toc-item:hover .auto-toc-text { color: var(--pv-accent-color, var(--accent)); }
.auto-toc-num {
  display: inline-block;
  min-width: 1.6em;
  color: var(--pv-accent-color, var(--accent));
  font-family: var(--pv-heading-font, var(--font-display));
}
.auto-toc-front { font-style: italic; }
.auto-toc-back  { font-style: italic; }
.auto-toc-part  { font-weight: 700; text-transform: uppercase; letter-spacing: .08em; }

/* TOC style: Classic — dotted leader between title and (no page) */
.auto-toc-classic .auto-toc-item {
  border-bottom: 1px dotted var(--pv-accent-color, var(--accent));
}

/* TOC style: Plain — no decorations, generous spacing */
.auto-toc-plain .auto-toc-item {
  padding: 0.55em 0;
  border: 0;
}
.auto-toc-plain .auto-toc-num { color: var(--pv-text-color); opacity: .55; }

/* TOC style: Lined — solid hairline rule under each row */
.auto-toc-lined .auto-toc-item {
  border-bottom: 1px solid color-mix(in srgb, var(--pv-text-color) 18%, transparent);
}
.auto-toc-lined .auto-toc-part { border-bottom-width: 2px; }

/* TOC style: Decorative — ornament between rows, centered titles */
.auto-toc-decorative {
  text-align: center;
  padding: 0;
}
.auto-toc-decorative .auto-toc-item {
  padding: 0.55em 0 0.85em;
  border: 0;
  position: relative;
}
.auto-toc-decorative .auto-toc-item + .auto-toc-item::before {
  content: "❦";
  position: absolute;
  top: -0.15em;
  left: 50%;
  transform: translateX(-50%);
  color: var(--pv-accent-color, var(--accent));
  font-size: .8em;
  opacity: .7;
}
.auto-toc-decorative .auto-toc-num {
  display: block;
  min-width: 0;
  font-size: .75em;
  letter-spacing: .12em;
  text-transform: uppercase;
  margin-bottom: 0.15em;
}
.auto-toc-decorative .auto-toc-text { font-style: italic; }

/* TOC style: Centered — entries centered, no rules */
.auto-toc-centered { text-align: center; }
.auto-toc-centered .auto-toc-item { border: 0; padding: 0.5em 0; justify-content: center; }
.auto-toc-centered .auto-toc-num { margin-right: .4em; }

/* TOC style: Modern — clean, uppercase spaced numbers, airy, no rules */
.auto-toc-modern .auto-toc-item { border: 0; padding: 0.6em 0; }
.auto-toc-modern .auto-toc-num {
  text-transform: uppercase;
  letter-spacing: .1em;
  font-size: .8em;
  opacity: .6;
}
.auto-toc-modern .auto-toc-text { font-weight: 500; }

/* TOC style: Numbered — accent-colored, prominent chapter numbers */
.auto-toc-numbered .auto-toc-item { border: 0; padding: 0.45em 0; }
.auto-toc-numbered .auto-toc-num {
  color: var(--pv-accent-color, var(--accent));
  font-weight: 700;
  min-width: 1.8em;
}

/* Mini TOC heading previews (inspector radio cards) */
.toc-preview-classic   { font-size: 11px; border-bottom: 1px dotted var(--accent); padding: 2px 6px; min-width: 60px; text-align: left; }
.toc-preview-plain     { font-size: 11px; padding: 2px 6px; min-width: 60px; text-align: left; }
.toc-preview-lined     { font-size: 11px; border-bottom: 1px solid var(--ink-3); padding: 2px 6px; min-width: 60px; text-align: left; }
.toc-preview-decorative { font-size: 11px; font-style: italic; padding: 2px 6px; min-width: 60px; text-align: center; }
.toc-preview-decorative::before { content: "❦ "; color: var(--accent); font-style: normal; font-size: .8em; }
.toc-preview-centered  { font-size: 11px; padding: 2px 6px; min-width: 60px; text-align: center; }
.toc-preview-modern    { font-size: 11px; padding: 2px 6px; min-width: 60px; text-align: left; text-transform: uppercase; letter-spacing: .08em; }
.toc-preview-numbered  { font-size: 11px; padding: 2px 6px; min-width: 60px; text-align: left; font-weight: 700; color: var(--accent); }

/* ============================================================
   PHASE 3 — Export view
   ============================================================ */
.export-view {
  background: var(--bg-page);
  overflow: auto;
}
.export-view[hidden] { display: none !important; }
.export-shell {
  flex: 1;
  display: flex;
  flex-direction: column;
  width: 100%;
}
.export-hero {
  padding: 32px 48px 18px;
  border-bottom: 1px solid var(--line);
  background: #fff;
}
.export-hero-title {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 600;
  margin: 0 0 4px;
  letter-spacing: .01em;
}
.export-hero-sub {
  margin: 0;
  color: var(--ink-3);
  font-size: 14px;
  max-width: 720px;
}
.export-grid {
  display: grid;
  grid-template-columns: 280px 1fr;
  gap: 32px;
  padding: 32px 48px 64px;
  align-items: start;
  max-width: 1200px;
  width: 100%;
  margin: 0 auto;
}

/* ---- Cover + summary ---- */
.export-summary {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 22px 22px 24px;
}
.export-cover {
  position: relative;
  border-radius: 3px;
  overflow: hidden;
  margin-bottom: 14px;
  /* No fixed ratio — sizes to image's intrinsic aspect. */
}
.export-cover:has(img) { background: var(--ink-1); box-shadow: var(--shadow-paper); }
.export-cover img { width: 100%; height: auto; display: block; }
.export-cover.is-uploading::after {
  content: 'Uploading…';
  position: absolute; inset: 0;
  background: rgba(0,0,0,.55); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; letter-spacing: .04em;
}
.cover-fallback {
  /* When no cover is uploaded, render a 2:3 placeholder. */
  aspect-ratio: 2 / 3;
  background: linear-gradient(155deg, #2c2820 0%, #1a1715 100%);
  border-radius: 3px;
  box-shadow: var(--shadow-paper);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  padding: 24px 18px;
  border-left: 4px solid var(--accent);
}
.cover-fallback .cover-title {
  font-family: var(--font-display);
  font-size: 17px;
  line-height: 1.25;
  color: #f1ead2;
  font-weight: 500;
}
.cover-fallback .cover-author {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 11px;
  color: rgba(241,234,210,.6);
  margin-top: 12px;
  letter-spacing: .04em;
}
.export-cover-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}
.export-cover-actions label.btn { cursor: pointer; }

.export-book-title {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 600;
  margin: 0 0 2px;
  color: var(--ink-1);
  line-height: 1.3;
}
.export-book-author {
  font-family: var(--font-serif);
  font-style: italic;
  color: var(--ink-3);
  margin: 0 0 16px;
  font-size: 13px;
}
.export-stats {
  list-style: none;
  margin: 0;
  padding: 14px 0 0;
  border-top: 1px solid var(--line-soft);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.export-stats li {
  font-size: 13px;
  color: var(--ink-3);
}
.export-stats strong { color: var(--ink-1); font-weight: 600; }

/* Right column on the export view: action cards on top, advisory
   preflight beneath. Stacked vertically so each takes the full
   width of the 1fr column. */
.export-main {
  display: flex;
  flex-direction: column;
  gap: 22px;
  min-width: 0;
}

/* ---- Pre-flight checks card ---- */
.export-preflight {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px 22px 14px;
}
/* Inside the summary card (v3.24 — under the book stats) it's a divided
   section of that card, never a card nested in a card. */
.export-summary .export-preflight {
  background: none;
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
  padding: 16px 0 0;
  margin-top: 16px;
}
.export-preflight-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 8px;
}
.export-preflight-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 500;
  color: var(--ink-1);
}
.export-preflight-summary {
  font-size: 12px;
  color: var(--ink-3);
}
.export-preflight-summary[data-tone="pass"] { color: #2f6633; }
.export-preflight-summary[data-tone="warn"] { color: #8a4d18; }
.export-preflight-summary[data-tone="fail"] { color: var(--danger, #b04646); font-weight: 600; }
.export-preflight-list {
  list-style: none;
  margin: 6px 0 0;
  padding: 0;
}
.export-preflight-item {
  display: grid;
  grid-template-columns: 14px auto 1fr;
  align-items: baseline;
  gap: 10px;
  padding: 5px 0;
  font-size: 13px;
  color: var(--ink-2);
  line-height: 1.45;
}
.export-preflight-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--ink-4);
  display: inline-block;
}
.export-preflight-item[data-level="pass"] .export-preflight-dot { background: #4a8b4d; }
.export-preflight-item[data-level="warn"] .export-preflight-dot { background: #d18a3d; }
.export-preflight-item[data-level="fail"] .export-preflight-dot { background: var(--danger, #b04646); }
.export-preflight-item[data-level="info"] .export-preflight-dot { background: #6b7480; }
.export-preflight-level {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
}
.export-preflight-item[data-level="fail"] .export-preflight-level { color: var(--danger, #b04646); }
.export-preflight-item[data-level="warn"] .export-preflight-level { color: #8a4d18; }
.export-preflight-item[data-level="pass"] .export-preflight-level { color: #2f6633; }
.export-preflight-msg { color: var(--ink-1); }

/* ---- Export options ---- */
.export-options {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 22px;
}
.export-card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 22px 24px;
  display: flex;
  flex-direction: column;
}
.export-card-icon {
  font-size: 28px;
  margin-bottom: 14px;
}
/* Compact cards (3.24): icon + title + one-line tagline in a head row,
   primary action next, everything else behind a <details>. All five
   products fit one viewport this way. */
.export-card-head {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  margin-bottom: 14px;
}
.export-card-head .export-card-icon { margin-bottom: 0; font-size: 26px; line-height: 1.2; }
.export-card-head .export-card-title { margin: 0 0 3px; font-size: 18px; }
.export-card-tag {
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--ink-3);
}
.export-card-more { margin-top: 14px; }
.export-card-more summary {
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  color: var(--accent);
  list-style: none;
  user-select: none;
  width: fit-content;
  letter-spacing: .01em;
}
.export-card-more summary:hover { text-decoration: underline; }
.export-card-more summary::-webkit-details-marker { display: none; }
.export-card-more summary::before {
  content: '▸';
  margin-right: 6px;
  color: var(--accent);
  font-size: 11px;
}
.export-card-more[open] summary::before { content: '▾'; }
.export-card-more[open] summary { margin-bottom: 10px; }
.export-card-more .export-card-desc { margin-top: 0; }
.export-card-more .export-card-meta { margin-bottom: 10px; }
/* The audiobook card: full grid width, and its summary IS the primary
   button ("Set up & generate") — the real #ab-generate sits inside. */
.export-card--wide { grid-column: 1 / -1; }
/* The summary carries .btn.btn-primary, but the generic
   `.export-card-more summary` styling above outranks the .btn classes
   (0,1,1 vs 0,1,0) — restore the button's own text explicitly, or it
   renders accent-on-dark / dark-on-dark and disappears. */
.export-card-setup summary {
  width: fit-content;
  color: var(--bg-page);
  font-size: 14px;      /* .btn-lg */
  font-weight: 500;     /* .btn */
  letter-spacing: .01em;
}
.export-card-setup summary:hover { text-decoration: none; color: #fff; }
:root[data-theme="dark"] .export-card-setup summary:hover { color: #1c1a17; }
.export-card-setup summary::before { content: none; }
.export-card-setup summary::after { content: '▾'; margin-left: 8px; font-size: 12px; }
.export-card-setup[open] summary::after { content: '▴'; }
.export-card--wide .audiobook-controls {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px 18px;
  align-items: start;
}
.export-card--wide .audiobook-field--chapters,
.export-card--wide .audiobook-check,
.export-card--wide .audiobook-pron-help { grid-column: 1 / -1; }
/* "Members" pill on export buttons for free accounts — the click opens
   the unlock dialog (export.js) instead of downloading. */
.export-lock-badge {
  display: inline-block;
  margin-left: 8px;
  padding: 2px 7px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  border-radius: 999px;
  background: rgba(255,255,255,.22);
  border: 1px solid rgba(255,255,255,.45);
  vertical-align: 2px;
}
.btn-link .export-lock-badge,
.export-menu-item .export-lock-badge {
  background: var(--bg-rail);
  border-color: var(--line);
  color: var(--ink-3);
}
.export-card-title {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 600;
  margin: 0 0 10px;
  color: var(--ink-1);
}
.export-card-desc {
  margin: 0 0 14px;
  color: var(--ink-2);
  font-size: 14px;
  line-height: 1.55;
}
.export-card-meta {
  list-style: none;
  margin: 0 0 22px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.export-card-meta li {
  font-size: 12.5px;
  color: var(--ink-3);
  padding-left: 18px;
  position: relative;
}
.export-card-meta li::before {
  content: '✓';
  position: absolute;
  left: 0;
  top: 0;
  color: var(--accent);
  font-weight: 700;
  font-size: 12px;
}
.export-card .btn { align-self: flex-start; margin-top: auto; }

/* ---- Audiobook export card ---- */
.audiobook-controls {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 4px 0 16px;
}
.audiobook-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  color: var(--ink-3);
}
.audiobook-field select {
  width: 100%;
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-paper);
  color: var(--ink-1);
  font-size: 14px;
}
.audiobook-check {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--ink-2);
  cursor: pointer;
}
.audiobook-check input { accent-color: var(--accent); }
#audiobook-card .btn { align-self: stretch; margin-top: 0; }
#ab-cancel { margin-top: 8px; }
.audiobook-progress { margin-top: 14px; }
.audiobook-progress-track {
  height: 8px;
  border-radius: 99px;
  background: var(--bg-rail);
  overflow: hidden;
}
.audiobook-progress-track > span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--accent);
  border-radius: 99px;
  transition: width .25s ease;
}
.audiobook-progress-label {
  margin: 8px 0 0;
  font-size: 12.5px;
  color: var(--ink-3);
}
.audiobook-note {
  margin: 0 0 12px;
  font-size: 12px;
  color: var(--ink-4);
}
.audiobook-chapters-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.audiobook-chapters-actions { display: flex; gap: 8px; }
.audiobook-chapters-actions .btn-link { font-size: 12px; }
.audiobook-chapters {
  margin-top: 6px;
  max-height: 200px;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-paper);
  padding: 4px;
}
.audiobook-chapter-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 6px;
  border-radius: var(--radius-sm);
  font-size: 13px;
  color: var(--ink-1);
  cursor: pointer;
}
.audiobook-chapter-row:hover { background: var(--bg-rail); }
.audiobook-chapter-row input { accent-color: var(--accent); flex: none; }
.audiobook-chapter-title {
  flex: 1 1 auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.audiobook-chapter-badge {
  flex: none;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--ink-4);
  background: var(--bg-rail);
  padding: 1px 6px;
  border-radius: 99px;
}
.audiobook-pron > summary {
  cursor: pointer;
  font-size: 13px;
  color: var(--ink-2);
  padding: 2px 0;
}
.audiobook-pron-help {
  margin: 6px 0 6px;
  font-size: 12px;
  color: var(--ink-4);
}
.audiobook-pron-help code {
  background: var(--bg-rail);
  padding: 1px 4px;
  border-radius: 3px;
  font-size: 11.5px;
}
.audiobook-pron textarea {
  width: 100%;
  resize: vertical;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-paper);
  color: var(--ink-1);
  font-size: 13px;
  font-family: var(--font-sans);
}

@media (max-width: 1100px) {
  .export-grid { grid-template-columns: 1fr; padding: 24px 28px 60px; }
  .export-options { grid-template-columns: 1fr; }
}

/* ============================================================
   Modal — Edit book details (tabbed)
   ============================================================ */
.modal-card-wide {
  max-width: 880px;
  padding: 0;
  overflow: hidden;
}
.modal-tabs {
  display: flex;
  align-items: center;
  border-bottom: 1px solid var(--line);
  padding: 0 6px;
  position: relative;
}
.modal-tab {
  background: transparent;
  border: none;
  padding: 16px 22px 14px;
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink-3);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.modal-tab:hover { color: var(--ink-1); }
.modal-tab.is-active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
.modal-close {
  position: absolute;
  top: 10px; right: 12px;
  width: 32px; height: 32px;
  border: none; background: transparent;
  font-size: 22px; line-height: 1;
  color: var(--ink-3); cursor: pointer; padding: 0;
  border-radius: 50%;
}
.modal-close:hover { background: var(--bg-rail); color: var(--ink-1); }

.modal-tab-body {
  padding: 28px 32px 32px;
}
.modal-tab-body[hidden] { display: none; }
.modal-help {
  font-size: 13px;
  color: var(--ink-3);
  margin: 0 0 20px;
  line-height: 1.55;
}
.modal-grid {
  display: grid;
  grid-template-columns: 1fr 200px;
  gap: 28px;
  align-items: start;
}
.modal-grid-main { min-width: 0; }
.modal-cover {
  aspect-ratio: 2 / 3;
  background: var(--ink-1);
  border-radius: 4px;
  overflow: hidden;
  box-shadow: var(--shadow-paper);
}
.modal-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.modal-cover-fallback {
  width: 100%; height: 100%;
  background: linear-gradient(155deg, #2c2820 0%, #1a1715 100%);
  border-left: 4px solid var(--accent);
  display: flex; align-items: center; justify-content: center;
  text-align: center;
  padding: 16px;
  color: #f1ead2;
  font-family: var(--font-display);
  font-size: 14px;
}
.modal-cover-hint {
  margin: 12px 0 0;
  font-size: 12px;
  color: var(--ink-3);
}

.field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
.field-coming {
  font-size: 11px;
  color: var(--ink-4);
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  font-style: italic;
}
/* A value the server refused (a blank title): outlined, never coerced. */
.field input.is-invalid { border-color: var(--danger, #c0392b); box-shadow: 0 0 0 2px rgba(192, 57, 43, .18); }
.field-help {
  display: block;
  margin-top: 4px;
  font-size: 11px;
  color: var(--ink-4);
  letter-spacing: 0;
  text-transform: none;
}
/* Read-only text sitting where an input would be — e.g. the computed
   words-per-day pace on Book details. Matches input vertical rhythm so
   the row lines up with its neighbours. */
.field-static {
  margin: 0;
  padding: 8px 0;
  font-size: 13.5px;
  color: var(--ink-2);
  line-height: 1.45;
}
.field-static .muted,
.field-static.muted { color: var(--ink-4); }
.field-help code {
  background: var(--bg-rail);
  padding: 1px 5px;
  border-radius: 3px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10.5px;
}

/* ============================================================
   PHASE B — Book details page
   ============================================================ */
.details-body { background: var(--bg-page); min-height: 100vh; }
.details-shell { min-height: 100vh; display: flex; flex-direction: column; }

.btn-back-editor {
  display: inline-flex; align-items: center;
  background: transparent;
  border: 1px solid #3a3733;
  color: #c8c3b6;
  font-size: 12.5px;
  padding: 5px 12px;
  border-radius: 999px;
  text-decoration: none;
  letter-spacing: .01em;
  transition: background .15s ease, color .15s ease;
}
.btn-back-editor:hover { background: var(--bg-topbar-soft); color: #fff; }

.details-main {
  flex: 1;
  padding: 32px 48px 80px;
  max-width: 1280px;
  margin: 0 auto;
  width: 100%;
}
.details-grid {
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: 28px;
  align-items: start;
}

/* ----- Left: tabbed form card ----- */
.details-card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.details-tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--line);
  padding: 0 24px;
  background: #fff;
}
.details-tab {
  background: transparent;
  border: none;
  padding: 18px 4px 14px;
  margin: 0 14px 0 0;
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-3);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.details-tab:hover { color: var(--ink-1); }
.details-tab.is-active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
.details-tab-pane { padding: 32px 36px 36px; }
.details-tab-pane[hidden] { display: none; }

.details-form-grid {
  display: grid;
  grid-template-columns: 1fr 220px;
  gap: 36px;
  align-items: start;
}
.details-form-main { min-width: 0; }
.details-form-side {
  display: flex;
  flex-direction: column;
  align-items: stretch;
}

.details-cover, .details-logo {
  position: relative;
  background: var(--ink-1);
  border-radius: 4px;
  overflow: hidden;
  box-shadow: var(--shadow-paper);
  margin-bottom: 12px;
}
/* Cover container has no fixed ratio — it sizes to the image's natural ratio.
   Only the fallback (when no cover uploaded) uses a 2:3 placeholder. */
.details-cover { background: transparent; box-shadow: none; }
.details-cover:has(img) { background: var(--ink-1); box-shadow: var(--shadow-paper); }
.details-logo  {
  background: #fafaf6;
  border: 1px solid var(--line);
  aspect-ratio: 1 / 1;
  box-shadow: none;
  display: flex;
  align-items: center;
  justify-content: center;
}
.details-cover img {
  width: 100%;
  height: auto;
  display: block;
  /* No object-fit — the box flexes to the image's intrinsic aspect ratio. */
}
.details-logo img  { max-width: 80%; max-height: 80%; object-fit: contain; display: block; }
.details-cover.is-uploading::after,
.details-logo.is-uploading::after {
  content: 'Uploading…';
  position: absolute; inset: 0;
  background: rgba(0,0,0,.55); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; letter-spacing: .04em;
}
.details-cover-fallback {
  /* Render as a 2:3 block when no cover is uploaded. */
  aspect-ratio: 2 / 3;
  background: linear-gradient(155deg, #2c2820 0%, #1a1715 100%);
  border-left: 4px solid var(--accent);
  border-radius: 4px;
  box-shadow: var(--shadow-paper);
  display: flex; align-items: center; justify-content: center;
  text-align: center;
  padding: 16px;
  color: #f1ead2;
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 1.3;
}
.details-cover-title { color: #f1ead2; font-weight: 500; }
.details-logo-fallback {
  font-size: 12px;
  color: var(--ink-4);
  font-style: italic;
}
.details-cover-btn {
  align-self: stretch;
  text-align: center;
}
.cover-remove {
  position: absolute;
  top: 8px; right: 8px;
  width: 26px; height: 26px;
  border-radius: 50%;
  border: none;
  background: rgba(255,255,255,.92);
  color: var(--ink-2);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  box-shadow: 0 1px 4px rgba(0,0,0,.2);
}
.cover-remove:hover { background: var(--danger); color: #fff; }

/* Chip input (Author shows as chip in Atticus) */
.chip-input {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 6px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-paper);
  margin-bottom: 6px;
  min-height: 32px;
}
.chip-input:empty { display: none; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 8px 3px 10px;
  background: var(--accent-bg);
  border: 1px solid var(--accent-soft);
  border-radius: 4px;
  font-size: 12.5px;
  color: var(--ink-1);
}
/* Contributors (184): role picker + name entry on one row. The .field
   width:100% rule lets a select swallow the whole row (it did), so the
   bases are explicit: the role gets a fixed lane, the name gets the
   rest. min-width:0 lets the input genuinely shrink on narrow panes. */
.contrib-entry-row { display: flex; gap: 8px; }
.contrib-entry-row select { flex: 0 0 160px; width: auto; }
.contrib-entry-row input { flex: 1 1 auto; min-width: 0; }
/* Contributors (184): the role rides inside the chip, quieter than
   the name. Same chip chrome as the author field above it. */
.chip-role { color: var(--ink-3); font-weight: 400; }
.chip-x { color: var(--ink-3); cursor: pointer; font-weight: 600; border: 0; background: none; padding: 0 2px; font: inherit; font-weight: 600; line-height: 1; }
.chip-x:hover { color: var(--danger); }

/* ----- Right rail of stat cards ----- */
.details-rail {
  display: flex;
  flex-direction: column;
  gap: 18px;
  position: sticky;
  top: 16px;
}
.rail-card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px 20px 20px;
}
.rail-card-title {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-1);
  margin: 0 0 14px;
  display: flex;
  align-items: center;
  gap: 6px;
}
.info-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--ink-5);
  color: #fff;
  font-size: 9px;
  font-style: italic;
  font-weight: 700;
  font-family: var(--font-display);
}
.stat-block {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 10px;
}
.stat-num {
  font-family: var(--font-display);
  font-size: 32px;
  font-weight: 600;
  color: var(--accent);
  line-height: 1;
}
.stat-label {
  color: var(--ink-3);
  font-size: 13px;
}
.rail-card-meta {
  list-style: none;
  margin: 6px 0 0;
  padding: 12px 0 0;
  border-top: 1px solid var(--line-soft);
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13px;
  color: var(--ink-3);
}
.rail-card-meta li {
  display: flex;
  justify-content: space-between;
}
.rail-card-meta strong { color: var(--ink-1); font-weight: 600; }

.rail-export-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 8px;
}
.btn-success {
  background: #16a34a;
  border-color: #16a34a;
  color: #fff;
}
.btn-success:hover { background: #15803d; border-color: #15803d; color: #fff; }
.btn-success:disabled,
.btn-success[disabled] {
  background: #c8c3b6;
  border-color: #c8c3b6;
  color: #fff;
  cursor: not-allowed;
}

.rail-card-help {
  margin: 10px 0 0;
  font-size: 12px;
  color: var(--ink-4);
  line-height: 1.55;
}

/* ----- Snapshot timeline (book details right rail) -----
   Vertical commit-history-style spine with a dot per snapshot. The
   spine is a pseudo-element on the <ol> so it draws between dots even
   if rows have variable height; each .snap-dot is absolutely-positioned
   over the spine. */
.snap-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.snapshot-empty {
  padding: 14px 0 4px;
  font-size: 12px;
  color: var(--ink-4);
  font-style: italic;
  text-align: center;
}
.snap-timeline {
  list-style: none;
  margin: 12px 0 0;
  padding: 4px 0 4px 0;
  position: relative;
}
.snap-timeline::before {
  content: '';
  position: absolute;
  left: 7px;
  top: 12px;
  bottom: 12px;
  width: 2px;
  background: var(--line);
  border-radius: 1px;
}
.snap-node {
  position: relative;
  display: grid;
  grid-template-columns: 22px 1fr auto;
  align-items: start;
  gap: 10px;
  padding: 10px 0;
}
.snap-dot {
  position: relative;
  display: block;
  width: 12px; height: 12px;
  margin: 4px 0 0 1px;
  background: var(--accent);
  border: 2px solid var(--bg-page, #fff);
  border-radius: 50%;
  box-shadow: 0 0 0 1px var(--line);
}
.snap-body { min-width: 0; }
.snap-line {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
}
.snap-rel {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-3);
  cursor: help;
}
.snap-label {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink-1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
.snap-label-empty {
  font-style: italic;
  font-weight: 400;
  color: var(--ink-3);
}
.snap-stats {
  font-size: 11.5px;
  color: var(--ink-3);
  margin-top: 3px;
}
.snap-actions {
  display: flex;
  gap: 4px;
  align-items: flex-start;
  flex-shrink: 0;
}
.snap-link {
  width: 26px; height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  background: #fff;
  color: var(--ink-2);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 14px;
  text-decoration: none;
  padding: 0;
  font-family: inherit;
  transition: background .12s, color .12s, border-color .12s;
}
.snap-link:hover { background: var(--bg-rail); border-color: var(--line-strong); color: var(--ink-1); }
.snap-link-danger:hover { background: var(--danger); border-color: var(--danger); color: #fff; }

/* ----- Beta-reader share rail card + modal ----- */
.share-list {
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  border-top: 1px solid var(--line-soft);
}
.share-empty {
  padding: 14px 0 4px;
  font-size: 12px;
  color: var(--ink-4);
  font-style: italic;
  text-align: center;
}
.share-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  padding: 12px 0;
  border-bottom: 1px solid var(--line-soft);
}
.share-row:last-child { border-bottom: none; }
.share-meta { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.share-label {
  font-size: 13px;
  color: var(--ink-1);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 220px;
}
.share-sub { font-size: 11px; color: var(--ink-3); }
.share-chips { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 2px; }
.share-chip {
  display: inline-flex;
  align-items: center;
  font-size: 10.5px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--bg-rail);
  color: var(--ink-3);
  border: 1px solid var(--line-soft);
  white-space: nowrap;
}
.share-chip-danger { background: #fde6e6; color: #8a1f1f; border-color: #f4c4c4; }
.share-actions { display: flex; gap: 4px; flex-shrink: 0; }

.share-modal-card { max-width: 520px; }

/* ----- Releases rail card (KDP / IngramSpark / etc.) -----
   Mirrors the share-list look so the two rail cards feel uniform —
   same row chrome, same chip pill, same right-side action buttons. */
.release-list {
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  border-top: 1px solid var(--line-soft);
}
.release-empty {
  padding: 14px 0 4px;
  font-size: 12px;
  color: var(--ink-4);
  font-style: italic;
  text-align: center;
}
.release-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  padding: 12px 0;
  border-bottom: 1px solid var(--line-soft);
}
.release-row:last-child { border-bottom: none; }
.release-meta { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.release-label {
  font-size: 13px;
  color: var(--ink-1);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 220px;
}
.release-sub { font-size: 11px; color: var(--ink-3); }
.release-chips { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 2px; }
.release-chip {
  display: inline-flex;
  align-items: center;
  font-size: 10.5px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--bg-rail);
  color: var(--ink-3);
  border: 1px solid var(--line-soft);
  white-space: nowrap;
}
/* Status-tinted chips. "live" gets the affirmative green tone, "delisted"
   gets the danger tone — quick visual scan of what's actually selling. */
.release-chip-live     { background: #e6f5ea; color: #1e6b35; border-color: #c4e4cf; }
.release-chip-delisted { background: #fde6e6; color: #8a1f1f; border-color: #f4c4c4; }
.release-chip-paused   { background: #fbf1cf; color: #6b5210; border-color: #efdf95; }
.release-actions { display: flex; gap: 4px; flex-shrink: 0; }

.release-modal-card { max-width: 540px; }

/* ----- Editor sub-tab views: Releases + Marketing ("Publishing").
   Single-column layout (no left sidebar, unlike Resources/Bible) —
   header bar on top, a full-width table below. Lives under the
   "Resources" top tab via the Workshop page picker (v3.19). */
.editor-view.publishing-view {
  flex-direction: column;
  background: var(--bg-page);
  overflow: hidden;
}
.publishing-view[hidden] { display: none !important; }

.publishing-main {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 28px 36px 48px;
}
/* Every Workshop pane reads as a centered column, like the Cover
   calculator (1000px shell) and the KDP steps (860px). The cap sits on
   the CHILDREN, not on .publishing-main — that's the scroll container,
   and capping it shoves the pane (scrollbar included) to the left edge
   of a wide screen (see the KDP keywords note). KDP's tighter 860px
   caps still win through their #view-kdp specificity. */
.publishing-main > * {
  max-width: 1200px;
  margin-left: auto;
  margin-right: auto;
}
.publishing-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: 22px;
  max-width: 1200px;
}
.publishing-title {
  margin: 0 0 6px;
  font-size: 22px;
  font-weight: 600;
  color: var(--ink-1);
}
.publishing-sub {
  margin: 0;
  font-size: 13px;
  color: var(--ink-3);
  max-width: 720px;
  line-height: 1.5;
}

.publishing-table-wrap {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 6px;
  overflow: hidden;
  max-width: 1200px;
}
.publishing-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.publishing-table th {
  text-align: left;
  padding: 10px 12px;
  background: var(--bg-rail);
  color: var(--ink-2);
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .08em;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.publishing-table td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: middle;
  color: var(--ink-2);
}
.publishing-table tbody tr:last-child td { border-bottom: none; }
.publishing-table tbody tr:hover td { background: var(--bg-rail); }
.publishing-cell-strong { color: var(--ink-1); font-weight: 500; }
.publishing-actions-col { width: 72px; }
.publishing-actions { text-align: right; white-space: nowrap; }
.publishing-actions .snap-link { margin-left: 4px; }
.publishing-empty-row td {
  text-align: center;
  padding: 28px 12px;
  color: var(--ink-4);
  font-style: italic;
}

/* Member-gate panel shown in a publishing sub-tab when a free user
   opens a paid feature (e.g. Beta readers). Centered, calm upsell. */
.publishing-locked {
  max-width: 560px;
  margin: 32px auto;
  padding: 40px 32px;
  text-align: center;
  background: var(--bg-rail);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
/* ARC campaign detail (197/198). Unlike the other Workshop panes, the
   detail view renders headings, forms and field labels OUTSIDE
   .publishing-table-wrap cards — over a per-book backdrop they sat
   straight on the artwork and disappeared. One opaque panel behind the
   whole detail; the table-wraps inside read as white cards on it.
   Theme tokens, so dark mode gets its own solids. */
#arc-detail {
  background: var(--bg-page);
  border: 1px solid var(--line);
  border-radius: var(--radius, 10px);
  padding: 22px 26px 30px;
}
#arc-detail h3 { color: var(--ink-1); }
.publishing-locked-icon { font-size: 34px; line-height: 1; margin-bottom: 14px; }
.publishing-locked-title { margin: 0 0 10px; font-size: 1.2rem; }
.publishing-locked-text {
  margin: 0 0 22px;
  color: var(--ink-2);
  line-height: 1.55;
}

/* Status pills shared between releases and campaigns. The base pill is
   the neutral "planned" look; status-tinted variants override. */
.pub-pill {
  display: inline-flex;
  align-items: center;
  font-size: 11px;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--bg-rail);
  color: var(--ink-3);
  border: 1px solid var(--line-soft);
  white-space: nowrap;
}
.pub-pill-live,
.pub-pill-active,
.pub-pill-completed  { background: #e6f5ea; color: #1e6b35; border-color: #c4e4cf; }
.pub-pill-delisted,
.pub-pill-cancelled  { background: #fde6e6; color: #8a1f1f; border-color: #f4c4c4; }
.pub-pill-paused     { background: #fbf1cf; color: #6b5210; border-color: #efdf95; }

.pub-dash { color: var(--ink-4); }

/* ---------- Book details: cover actions ---------- */
.details-cover-actions {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 8px;
}
.share-chapter-picker {
  list-style: none;
  margin: 8px 0 0;
  padding: 8px 10px;
  max-height: 240px;
  overflow-y: auto;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  background: var(--bg-rail);
}
.share-chapter-picker li { padding: 2px 0; }
.share-chapter-picker .field-toggle { display: flex; align-items: center; gap: 8px; }
.share-chapter-kind {
  font-size: 10.5px;
  color: var(--ink-4);
  text-transform: uppercase;
  letter-spacing: .06em;
  margin-left: 6px;
}

/* ----- Public beta-reader unlock page ----- */
.share-unlock-body {
  background: #1d1b18;
  color: #e8e3d6;
  min-height: 100vh;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.share-unlock-shell { width: 100%; padding: 24px; }
.share-unlock-card {
  max-width: 380px;
  margin: 0 auto;
  background: #25221e;
  border: 1px solid #3a3733;
  border-radius: 10px;
  padding: 28px;
  box-shadow: 0 12px 36px rgba(0,0,0,.4);
}
.share-unlock-title {
  font-family: 'Playfair Display', Georgia, serif;
  font-size: 22px;
  font-weight: 600;
  margin: 0 0 8px;
  color: #fff;
}
.share-unlock-help {
  margin: 0 0 16px;
  color: #b8b3a6;
  font-size: 13px;
  line-height: 1.5;
}
.share-unlock-form .field-label { color: #c8c3b6; }
.share-unlock-form input[type="password"] {
  width: 100%;
  background: #1d1b18;
  border: 1px solid #3a3733;
  color: #e8e3d6;
  padding: 10px 12px;
  border-radius: 6px;
  font-size: 14px;
}
.share-unlock-form input[type="password"]:focus {
  outline: none;
  border-color: #8b6914;
  box-shadow: 0 0 0 2px rgba(139,105,20,.25);
}

/* ----- Public beta-reader main view ----- */
.share-reader-body {
  background: var(--reader-bg, #fdfbf6);
  color: var(--reader-text, #1a1a1a);
  margin: 0;
  min-height: 100vh;
}
.share-reader-shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
.share-reader-topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  background: rgba(29,27,24,.96);
  color: #e8e3d6;
  border-bottom: 1px solid #2a2723;
  backdrop-filter: blur(8px);
}
.share-reader-topbar-inner {
  max-width: 1100px;
  margin: 0 auto;
  padding: 12px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.share-reader-brand { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.share-reader-brand-name {
  font-family: 'Playfair Display', Georgia, serif;
  font-size: 16px;
  font-weight: 600;
  color: #fff;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.share-reader-brand-author { color: #b8b3a6; font-size: 12px; }
.share-reader-topbar-actions { display: flex; gap: 12px; align-items: center; }
.share-reader-label {
  font-size: 11.5px;
  color: #b8b3a6;
  border: 1px solid #3a3733;
  padding: 3px 10px;
  border-radius: 999px;
}
.share-reader-toc-toggle {
  display: none;
  background: transparent;
  border: 1px solid #3a3733;
  color: #d4cfc1;
  padding: 6px 10px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 14px;
}

.share-reader-grid {
  flex: 1;
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  max-width: 1100px;
  width: 100%;
  margin: 0 auto;
  padding: 0 24px 48px;
  gap: 32px;
  box-sizing: border-box;
}

.share-reader-toc {
  position: sticky;
  top: 60px;
  align-self: start;
  max-height: calc(100vh - 80px);
  overflow-y: auto;
  padding: 22px 0;
  border-right: 1px solid rgba(0,0,0,.06);
}
.share-reader-toc-title {
  font-family: var(--reader-heading-font, 'Playfair Display', Georgia, serif);
  font-size: 13px;
  font-weight: 600;
  margin: 0 0 14px;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--reader-accent, #8b6914);
}
.share-reader-toc-empty { font-size: 13px; color: #888; font-style: italic; }
.share-reader-toc-list { list-style: none; padding: 0; margin: 0; }
.share-reader-toc-list li { margin: 0; }
.share-reader-toc-item {
  width: 100%;
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 12px 8px 0;
  background: transparent;
  border: 0;
  border-left: 2px solid transparent;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
  font-size: 13.5px;
  line-height: 1.4;
}
.share-reader-toc-item:hover { color: var(--reader-accent, #8b6914); }
.share-reader-toc-item.is-active {
  border-left-color: var(--reader-accent, #8b6914);
  padding-left: 10px;
  color: var(--reader-accent, #8b6914);
  font-weight: 600;
}
/* Per-chapter count of THIS reader's notes. Decorative — fades in once
   the counts API resolves; hidden when the chapter has zero notes. */
.share-reader-toc-badge {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--reader-accent, #8b6914);
  color: #fff;
  font-size: 10px;
  font-weight: 600;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  font-family: inherit;
}
.share-reader-toc-num {
  font-family: var(--reader-heading-font, 'Playfair Display', Georgia, serif);
  color: var(--reader-accent, #8b6914);
  font-size: 12px;
  flex-shrink: 0;
  min-width: 1.6em;
}

.share-reader-main {
  padding: 32px 0 24px;
  display: flex;
  flex-direction: column;
  gap: 20px;
  min-width: 0;
}

/* First-visit orientation card for beta readers — dismissable, shown
   once per share link (localStorage). UI-sans on purpose: it's chrome,
   not part of the manuscript. */
.share-reader-intro {
  background: #faf7ef;
  border: 1px solid #e9e2d0;
  border-left: 3px solid var(--reader-accent, #8b6914);
  border-radius: 8px;
  padding: 20px 24px;
  font-family: var(--font-sans, -apple-system, sans-serif);
  color: #3a3630;
}
/* Whole header doubles as the open/close toggle — the card is
   collapsible (never dismissed) so readers can re-check the
   instructions whenever they forget how commenting works. */
.share-reader-intro-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-align: left;
  color: inherit;
}
.share-reader-intro-title {
  font-family: var(--reader-heading-font, 'Playfair Display', Georgia, serif);
  font-size: 19px;
  font-weight: 600;
  margin: 0;
  color: var(--reader-text, #1a1a1a);
}
.share-reader-intro-chevron {
  font-size: 14px;
  color: var(--reader-accent, #8b6914);
  transition: transform .15s ease;
}
.share-reader-intro-toggle[aria-expanded="false"] .share-reader-intro-chevron {
  transform: rotate(-90deg);
}
.share-reader-intro-body {
  margin-top: 10px;
}
.share-reader-intro-list {
  margin: 0 0 14px;
  padding-left: 20px;
  font-size: 14px;
  line-height: 1.6;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.share-reader-intro-actions {
  display: flex;
  justify-content: flex-end;
}

/* "Powered by …" line under the reader — quiet, but present on every
   share page (beta readers are prospective authors). */
.share-reader-powered {
  text-align: center;
  font-family: var(--font-sans, -apple-system, sans-serif);
  font-size: 12.5px;
  color: #8a857a;
  padding: 8px 0 4px;
}
.share-reader-powered a {
  color: var(--reader-accent, #8b6914);
  text-decoration: none;
  font-weight: 600;
}
.share-reader-powered a:hover { text-decoration: underline; }
.share-reader-page {
  background: var(--reader-bg, #fdfbf6);
  font-family: var(--reader-body-font, 'Lora', Georgia, serif);
  font-size: var(--reader-font-size, 12pt);
  line-height: var(--reader-line-height, 1.65);
  color: var(--reader-text, #1a1a1a);
  padding: 56px 64px;
  border: 1px solid rgba(0,0,0,.06);
  border-radius: 6px;
  box-shadow: 0 2px 24px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);
  min-height: 60vh;
}
.share-reader-page p {
  margin: 0 0 0.7em;
  text-indent: var(--reader-indent, 0.3in);
  text-align: var(--reader-text-align, justify);
  hyphens: auto;
  -webkit-hyphens: auto;
}
.share-reader-page .share-reader-content p:first-child { text-indent: 0; }
/* Per-paragraph alignment from the editor (Quill writes these classes). */
.share-reader-page .ql-align-center  { text-align: center !important; text-indent: 0 !important; }
.share-reader-page .ql-align-right   { text-align: right !important; text-indent: 0 !important; }
.share-reader-page .ql-align-justify { text-align: justify !important; }
/* Theme-level default: indent every chapter's first paragraph. */
.share-reader-page.is-indent-first .share-reader-content p:first-child { text-indent: var(--reader-indent, 0.3in); }
.share-reader-head {
  text-align: center;
  margin: 0 0 1.8em;
  padding-bottom: 1.2em;
  border-bottom: 1px solid rgba(0,0,0,.06);
}
/* Auto-rendered title page + TOC inside the share reader — look more
   like a real book page rather than a stub list. */
.share-reader-page .auto-titlepage { padding: 2em 0 1.4em; }
.share-reader-page .auto-titlepage-title {
  font-family: var(--reader-heading-font, 'Playfair Display', Georgia, serif);
  font-size: 2.2em;
  font-weight: 600;
  margin: 0 0 .35em;
  line-height: 1.15;
  color: var(--reader-text);
}
.share-reader-page .auto-titlepage-subtitle {
  font-style: italic;
  color: var(--reader-accent, #8b6914);
  margin: 0 0 1.6em;
  text-indent: 0;
  text-align: center;
}
.share-reader-page .auto-titlepage-author {
  font-family: var(--reader-heading-font, 'Playfair Display', Georgia, serif);
  letter-spacing: .12em;
  margin: 0 0 2.4em;
  text-indent: 0;
  text-align: center;
}
.share-reader-page .auto-titlepage-publisher-logo {
  margin: 1.6em auto 0.4em;
  max-width: 50%;
}
.share-reader-page .auto-titlepage-publisher-logo img {
  max-width: 100%;
  max-height: 120px;
  height: auto;
  display: block;
  margin: 0 auto;
}
.share-reader-page .auto-titlepage-publisher-name {
  font-family: var(--reader-heading-font, 'Playfair Display', Georgia, serif);
  font-size: .8em;
  letter-spacing: .14em;
  text-transform: uppercase;
  margin: 0;
  text-indent: 0;
  text-align: center;
}
.share-reader-page .auto-toc { list-style: none; margin: 0; padding: 0; }
.share-reader-page .auto-toc-item {
  padding: 10px 0;
  border-bottom: 1px dotted rgba(0,0,0,.18);
  text-indent: 0;
}
.share-reader-page .auto-toc-item:last-child { border-bottom: 0; }
.share-reader-page .auto-toc-item a {
  color: var(--reader-text);
  text-decoration: none;
  display: flex;
  gap: 10px;
  align-items: baseline;
}
.share-reader-page .auto-toc-item a:hover { color: var(--reader-accent, #8b6914); }
.share-reader-page .auto-toc-num {
  color: var(--reader-accent, #8b6914);
  font-family: var(--reader-heading-font, 'Playfair Display', Georgia, serif);
  font-size: .9em;
  min-width: 1.8em;
}
.share-reader-page .auto-toc-front,
.share-reader-page .auto-toc-back  { font-style: italic; }
.share-reader-page .auto-toc-part {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
}
.share-reader-title {
  font-family: var(--reader-heading-font, 'Playfair Display', Georgia, serif);
  font-size: 1.9em;
  font-weight: 600;
  margin: 0;
  color: var(--reader-text, #1a1a1a);
}
.share-reader-subtitle {
  font-family: var(--reader-heading-font, 'Playfair Display', Georgia, serif);
  font-style: italic;
  margin: 0.4em 0 0;
  color: var(--reader-accent, #8b6914);
  text-indent: 0;
}
.share-reader-page h1, .share-reader-page h2, .share-reader-page h3,
.share-reader-page h4, .share-reader-page h5, .share-reader-page h6 {
  font-family: var(--reader-heading-font, 'Playfair Display', Georgia, serif);
}
/* LEGACY <blockquote> — chapters written before text features. The
   border-left was a blog idiom the print PDF never drew, and `blockquote p`
   is what stops the body's first-line indent leaking into every quotation.
   Text features are styled by TextFeatureStyle::forBrowser(), emitted into
   this page's own <style> block. */
.share-reader-page blockquote { margin: 1em 1.4em; }
.share-reader-page blockquote p { margin: 0; text-indent: 0; }
.share-reader-page img { max-width: 100%; height: auto; display: block; margin: 0.6em auto; }
.share-reader-page hr {
  border: 0;
  text-align: center;
  margin: 1.6em 0;
  color: var(--reader-accent, #8b6914);
}
.share-reader-page hr::before { content: '*  *  *'; letter-spacing: .25em; }
.share-reader-loading {
  text-align: center;
  padding: 60px 0;
  color: #999;
  font-style: italic;
}
.share-reader-page .placeholder-line { color: #aaa; font-style: italic; text-align: center; }

.share-reader-nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 16px 0 8px;
  border-top: 1px solid rgba(0,0,0,.08);
}

/* ---------- Beta-reader notes panel (2.91) ----------
   The composer + the reader's own notes, DOCKED beside the chapter so a
   reader can look at the passage while writing about it. Three postures
   by width: a sticky third grid column (>1100px), a right-hand drawer
   (801–1100px), a bottom sheet (≤800px). Open/closed is the `is-open`
   class, set by share_reader.js; the breakpoints decide what "open"
   looks like. */
.share-reader-notes-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  border: 1px solid #3a3733;
  color: #d4cfc1;
  padding: 6px 12px;
  border-radius: 999px;
  cursor: pointer;
  font: inherit;
  font-size: 13px;
}
.share-reader-notes-toggle:hover,
.share-reader-notes-toggle[aria-expanded="true"] { color: #fff; border-color: var(--reader-accent, #8b6914); }
.share-reader-notes-collapse {
  background: transparent;
  border: 0;
  color: rgba(0,0,0,.45);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
  margin-left: auto;
}
.share-reader-notes-collapse:hover { color: var(--reader-text, #1a1a1a); }
.share-comments-head-row { display: flex; align-items: center; gap: 8px; }
.share-comments-list-title {
  font-family: var(--reader-heading-font, 'Playfair Display', Georgia, serif);
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--reader-accent, #8b6914);
  margin: 22px 0 10px;
}
/* Docked (wide): a third column, sticky like the TOC. */
.share-reader-notes {
  position: sticky;
  top: 60px;
  align-self: start;
  max-height: calc(100vh - 80px);
  overflow-y: auto;
  padding: 22px 0 22px 4px;
  display: none;
}
.share-reader-notes.is-open { display: block; }
@media (min-width: 1101px) {
  /* The TOC column is 220px whether or not the panel is open, so
     collapsing the panel never jolts the chapter column. */
  .share-reader-grid.has-notes { max-width: 1400px; grid-template-columns: 220px minmax(0, 1fr); }
  body.share-notes-open .share-reader-grid.has-notes { grid-template-columns: 220px minmax(0, 1fr) 320px; }
}
/* Drawer (medium): slides in from the right over the page. */
@media (max-width: 1100px) {
  .share-reader-notes {
    position: fixed;
    top: 56px; right: 0; bottom: 0;
    width: min(380px, 92vw);
    z-index: 35;
    background: var(--reader-bg, #fdfbf6);
    border-left: 1px solid rgba(0,0,0,.1);
    box-shadow: -12px 0 32px rgba(0,0,0,.14);
    padding: 20px 20px 28px;
    max-height: none;
    display: block;
    transform: translateX(105%);
    transition: transform .22s ease;
  }
  .share-reader-notes.is-open { transform: translateX(0); }
}
/* Sheet (narrow): rises from the bottom, so the text stays above it. */
@media (max-width: 800px) {
  .share-reader-notes {
    top: auto; left: 0; right: 0; bottom: 0;
    width: auto;
    max-height: 70vh;
    border-left: 0;
    border-top: 1px solid rgba(0,0,0,.1);
    border-radius: 14px 14px 0 0;
    box-shadow: 0 -12px 32px rgba(0,0,0,.14);
    transform: translateY(105%);
  }
  .share-reader-notes.is-open { transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .share-reader-notes { transition: none; }
}
.share-comments {
  padding: 0;
  font-family: var(--reader-body-font, Georgia, serif);
}
.share-comments-head { margin-bottom: 14px; }
.share-comments-title {
  font-family: var(--reader-heading-font, 'Playfair Display', Georgia, serif);
  font-size: 18px;
  margin: 0 0 6px;
  color: var(--reader-text, #1a1a1a);
}
.share-comments-help {
  font-size: 13px;
  color: rgba(0,0,0,.55);
  margin: 0;
  line-height: 1.45;
}
.share-comments-list {
  list-style: none;
  margin: 0 0 22px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.share-comments-empty {
  font-size: 13px;
  color: rgba(0,0,0,.45);
  font-style: italic;
  padding: 8px 0;
}
.share-comment {
  background: rgba(255,255,255,.6);
  padding: 12px 14px;
  border-radius: 6px;
  border: 1px solid rgba(0,0,0,.06);
}
.share-comment-meta {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 6px;
}
.share-comment-author {
  font-weight: 600;
  font-size: 13px;
  color: var(--reader-text, #1a1a1a);
}
.share-comment-time {
  font-size: 11px;
  color: rgba(0,0,0,.45);
  letter-spacing: .04em;
}
.share-comment-body {
  font-size: 14px;
  line-height: 1.5;
  color: var(--reader-text, #1a1a1a);
  white-space: pre-wrap;
  word-break: break-word;
}

/* Acknowledgement badges on the reader side. The author can mark a
   comment resolved or post a short reply; both surface here so the
   reader can see what the author has done with their feedback. */
.share-comment-resolved {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  color: #2f6633;
  background: #d8ebd8;
  padding: 1px 7px;
  border-radius: 999px;
  border: 1px solid #b8d8b8;
}
.share-comment.is-resolved {
  background: rgba(255,255,255,.4);
}
.share-comment.is-resolved .share-comment-body {
  color: rgba(0,0,0,.55);
}
/* The author's reply is the OTHER voice in the thread, so it gets the
   other bubble (2.91): the book's accent as a solid header strip with
   white lettering, the body on a soft tint of the same accent, offset
   to the right like a received message. It read as a footnote in grey
   before and was easy to skim past. */
.share-comment-reply {
  margin: 10px 0 0 14px;
  background: color-mix(in srgb, var(--reader-accent, #8b6914) 12%, #fff);
  border: 1px solid color-mix(in srgb, var(--reader-accent, #8b6914) 35%, #fff);
  border-radius: 10px 10px 10px 2px;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0,0,0,.06);
}
.share-comment-reply-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 6px 10px;
  background: var(--reader-accent, #8b6914);
  color: #fff;
}
.share-comment-reply-meta .share-comment-time { color: rgba(255,255,255,.8); }
.share-comment-reply-from {
  font-weight: 700;
  font-size: 11px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: #fff;
}
.share-comment-reply-from::before { content: "✎ "; }
.share-comment-reply-body {
  padding: 9px 10px 10px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--reader-text, #1a1a1a);
  white-space: pre-wrap;
  word-break: break-word;
}
.share-comments-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.share-comments-name {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
  color: rgba(0,0,0,.6);
}
.share-comments-name-label {
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  font-size: 11px;
}
.share-comments-form input[type="text"],
.share-comments-form textarea {
  font-family: inherit;
  font-size: 14px;
  padding: 9px 12px;
  border: 1px solid rgba(0,0,0,.18);
  border-radius: 6px;
  background: #fff;
  color: var(--reader-text, #1a1a1a);
  width: 100%;
  box-sizing: border-box;
}
.share-comments-form textarea {
  resize: vertical;
  min-height: 70px;
  line-height: 1.45;
}
.share-comments-form input[type="text"]:focus,
.share-comments-form textarea:focus {
  outline: none;
  border-color: var(--reader-accent, #8b6914);
  box-shadow: 0 0 0 3px rgba(139,105,20,.15);
}
.share-comments-form-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.share-comments-error {
  font-size: 12px;
  color: #b03030;
}
.share-comments-disabled {
  font-size: 13px;
  color: rgba(0,0,0,.55);
  font-style: italic;
  margin: 24px 0 0;
  padding: 12px 0 0;
  border-top: 1px dashed rgba(0,0,0,.1);
  font-family: var(--reader-body-font, Georgia, serif);
}

/* Floating "Comment on selection" button. position: absolute against the
   document — JS sets top/left to hover above the user's text selection. */
.share-comment-floater {
  position: absolute;
  z-index: 50;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  background: var(--reader-text, #1a1a1a);
  color: #fff;
  border: 0;
  border-radius: 999px;
  font-family: var(--reader-body-font, Georgia, serif);
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  box-shadow: 0 6px 18px rgba(0,0,0,.18);
}
.share-comment-floater:hover { background: var(--reader-accent, #8b6914); }

/* Staged-quote preview shown above the form once a selection has been
   adopted. The blockquote keeps the reader oriented while they type. */
.share-comments-staged {
  background: rgba(255,235,150,.35);
  border-left: 3px solid var(--reader-accent, #8b6914);
  padding: 10px 14px;
  border-radius: 4px;
  font-size: 13px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.share-comments-staged-label {
  font-weight: 600;
  font-size: 11px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: rgba(0,0,0,.6);
}
.share-comments-staged-quote {
  margin: 0;
  font-style: italic;
  color: var(--reader-text, #1a1a1a);
  line-height: 1.45;
  max-height: 6.5em;
  overflow: hidden;
}
.share-comments-staged-clear {
  align-self: flex-start;
  font-size: 12px;
}

/* Per-comment quoted snippet (shown alongside the body in the reader's
   own list of posted notes). Visually parallel to .share-comments-staged. */
.share-comment-quote {
  margin: 0 0 6px;
  padding: 6px 10px;
  background: rgba(0,0,0,.03);
  border-left: 2px solid var(--reader-accent, #8b6914);
  font-style: italic;
  font-size: 13px;
  color: rgba(0,0,0,.65);
  line-height: 1.4;
}
.share-reader-pos {
  font-size: 12px;
  color: #888;
  font-family: var(--reader-heading-font, 'Playfair Display', Georgia, serif);
  letter-spacing: .08em;
  text-transform: uppercase;
}

@media (max-width: 800px) {
  .share-reader-toc-toggle { display: inline-flex; }
  .share-reader-grid {
    grid-template-columns: 1fr;
    padding: 0 18px 40px;
    gap: 0;
  }
  .share-reader-toc {
    position: fixed;
    top: 56px; left: 0; right: 0; bottom: 0;
    z-index: 30;
    background: var(--reader-bg, #fdfbf6);
    padding: 20px 24px;
    border-right: 0;
    border-top: 1px solid rgba(0,0,0,.08);
    transform: translateY(-110%);
    transition: transform .22s ease;
    max-height: none;
  }
  .share-reader-toc.is-open { transform: translateY(0); }
  .share-reader-main { padding-top: 24px; }
  .share-reader-page { padding: 32px 22px; }
}

/* ============================================================
   PHASE D — Admin area
   ============================================================ */
.admin-body { background: var(--bg-page); min-height: 100vh; }
.admin-shell {
  min-height: 100vh;
  display: grid;
  grid-template-columns: 256px 1fr;
}

/* ----- Topbar Admin pill (visible across editor/library/details) ----- */
.topbar-admin-link {
  display: inline-flex;
  align-items: center;
  padding: 5px 12px;
  border: 1px solid #3a3733;
  color: #c8c3b6;
  font-size: 12.5px;
  border-radius: 999px;
  text-decoration: none;
  letter-spacing: .04em;
  transition: background .15s ease, color .15s ease;
}
.topbar-admin-link:hover { background: var(--bg-topbar-soft); color: #fff; }
.topbar .topbar-admin-link {
  border-color: #3a3733;
  color: #c8c3b6;
}

/* ----- "Upgrade" CTA (free-tier users only, see partials/upgrade_button.php) -----
   Solid accent fill so it visibly outranks the plain outlined pills
   (Support/Settings/Admin) sitting next to it — this is the one thing in
   the topbar we actually want the user's eye to land on. */
.topbar-upgrade-btn {
  display: inline-flex;
  align-items: center;
  padding: 6px 14px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: .02em;
  text-decoration: none;
  white-space: nowrap;
  transition: background .15s ease;
}
.topbar-upgrade-btn:hover { background: var(--accent-soft); color: #fff; }

/* "?" keyboard-help button. Lives in the topbar-right next to
   Settings / Admin. Same dark-bar palette as the admin link, but
   round so it reads as a glyph rather than text. */
.topbar-help-btn {
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid #3a3733;
  background: transparent;
  color: #c8c3b6;
  font-size: 13px;
  font-weight: 600;
  border-radius: 50%;
  cursor: pointer;
  padding: 0;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.topbar-help-btn:hover { background: var(--bg-topbar-soft); color: #fff; }
.topbar-help-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px rgba(200,195,182,.45);
}

/* ----- Sidebar nav ----- */
.admin-nav {
  background: #1d1b18;
  color: #c8c3b6;
  display: flex;
  flex-direction: column;
  padding: 18px 0 14px;
  /* The nav is taller than most pages now (6 groups); pin it so the
     rail scrolls independently instead of scrolling away with long
     tables. Reset to static in the 900px mobile block. */
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  align-self: start;
  scrollbar-width: thin;
  scrollbar-color: #3a3733 transparent;
}
.admin-nav-head {
  padding: 0 18px 18px;
  border-bottom: 1px solid #2e2a26;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
/* Logo on the left, the dark/light switch on the right of the same line. */
.admin-nav-head-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-width: 0;
}
.admin-nav-head-row .admin-logo { min-width: 0; }
.admin-theme-toggle { flex: 0 0 auto; }
.admin-logo {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: #fff;
  text-decoration: none;
}
.admin-logo .logo-mark {
  width: 26px; height: 26px;
  background: var(--accent);
  border-radius: 6px;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 700;
  color: #fff;
}

/* ----- Admin › Branding: brand-logo manager (preview + upload) ----- */
.admin-logo-manage {
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
}
.admin-logo-preview {
  flex: 0 0 auto;
  width: 72px; height: 72px;
  border-radius: 10px;
  background: var(--bg-topbar);
  border: 1px solid var(--line, #ddd);
  display: inline-flex; align-items: center; justify-content: center;
  padding: 10px;
}
.admin-logo-preview img { max-width: 100%; max-height: 100%; object-fit: contain; }
.admin-logo-preview-mark {
  width: 40px; height: 40px;
  border-radius: 8px;
  background: var(--accent);
  color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-display);
  font-size: 22px; font-weight: 700;
}
.admin-logo-forms {
  display: flex;
  align-items: flex-end;
  gap: 16px;
  flex-wrap: wrap;
}
.admin-logo-label {
  font-family: var(--font-display);
  font-size: 18px;
  letter-spacing: .02em;
}
.admin-section {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--accent);
  margin-left: 36px;
}
.admin-nav-list {
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  flex: 1;
}
.admin-nav-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 18px;
  color: #c8c3b6;
  text-decoration: none;
  font-size: 13.5px;
}
.admin-nav-link:hover { color: #fff; background: rgba(255,255,255,.04); }
.admin-nav-link:hover .admin-nav-icon { opacity: 1; }
.admin-nav-link.is-active {
  background: #2a2723;
  color: #fff;
  font-weight: 500;
  box-shadow: inset 2px 0 0 var(--accent);
}
.admin-nav-link.is-active .admin-nav-icon { opacity: 1; color: var(--accent); }
.admin-nav-icon {
  flex-shrink: 0;
  display: inline-flex;
  opacity: .65;
  transition: opacity .12s ease;
}
.admin-nav-icon svg { width: 16px; height: 16px; display: block; }
.admin-nav-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.admin-nav-link-muted { color: #8a857a; font-size: 12.5px; }

/* "Jump to…" quick filter between the head and the group list.
   Pure client-side: typing hides non-matching links (see the inline
   script in admin_nav.php); it never touches collapse state. */
.admin-nav-filter { padding: 12px 18px 0; }
.admin-nav-filter input {
  width: 100%;
  background: #26231f;
  border: 1px solid #35312b;
  color: #e8e4da;
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 12.5px;
  padding: 6px 10px;
}
.admin-nav-filter input::placeholder { color: #6c655a; }
.admin-nav-filter input:focus {
  outline: none;
  border-color: #55503f;
}
/* While filtering: collapse is overridden so matches in collapsed
   groups still show; groups with zero matches disappear entirely. */
.admin-nav-list.is-filtering .admin-nav-group-items { max-height: none !important; }
.admin-nav-section.is-empty { display: none; }
.admin-nav-foot {
  padding: 14px 18px 0;
  border-top: 1px solid #2e2a26;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.admin-nav-user {
  font-size: 11px;
  color: #6a665e;
  letter-spacing: .04em;
  padding: 0 0 0 6px;
}
/* Sign out under the logo in the admin rail header — a real button
   (logout is a CSRF-protected POST). Indented to line up under the
   wordmark, matching the "Admin" label's alignment. */
.admin-nav-head-signout-form { margin: 8px 0 0 36px; }
.admin-nav-signout-btn {
  cursor: pointer;
  font-family: inherit;
  font-size: 12px;
  font-weight: 500;
  color: #d8d3c7;
  background: #2e2a26;
  border: 1px solid #423d37;
  border-radius: 6px;
  padding: 5px 14px;
  transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.admin-nav-signout-btn:hover {
  background: #3a352f;
  border-color: #55504a;
  color: #fff;
}

/* ----- Main pane ----- */
.admin-main {
  padding: 32px 40px 80px;
  max-width: 1100px;
  width: 100%;
}
/* Wide-table pages (e.g. the Users list) need more than the standard
   1100px reading-width cap so columns don't fight each other for
   space and force a horizontal scrollbar. */
.admin-main.admin-main-wide {
  max-width: 1500px;
}
.admin-page-head {
  margin-bottom: 24px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--line-soft);
}
/* Title/sub on the left, page-level action buttons on the right.
   Emitted by partials/admin_page_start.php — the one shared header. */
.admin-page-head-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  flex-wrap: wrap;
}
.admin-page-head-main { min-width: 0; }
.admin-page-actions {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-shrink: 0;
  /* Optical alignment with the Playfair title's cap height. */
  margin-top: 4px;
}
.admin-page-backlink {
  display: inline-block;
  font-size: 12.5px;
  color: var(--ink-3);
  text-decoration: none;
  margin-bottom: 6px;
}
.admin-page-backlink:hover { color: var(--ink-1); }
.admin-page-title {
  font-family: var(--font-display);
  font-size: 28px;
  font-weight: 600;
  margin: 0 0 6px;
  letter-spacing: .01em;
  color: var(--ink-1);
}
.admin-page-sub {
  margin: 0;
  color: var(--ink-3);
  font-size: 14px;
  max-width: 720px;
  line-height: 1.55;
}

/* ----- The one count-badge component -----
   Replaces both .admin-nav-badge (sidebar/dashboard) and the
   .admin-tier-chip count usage on tab rails. -accent = "needs you"
   (gold), -muted = neutral count. */
.admin-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 18px;
  padding: 0 6px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
}
.admin-badge-accent { background: var(--accent); color: #fff; }
.admin-badge-muted  { background: var(--bg-rail); color: var(--ink-3); }
.admin-nav-link .admin-badge { margin-left: auto; }
.admin-tab-rail-link .admin-badge { margin-left: 6px; }

.admin-alert {
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  font-size: 13px;
  margin-bottom: 18px;
}
.admin-alert-ok    { background: #ecf5e3; color: #2f5a1e; border-left: 3px solid var(--success); }
.admin-alert-error { background: #fdf0ed; color: #8a2c1d; border-left: 3px solid var(--danger); }

.admin-card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px 22px 22px;
  margin-bottom: 22px;
}
.admin-card-title {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0 0 16px;
}

.admin-form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px 18px;
  align-items: end;
}
.admin-form-grid > .field-toggle {
  grid-column: 1 / -1;
}
.admin-form-actions {
  grid-column: 1 / -1;
  display: flex;
  justify-content: flex-end;
}
.admin-form-inline {
  margin-top: 10px;
  padding: 14px;
  background: var(--bg-paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* ----- Stat grid (dashboard) ----- */
.admin-stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 16px;
}
.admin-stat {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px 20px 16px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.admin-stat-wide { grid-column: 1 / -1; }
.admin-stat-num {
  font-family: var(--font-display);
  font-size: 32px;
  font-weight: 600;
  color: var(--accent);
  line-height: 1;
}
.admin-stat-label {
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-1);
  margin-top: 4px;
}
.admin-stat-meta {
  font-size: 12px;
  color: var(--ink-3);
}

/* ----- Dashboard launchpad ----- */
/* "Needs attention" card gets a gold left edge only while there is
   actually something to do — all-clear renders as a plain card. */
.admin-attention-card.has-items { border-left: 3px solid var(--accent); }
.admin-overview-priorities { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin-bottom: 20px; }
.admin-overview-priorities > .admin-card { margin: 0; min-width: 0; }
.admin-overview-priorities .admin-form-inline { padding: 0; border: 0; background: transparent; }
.admin-overview-priorities .admin-attention-row { flex-wrap: wrap; }
@media (max-width: 900px) { .admin-overview-priorities { grid-template-columns: 1fr; } }
.admin-attention-list { list-style: none; margin: 0; padding: 0; }
.admin-attention-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line-soft);
}
.admin-attention-row:last-child { border-bottom: none; }
.admin-attention-row:first-child { padding-top: 0; }
.admin-attention-label {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 14px;
  color: var(--ink-1);
}
.admin-attention-clear {
  margin: 0;
  font-size: 14px;
  color: var(--ink-3);
}
.admin-quick-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.admin-quick-actions .btn svg {
  width: 15px;
  height: 15px;
  display: block;
  opacity: .7;
}
.admin-recent-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  align-items: start;
}
.admin-recent-grid .admin-card { margin-bottom: 0; }
.admin-recent-list { list-style: none; margin: 0; padding: 0; }
.admin-recent-list li {
  padding: 9px 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: 13.5px;
}
.admin-recent-list li:last-child { border-bottom: none; }
.admin-recent-list li:first-child { padding-top: 0; }
.admin-recent-meta {
  display: block;
  font-size: 12px;
  color: var(--ink-3);
  margin-top: 2px;
}

/* ----- Tables ----- */
.admin-table-wrap {
  /* Belt-and-suspenders: if a row's content ever forces the table wider
     than the card (e.g. a long email), scroll horizontally inside the
     card instead of spilling out past its border. */
  overflow-x: auto;
}
.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13.5px;
}
.admin-table th,
.admin-table td {
  text-align: left;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: top;
}
.admin-table th {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-3);
}
.admin-table tr:last-child td { border-bottom: none; }

/* ----- Drag-reorder rows (v2.42, Auto-poster topic queue) -----
   Mouse-only grip, mirroring .book-drag-handle on /library. The keyboard
   path stays the ↑/↓ buttons beside it, so unlike the library card this
   handle has no keyboard duty at all (tabindex=-1 + aria-hidden). */
.admin-drag-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  vertical-align: middle;
  background: none;
  border: 1px solid transparent;
  border-radius: 6px;
  color: var(--ink-3);
  cursor: grab;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.admin-drag-handle:hover {
  background: var(--bg-page);
  border-color: var(--line);
  color: var(--ink-1);
}
.admin-drag-handle:active { cursor: grabbing; }
.admin-table tr.is-dragging { opacity: .5; }
/* Insertion line, the list-row idiom (.chapter-item.drag-over) rather than
   the dashed outline used for card grids. INSET, and on the cells rather
   than the <tr>: a table row is not a normal box in several engines and an
   outer box-shadow on it gets clipped away. */
.admin-table tr.drag-over > td { box-shadow: inset 0 2px 0 var(--accent); }
/* Auto-poster queue (2.93): a row whose order is saving, the Next
   button, the stale chip, and the bulk bar over the table. */
.admin-table tr.is-busy > td { opacity: .55; }
.admin-topic-next { font-weight: 600; }
/* Collapsible admin cards (2.97): <details> wearing the card styles. */
.admin-card-fold > summary { list-style: none; cursor: pointer; display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.admin-card-fold > summary::-webkit-details-marker { display: none; }
.admin-card-fold > summary::before { content: '▸'; font-size: 12px; color: var(--ink-3); transition: transform .12s ease; }
.admin-card-fold[open] > summary::before { transform: rotate(90deg); }
.admin-card-fold-help { font-weight: 400; font-size: 12.5px; margin-left: auto; }
.admin-card-fold > .admin-table-wrap { margin-top: 12px; }
.admin-used-toggle { margin-top: 10px; }
.admin-topic-check { display: block; margin-top: 4px; padding-left: 8px; border-left: 3px solid var(--line); }
.admin-topic-check-keep    { border-left-color: #3a8f5c; }
.admin-topic-check-rewrite { border-left-color: #d3a21a; }
.admin-topic-check-drop    { border-left-color: var(--danger); }
.admin-badge-warn { background: #f4d98a; color: #5a4300; margin-left: 6px; cursor: help; }
:root[data-theme="dark"] .admin-badge-warn { background: #6a5313; color: #f7e7b3; }
.admin-inline-check {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--ink-2);
  white-space: nowrap;
}
.admin-bulk-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 12px;
  margin-top: 12px;
  padding: 8px 12px;
  background: var(--accent-soft);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
}
.admin-bulk-bar[hidden] { display: none; }
.admin-bulk-count { font-weight: 600; font-size: 13px; }
.admin-bulk-group { display: inline-flex; gap: 2px 10px; flex-wrap: wrap; }
.admin-bulk-spacer { flex: 1; }
.admin-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding-top: 16px;
  margin-top: 4px;
}
.admin-pagination-label { font-size: 13px; color: var(--ink-3); }
.admin-pagination .is-disabled {
  opacity: .4;
  pointer-events: none;
}
.admin-table-actions {
  text-align: right;
  white-space: nowrap;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 8px;
  justify-content: flex-end;
  align-items: center;
}
/* Utility actions (resend verification / email) tucked inside the
   expanded Edit panel, below Save — keeps the collapsed row down to
   just "Edit" + "Delete" so the actions column stays narrow. */
.admin-row-edit-secondary {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px dashed var(--line-soft);
}
.admin-pill {
  display: inline-block;
  padding: 2px 8px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  border-radius: 999px;
  background: var(--bg-rail);
  color: var(--ink-3);
  margin-left: 4px;
}
.admin-pill-on  { background: var(--accent-bg); color: var(--accent); }
.admin-pill-off { background: var(--bg-rail);   color: var(--ink-3);  }

/* Extra pill tones for the admin tables (v2.53). Tints are color-mix()ed
   off the semantic vars rather than hard-coded, so they follow the dark
   theme with no second definition — the same move .kdp-* chips make.
   -warn is deliberately the danger hue at low strength: these mark a
   pending deletion, which is a caution, not an error. */
.admin-pill-quiet { background: var(--bg-rail); color: var(--ink-3); }
.admin-pill-count {
  background: var(--accent-bg);
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.admin-pill-good {
  background: color-mix(in srgb, var(--success) 16%, transparent);
  color: var(--success);
}
.admin-pill-warn {
  background: color-mix(in srgb, var(--danger) 13%, transparent);
  color: var(--danger);
}
/* For pills whose content is a date or a phrase ("last 1 Aug", "17d old",
   "2 archived") rather than a status word. The base pill's uppercase +
   wide tracking is right for FULL / ADMIN / YOU and shouts on anything
   longer. */
.admin-pill-date {
  text-transform: none;
  letter-spacing: .01em;
  font-weight: 500;
  font-size: 11px;
}
/* The base pill carries a left margin for sitting AFTER text (the "you"
   chip). A pill that opens its own cell shouldn't inherit that nudge, or
   the column reads as misaligned against its header. */
.admin-table td > .admin-pill:first-child { margin-left: 0; }

.admin-row-edit summary {
  list-style: none;
  cursor: pointer;
  padding: 4px 0;
}
.admin-row-edit summary::-webkit-details-marker { display: none; }
.admin-row-edit[open] summary::after { content: ' (close)'; color: var(--ink-4); font-size: 11px; }
.admin-row-edit:not([open]) summary::after { content: ' ›'; color: var(--ink-4); }

/* Expanded Edit panel as a floating card (admin/users). Rendered inline
   it widens the actions cell past the card, and .admin-table-wrap's
   overflow-x then clips it half-hidden. Positioning it absolutely against
   the card (.admin-card-popover-host) escapes the scroller's clipping —
   the wrap isn't in the panel's containing-block chain — so the table
   keeps its width and the form floats above the rows beneath. No `top`:
   the static position keeps it anchored just under its row's Edit link. */
.admin-card-popover-host { position: relative; }
.admin-row-edit-panel {
  position: absolute;
  right: 22px;
  z-index: 30;
  width: min(380px, calc(100% - 44px));
  margin-top: 6px;
  padding: 14px 16px 16px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 14px 36px rgba(31, 27, 20, .18);
  text-align: left;
  white-space: normal;
}
/* Corner × for the floating panel — same look as .modal-close, slightly
   smaller. The summary's "Edit (close)" still toggles too, but the panel
   floats away from its row so it needs its own visible way out. */
.admin-row-edit-close {
  position: absolute;
  top: 6px; right: 8px;
  width: 28px; height: 28px;
  border: none; background: transparent;
  font-size: 19px; line-height: 1;
  color: var(--ink-3); cursor: pointer; padding: 0;
  border-radius: 50%;
  z-index: 1;
}
.admin-row-edit-close:hover { background: var(--bg-rail); color: var(--ink-1); }

/* The inline-form chrome (paper box) reads as a box-in-a-box inside the
   floating card — strip it there. */
.admin-row-edit-panel .admin-form-inline {
  margin-top: 0;
  padding: 0;
  background: transparent;
  border: none;
}

.admin-danger { color: var(--danger) !important; }
.admin-danger:hover { color: #7f2618 !important; }

.admin-empty {
  color: var(--ink-3);
  font-size: 14px;
  font-style: italic;
}

/* ---------- Preset list (admin/presets) ---------- */
.preset-list { list-style: none; margin: 0; padding: 0; }
.preset-row {
  border-bottom: 1px solid var(--line-soft);
}
.preset-row:last-child { border-bottom: none; }
.preset-details summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 12px 4px;
}
.preset-details summary::-webkit-details-marker { display: none; }
.preset-details[open] summary { border-bottom: 1px dashed var(--line-soft); }
.preset-summary-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.preset-summary-label { color: var(--ink-1); font-weight: 500; }
.preset-summary-desc {
  font-size: 12px;
  color: var(--ink-3);
}
.preset-summary-meta { display: flex; gap: 4px; flex-shrink: 0; }
.preset-edit-form { padding: 14px 4px 0; }
.preset-edit-form textarea.admin-html {
  width: 100%;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
  background: var(--bg-paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  resize: vertical;
}
.preset-edit-form textarea.admin-html:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(139,105,20,.12);
  background: #fff;
}
.preset-edit-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 6px;
  padding: 0 4px 14px;
}

/* The same admin-html textarea on the create form too */
.admin-card .admin-html {
  width: 100%;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
  background: var(--bg-paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  resize: vertical;
}
.admin-card .admin-html:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(139,105,20,.12);
  background: #fff;
}

/* Disabled buttons in the admin must LOOK disabled — the app has no
   global .btn:disabled on purpose (app-side disabled buttons get
   bespoke treatment), but in admin a full-strength disabled button
   reads as clickable and fails silently (the Auto-poster scan button
   taught us that). */
.admin-main .btn:disabled {
  opacity: .45;
  cursor: not-allowed;
}

/* ------------------------------------------------------------
   Admin styles consolidated here from marketing.css (2026-07 admin
   redesign) so the whole admin surface lives in one place and shares
   ONE responsive breakpoint (900px). marketing.css keeps only truly
   public-facing rules.
   ------------------------------------------------------------ */

/* Horizontal underline tabs (support queue filter, etc.) */
.admin-tab-strip {
  display: flex;
  gap: 4px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--line-soft);
}
.admin-tab-link {
  padding: 10px 18px;
  color: var(--ink-3);
  text-decoration: none;
  font-size: 14px;
  font-weight: 500;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.admin-tab-link:hover { color: var(--ink-1); }
.admin-tab-link.is-active {
  color: var(--ink-1);
  border-bottom-color: var(--accent);
}
.admin-tab-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
}

.admin-row-unread { background: rgba(139, 105, 20, 0.04); }
.admin-row-unread strong { color: var(--ink-1); font-weight: 600; }
.admin-unread-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
}
.admin-row-sub {
  display: block;
  font-size: 12px;
  color: var(--ink-3);
  margin-top: 2px;
}
.admin-link { color: var(--ink-1); text-decoration: none; }
.admin-link:hover { color: var(--accent); }
/* Legacy back-link (pre-redesign pages); superseded by
   .admin-page-backlink in the shared header partial. */
.admin-back-link {
  display: inline-block;
  margin-bottom: 8px;
  color: var(--ink-3);
  text-decoration: none;
  font-size: 13px;
}
.admin-back-link:hover { color: var(--ink-1); }

.admin-message-body {
  font-family: var(--font-serif);
  font-size: 15.5px;
  line-height: 1.65;
  color: var(--ink-1);
  background: var(--bg-page);
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  padding: 24px;
  margin: 18px 0;
  white-space: pre-wrap;
  word-wrap: break-word;
}
.admin-card-foot {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding-top: 18px;
  border-top: 1px solid var(--line-soft);
}
.admin-message-meta {
  display: flex;
  gap: 24px;
  font-size: 12px;
  color: var(--ink-3);
  flex-wrap: wrap;
}
.admin-message-meta code {
  background: var(--bg-rail);
  padding: 1px 6px;
  border-radius: 3px;
  font-size: 11.5px;
}

.admin-h3 {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-2);
  margin: 24px 0 10px;
}
.admin-h3:first-child { margin-top: 0; }
.admin-help-strong {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink-2);
  margin: 12px 0 6px;
}
.admin-help {
  font-size: 12.5px;
  color: var(--ink-3);
  margin: 6px 0 0;
}

.admin-code-block {
  background: #1a1815;
  color: #ebe6d8;
  padding: 12px 14px;
  border-radius: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 12px;
  line-height: 1.5;
  margin: 0 0 14px;
  overflow-x: auto;
  white-space: pre;
  word-wrap: normal;
}

.admin-cron-wire {
  padding: 18px;
  background: var(--bg-page);
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  margin: 18px 0;
}
.admin-cron-url-row {
  display: flex;
  gap: 8px;
  align-items: center;
  margin: 6px 0 4px;
}
.admin-cron-url {
  flex: 1;
  padding: 8px 10px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 12px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: 6px;
  color: var(--ink-1);
}
.admin-cron-status {
  padding: 18px;
  background: var(--bg-page);
  border: 1px solid var(--line-soft);
  border-radius: 8px;
}
.admin-cron-status-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}
.admin-cron-status-list li {
  display: grid;
  grid-template-columns: 110px 1fr;
  align-items: baseline;
  gap: 12px;
  font-size: 13.5px;
  color: var(--ink-1);
}
.admin-cron-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-3);
}
.admin-cron-error {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 12px;
  color: var(--danger);
  word-break: break-word;
}

/* Compact admin card variant (Email/SMTP, test-send). */
.admin-card-tight { padding-top: 18px; padding-bottom: 18px; }

/* Logs viewer (Site settings → Logs) */
.admin-log-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  font-size: 12.5px;
  color: var(--ink-3);
  padding: 10px 14px;
  background: var(--bg-rail);
  border: 1px solid var(--line-soft);
  border-radius: 6px;
  margin: 0 0 14px;
}
.admin-log-meta code {
  background: var(--bg-card);
  padding: 1px 6px;
  border-radius: 3px;
  font-size: 11.5px;
}
.admin-log-meta strong { color: var(--ink-1); font-weight: 600; }
.admin-log-truncated {
  background: #fbf1cf;
  color: #6b5210;
  border: 1px solid #efdf95;
  padding: 8px 12px;
  border-radius: 6px;
  font-size: 12.5px;
  margin-bottom: 10px;
}
.admin-log {
  background: #1a1815;
  color: #ebe6d8;
  padding: 16px 18px;
  border-radius: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 12px;
  line-height: 1.6;
  margin: 0;
  max-height: 70vh;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
}
:root[data-theme="dark"] .admin-log { background: #0e0d0b; }

/* Settings-page vertical tab rail (plain ?tab= anchors, no JS). */
.admin-tabs {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 24px;
  align-items: flex-start;
}
.admin-tabs-rail {
  display: flex;
  flex-direction: column;
  gap: 2px;
  position: sticky;
  top: 24px;
}
.admin-tab-rail-link {
  display: block;
  padding: 10px 14px;
  border-radius: 6px;
  color: var(--ink-2);
  text-decoration: none;
  font-size: 14px;
  font-weight: 500;
  background: transparent;
  border: 1px solid transparent;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.admin-tab-rail-link:hover {
  background: var(--bg-card);
  color: var(--ink-1);
}
.admin-tab-rail-link.is-active {
  background: var(--accent);
  color: #ffffff;
  border-color: var(--accent);
}
.admin-tabs-pane { min-width: 0; }

/* Collapsible sidebar groups */
.admin-nav-section { margin-top: 2px; }
.admin-nav-section:first-child { margin-top: 0; }
.admin-nav-group-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  background: transparent;
  border: none;
  padding: 14px 18px 6px;
  font-size: 10.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: #6c655a;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  transition: color .12s ease;
}
.admin-nav-section:first-child .admin-nav-group-header { padding-top: 12px; }
.admin-nav-group-header:hover { color: #8a857a; }
.admin-nav-chevron {
  font-size: 11px;
  color: #6c655a;
  transition: transform .15s ease;
}
.admin-nav-section.is-collapsed .admin-nav-chevron { transform: rotate(-90deg); }
.admin-nav-group-items {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 600px;
  overflow: hidden;
  transition: max-height .2s ease;
}
.admin-nav-section.is-collapsed .admin-nav-group-items { max-height: 0; }

/* Legacy sidebar count badge — superseded by .admin-badge; kept only
   until every view stops referencing it (cleanup pass). */
.admin-nav-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 18px;
  padding: 0 6px;
  margin-left: 8px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
}
.admin-nav-badge-inline { margin-left: 8px; }

/* ------------------------------------------------------------
   Admin — the ONE responsive breakpoint. Sidebar becomes a wrapping
   pill row: filter + group headers hidden, collapse disabled (the
   sections and lists flatten via display:contents so every link is
   a direct flex item of .admin-nav-list).
   ------------------------------------------------------------ */
@media (max-width: 900px) {
  .admin-shell { grid-template-columns: 1fr; }
  .admin-nav {
    position: static;
    height: auto;
    overflow: visible;
    flex-direction: row;
    padding: 10px;
    gap: 8px;
    flex-wrap: wrap;
  }
  .admin-nav-head { border-bottom: none; padding: 0 8px; flex-direction: row; align-items: center; gap: 8px; flex-wrap: wrap; }
  /* On mobile the header is a row and the footer (its old home) is
     hidden, so let the sign-out button sit inline without the desktop
     indent — and push it to the far right. */
  .admin-nav-head-signout-form { margin: 0 0 0 auto; }
  .admin-nav-filter { display: none; }
  .admin-nav-list { display: flex; gap: 4px; flex: 1; flex-wrap: wrap; margin: 0; }
  .admin-nav-section,
  .admin-nav-group-items,
  .admin-nav-group-items li { display: contents; }
  /* display:contents would defeat the UA's [hidden] rule — keep
     filtered-out links hidden if the viewport shrinks mid-filter. */
  .admin-nav-group-items li[hidden] { display: none; }
  .admin-nav-group-header { display: none; }
  .admin-nav-link { padding: 6px 12px; border-radius: 999px; gap: 6px; }
  .admin-nav-link.is-active {
    background: var(--accent);
    color: #fff;
    box-shadow: none;
  }
  .admin-nav-link.is-active .admin-nav-icon { color: #fff; }
  .admin-nav-foot { display: none; }
  .admin-main { padding: 18px 16px 60px; }
  .admin-form-grid { grid-template-columns: 1fr; }
  .admin-tabs { grid-template-columns: 1fr; gap: 16px; }
  .admin-tabs-rail { position: static; flex-direction: row; flex-wrap: wrap; }
  .admin-recent-grid { grid-template-columns: 1fr; }
}

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 1180px) {
  .editor-body-grid { grid-template-columns: 244px 1fr 56px; }
  .editor-body-grid.is-panel-open { grid-template-columns: 244px 1fr 260px 56px; }
  .page-sheet { padding: 40px 48px 60px; }
  .format-body[data-mode="gallery"] {
    grid-template-columns: 1fr;
    grid-template-areas:
      "gallery"
      "side";
    gap: 24px;
  }
  .format-body[data-mode="editor"] {
    grid-template-columns: 1fr;
    grid-template-areas:
      "bar"
      "nav"
      "editor"
      "side";
    gap: 16px;
  }
  /* Stacked below the editor and no longer sticky — shorten the shared
     preview height so the panel doesn't dominate a narrow screen (the
     device's width comes off this, so it also stops the bezel from being
     max-width-clamped into a squat shape). */
  .format-side { position: static; --preview-h: clamp(420px, 62vh, 620px); }
  .format-editor-nav { position: static; }
  .format-editor-nav ul { display: flex; flex-wrap: wrap; }
  .format-editor-nav li { flex: 1 1 auto; }
  .format-editor-nav-btn { padding: 8px 12px; }
}
@media (max-width: 900px) {
  .editor-body-grid { grid-template-columns: 220px 1fr 48px; }
  .editor-body-grid.is-panel-open { grid-template-columns: 220px 1fr 260px 48px; }
  .editor-topbar { grid-template-columns: 1fr auto auto; gap: 8px; }
  .topbar-title-text { max-width: 160px; font-size: 14px; }
  .btn-edit-details { display: none; }
  .editor-topbar .topbar-right { gap: 8px; }
}
@media (max-width: 1100px) {
  .details-grid { grid-template-columns: 1fr; }
  .details-rail { position: static; }
  .details-form-grid { grid-template-columns: 1fr; }
}
@media (max-width: 680px) {
  .editor-body-grid { grid-template-columns: 1fr; }
  .editor-body-grid.is-panel-open { grid-template-columns: 1fr; }
  .chapter-sidebar { display: none; }
  .icon-rail { display: none; }
  .rail-panel { display: none; }
  .page-sheet { padding: 28px 22px 48px; }
  .lib-band-inner { padding: 32px 16px 24px; }
  .lib-shelf { padding: 24px 16px 60px; }
  .library-hero { flex-direction: column; align-items: flex-start; }
  /* --shelf-gap must stay equal to the column gap (ledge overhang math). */
  .book-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); --shelf-gap: 16px; gap: 32px var(--shelf-gap); }
  .modal-grid { grid-template-columns: 1fr; }
  .details-main { padding: 16px 16px 60px; }
  .details-tab-pane { padding: 22px 18px 24px; }
  .details-form-grid { gap: 24px; }
}

/* ============================================================
   Scratchpad — persistent right-edge notes panel.

   A vertical "SCRATCHPAD" tab is always visible. Clicking slides
   a panel in from the right (translateX on .scratchpad-panel).
   The whole thing is position:fixed at z:1000 so it overlays
   every sticky toolbar, modal, dropdown, and confirm dialog the
   app uses (topmost in-app layer is z:500 for toasts).

   Markup is rendered in layout.php only when Auth::check(), so
   public pages (marketing/share/auth/maintenance) never see it.
   ============================================================ */
#scratchpad {
  position: fixed;
  top: 0;
  right: 0;
  height: 100vh;
  z-index: 1000;
  display: flex;
  align-items: flex-start;
  pointer-events: none; /* panel + tab each opt-in below */
  font-family: var(--font-sans);
}
.scratchpad-tab,
.scratchpad-panel { pointer-events: auto; }

.scratchpad-tab {
  position: absolute;
  /* Anchored to the lower-right corner rather than vertical-center so it
     clears the editor's icon-rail (whose buttons cluster at the top) and
     sits above the bottom status bar — covering nothing on any page. */
  top: auto;
  bottom: 72px;
  right: 0;
  /* vertical-rl + rotate(180deg) makes the label read bottom-to-top */
  writing-mode: vertical-rl;
  transform-origin: center;
  -webkit-appearance: none;
  appearance: none;
  /* Gold with a cream hairline and a gold ring outside it: reads on cream
     paper, in the dark theme and over a dark backdrop image alike (the old
     topbar-black tab vanished against artwork). */
  background: var(--accent);
  color: #fffaf0;
  border: 1px solid #fffaf0;
  border-right: none;
  border-radius: var(--radius) 0 0 var(--radius);
  padding: 18px 6px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .22em;
  text-shadow: 0 1px 0 rgba(0,0,0,.25);
  cursor: pointer;
  box-shadow: 0 0 0 1px var(--accent), -2px 2px 12px rgba(0,0,0,.35);
  transition: background .15s ease, color .15s ease, transform .15s ease;
}
.scratchpad-tab:hover { background: var(--accent-soft); }
/* Open: the tab becomes part of the panel's edge. */
#scratchpad.is-open .scratchpad-tab,
#todo-drawer.is-open .todo-tab {
  background: var(--bg-card); color: var(--accent); border-color: currentColor; box-shadow: none; text-shadow: none;
}
/* TO DO sits right above SCRATCHPAD — a second colour tells them apart. */
#todo-drawer .todo-tab { background: var(--success); box-shadow: 0 0 0 1px var(--success), -2px 2px 12px rgba(0,0,0,.35); }
#todo-drawer .todo-tab:hover { background: #5f9339; }
:root[data-theme="dark"] #todo-drawer .todo-tab:hover { background: #9ccc80; }
#todo-drawer.is-open .todo-tab { background: var(--bg-card); color: var(--success); box-shadow: none; }
.scratchpad-tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.scratchpad-tab-label {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  display: inline-block;
}

/* Panel sits offscreen by default; .is-open slides it in. */
.scratchpad-panel {
  width: 360px;
  height: 100vh;
  background: var(--bg-card);
  border-left: 1px solid var(--line);
  box-shadow: -8px 0 24px rgba(0,0,0,.12);
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  /* .scratchpad-ready is added one frame after load so the initial
     paint doesn't animate when the panel was already open on reload. */
}
#scratchpad.scratchpad-ready .scratchpad-panel {
  transition: transform 260ms ease;
}
#scratchpad.is-open .scratchpad-panel {
  transform: translateX(0);
}
/* When open, the tab rides the panel's left edge. */
#scratchpad.is-open .scratchpad-tab {
  right: 360px;
}
#scratchpad.scratchpad-ready .scratchpad-tab {
  transition: right 260ms ease, background .15s ease;
}

.scratchpad-head {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--bg-topbar);
  color: #f5f1e6;
  padding: 10px 14px;
  border-bottom: 1px solid var(--bg-topbar-soft);
}
.scratchpad-title {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: .02em;
  flex: 1;
  /* min-width:0 lets the flex item shrink so the active pad name can
     ellipsize instead of shoving the status pill + Clear off the edge. */
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.scratchpad-title-brand { flex: 0 0 auto; }
/* The active pad's name, appended after "Scratchpad ·". Slightly lighter
   than the brand so the two read as label + value. */
.scratchpad-title-pad { font-weight: 500; opacity: .82; }
.scratchpad-status {
  font-size: 11.5px;
  letter-spacing: .04em;
  opacity: .7;
  font-variant-numeric: tabular-nums;
}
.scratchpad-status[data-state="loading"] { color: var(--accent-soft); opacity: 1; }
.scratchpad-status[data-state="saving"]  { color: var(--accent-soft); opacity: 1; }
.scratchpad-status[data-state="saved"]   { color: #cfe6b5; opacity: .9; }
.scratchpad-status[data-state="empty"]   { opacity: .5; }
.scratchpad-status[data-state="error"]   { color: #f0a99d; opacity: 1; }
.scratchpad-clear {
  background: transparent;
  border: 1px solid rgba(255,255,255,.18);
  color: #f5f1e6;
  border-radius: var(--radius-sm);
  padding: 4px 10px;
  font-size: 12px;
  font-family: inherit;
  cursor: pointer;
  transition: background .12s ease, border-color .12s ease;
}
.scratchpad-clear:hover {
  background: rgba(255,255,255,.08);
  border-color: rgba(255,255,255,.32);
}

/* Rich-text scratchpad (contenteditable). Sans-serif by default — the
   point of preserving paste formatting is that the source typography
   carries through, so a monospace base font fights it. The mono font
   is reused only for code-ish content via <code>/<pre> below. */
.scratchpad-content {
  flex: 1;
  width: 100%;
  border: none;
  outline: none;
  padding: 14px 16px;
  background: var(--bg-paper);
  color: var(--ink-1);
  font-family: var(--font-sans, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
  font-size: 13.5px;
  line-height: 1.55;
  /* pre-wrap preserves whitespace from plaintext pastes (legacy data
     stored as raw text still reads correctly); overflow-wrap: anywhere
     handles long URLs / unbreakable tokens. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  overflow: auto;
  cursor: text;
}
/* CSS-only placeholder. :empty matches when the contenteditable has
   nothing in it; the load handler in scratchpad.js sets innerHTML=""
   for empty state so this kicks in cleanly. */
.scratchpad-content:empty::before {
  content: attr(data-placeholder);
  color: var(--ink-4);
  font-family: var(--font-sans);
  font-size: 12.5px;
  line-height: 1.5;
  pointer-events: none;
}
.scratchpad-content:focus {
  background: var(--bg-card);
}
/* While loading, scratchpad.js sets contenteditable="false". We lean
   on that attribute for the disabled visual cue (consistent with
   form controls). */
.scratchpad-content[contenteditable="false"] {
  cursor: progress;
  opacity: .55;
}
/* Rich-text formatting inside the scratchpad — keep it readable in
   the narrow panel. Headings step down in size relative to a normal
   article so an H1 paste doesn't blow out the panel. */
.scratchpad-content h1,
.scratchpad-content h2,
.scratchpad-content h3,
.scratchpad-content h4 {
  font-family: var(--font-sans);
  font-weight: 600;
  margin: 12px 0 6px;
  line-height: 1.25;
  color: var(--ink-1);
}
.scratchpad-content h1 { font-size: 15.5px; }
.scratchpad-content h2 { font-size: 14.5px; }
.scratchpad-content h3 { font-size: 13.8px; }
.scratchpad-content h4 { font-size: 13.3px; }
.scratchpad-content p { margin: 0 0 8px; }
.scratchpad-content ul,
.scratchpad-content ol { margin: 4px 0 8px; padding-left: 1.4em; }
.scratchpad-content li { margin: 2px 0; }
/* Some sources (Google Docs, rendered markdown) wrap each list item's
   text in its own <p>. Without this the <p>'s bottom margin stacks on
   top of the <li> margin and every bullet gets a blank line under it. */
.scratchpad-content li > p { margin: 0; }
.scratchpad-content a { color: var(--accent); text-decoration: underline; }
.scratchpad-content blockquote {
  border-left: 2px solid var(--line);
  padding-left: 10px;
  margin: 6px 0;
  color: var(--ink-2);
}
.scratchpad-content code,
.scratchpad-content pre {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  background: rgba(0,0,0,0.04);
  border-radius: 3px;
}
.scratchpad-content code { padding: 1px 4px; font-size: 12.5px; }
.scratchpad-content pre {
  padding: 8px 10px;
  overflow-x: auto;
  font-size: 12.5px;
  line-height: 1.5;
}
.scratchpad-content img {
  max-width: 100%;
  height: auto;
  border-radius: 4px;
}

/* Pad tabs (migration 072) — the strip of pills under the header that
   switches between up to 4 independent pads. Same pill idiom as the
   KDP pane tabs, on its own class names: these must NOT be
   .scratchpad-tab (that's the drawer's edge toggle, and todo.js's
   one-drawer-at-a-time clicks the first .scratchpad-tab:not(.todo-tab)).
   The To-Do drawer shares .scratchpad-panel/.scratchpad-head but never
   renders a .scratchpad-pads node, so none of this leaks into it. */
.scratchpad-pads {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px 6px;
  border-bottom: 1px solid var(--line);
  background: var(--bg-card);
  overflow-x: auto;
  flex: 0 0 auto;
}
.scratchpad-pad-tab {
  font: inherit;
  font-size: 12px;
  line-height: 1.4;
  padding: 3px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg-card);
  color: var(--ink-3);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 118px;
  flex: 0 0 auto;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.scratchpad-pad-tab .scratchpad-pad-label {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.scratchpad-pad-tab:hover { color: var(--ink-1); border-color: var(--ink-4); }
.scratchpad-pad-tab.is-active {
  background: var(--accent-bg);
  border-color: var(--accent-soft);
  color: var(--ink-1);
  font-weight: 600;
}
.scratchpad-pad-del {
  opacity: .55;
  font-weight: 400;
  padding: 0 1px;
}
.scratchpad-pad-del:hover { opacity: 1; color: var(--danger, #c0392b); }
.scratchpad-pad-add {
  font: inherit;
  font-size: 13px;
  line-height: 1;
  width: 24px;
  height: 24px;
  border: 1px dashed var(--line);
  border-radius: 999px;
  background: none;
  color: var(--ink-3);
  cursor: pointer;
  flex: 0 0 auto;
}
.scratchpad-pad-add:hover { color: var(--ink-1); border-color: var(--ink-4); }
.scratchpad-pad-rename {
  font: inherit;
  font-size: 12px;
  width: 92px;
  padding: 1px 6px;
  border: 1px solid var(--accent-soft);
  border-radius: 999px;
  background: var(--bg-paper);
  color: var(--ink-1);
  outline: none;
}

@media (max-width: 820px) {
  .scratchpad-panel { width: 80vw; }
  #scratchpad.is-open .scratchpad-tab { right: 80vw; }
}

/* Dark theme — the cream paper feel reads wrong on the dark app
   shell. Swap to the inky surfaces so the panel sits naturally
   above the editor and admin pages. Everything else in the drawer
   (content, pad pills) is var()-driven and needs no override. */
:root[data-theme="dark"] .scratchpad-panel {
  background: var(--bg-card);
  border-left-color: var(--line);
  box-shadow: -8px 0 24px rgba(0,0,0,.45);
}

/* ============================================================
   Codex drawer — left-edge slideout for the story codex.

   The mirror image of the scratchpad: a vertical "CODEX" tab pinned
   to the LEFT edge slides a panel in from the left (translateX). It
   hosts the same .bible-* sidebar/form that bible.js drives, so the
   codex is available while writing without leaving the page. Only
   rendered inside the editor (see editor.php); position:fixed at
   z:1000 so it overlays the editor chrome, matching the scratchpad.

   Left edge is deliberate — the scratchpad owns the right edge, so
   the two never collide and can be open at the same time.
   ============================================================ */
.codex-drawer {
  position: fixed;
  top: 0;
  left: 0;
  height: 100vh;
  z-index: 1000;
  display: flex;
  align-items: flex-start;
  pointer-events: none; /* tab + panel each opt-in below */
  font-family: var(--font-sans);
}
.codex-drawer-tab,
.codex-drawer-panel { pointer-events: auto; }

.codex-drawer-tab {
  position: absolute;
  /* Sit clear of the left chapter-sidebar's footer cluster (the tall
     "Add new chapter" + auto-number + recalc stack, ~115px), so the tab
     floats over the scrollable chapter list rather than on top of those
     buttons. That footer is why this sits higher than the scratchpad tab
     (bottom:72px), which only has to clear the 40px manuscript footer. */
  top: auto;
  bottom: 132px;
  left: 0;
  writing-mode: vertical-rl;
  -webkit-appearance: none;
  appearance: none;
  /* Same gold-and-cream treatment as the scratchpad tab (see there). */
  background: var(--accent);
  color: #fffaf0;
  border: 1px solid #fffaf0;
  border-left: none;
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 18px 6px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .22em;
  text-shadow: 0 1px 0 rgba(0,0,0,.25);
  cursor: pointer;
  box-shadow: 0 0 0 1px var(--accent), 2px 2px 12px rgba(0,0,0,.35);
  transition: background .15s ease, color .15s ease;
}
.codex-drawer-tab:hover { background: var(--accent-soft); }
#codex-drawer.is-open .codex-drawer-tab {
  background: var(--bg-card); color: var(--accent); border-color: currentColor; box-shadow: none; text-shadow: none;
}
.codex-drawer-tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.codex-drawer-tab-label {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  display: inline-block;
}

/* Panel sits offscreen to the left by default; .is-open slides it in.
   Width is --codex-w (declared on .editor-shell, written by editor.js,
   drag-resized by the grip below). The .is-open tab position and the
   shell's dock gutter read the same variable — one knob, not three. */
.codex-drawer-panel {
  width: var(--codex-w, 880px);
  max-width: 96vw;
  height: 100vh;
  background: var(--bg-card);
  border-right: 1px solid var(--line);
  box-shadow: 8px 0 24px rgba(0,0,0,.12);
  display: flex;
  flex-direction: column;
  transform: translateX(-100%);
  position: relative;             /* anchors .codex-drawer-resize */
  /* Container for the inner-layout tiers further down: the two-pane
     body has to respond to THIS panel's width, not the viewport's.

     ⚠ container-type implies `contain: layout`, which makes the element a
     containing block for position:fixed descendants. Safe here only
     because the codex family's one fixed element,
     #bible-mention-popover, lives OUTSIDE the drawer (editor.php). Never
     put this on .editor-shell — #codex-drawer is fixed inside it. */
  container: codex / inline-size;
  /* .codex-drawer-ready is added one frame after load so a drawer that
     was open on the last visit doesn't animate on the initial paint. */
}
#codex-drawer.codex-drawer-ready .codex-drawer-panel {
  transition: transform 260ms ease;
}
#codex-drawer.is-open .codex-drawer-panel {
  transform: translateX(0);
}
/* When open, the tab rides the panel's right edge. min() keeps it glued
   to that edge even when the 96vw max-width clamps the panel narrower. */
#codex-drawer.is-open .codex-drawer-tab {
  left: min(var(--codex-w, 880px), 96vw);
}
#codex-drawer.codex-drawer-ready .codex-drawer-tab {
  transition: left 260ms ease, background .15s ease;
}
/* Docked, the tab would land on the chapter sidebar's list instead of the
   empty manuscript margin it floats over today. Hide it while docked —
   the drawer's own × closes the panel and the resize grip is the visible
   seam. It returns the moment the drawer closes, so there is always a way
   back in. (Set by editor.js on the shell, hence the sibling-free
   descendant selector — the drawer is a child of .editor-shell.) */
.editor-shell.is-codex-docked #codex-drawer.is-open .codex-drawer-tab {
  opacity: 0;
  pointer-events: none;
}

/* ---- Drag-to-resize grip on the panel's right edge ----
   A div[role=separator][tabindex=0], deliberately not a <button>: a
   button swallows Enter/Space and is announced wrong. A focusable
   separator is the ARIA window-splitter pattern and carries
   aria-valuenow/min/max, which editor.js keeps current.

   Absolutely positioned INSIDE the panel so it rides the translateX when
   the drawer tucks offscreen. */
.codex-drawer-resize {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 10px;
  margin-right: -5px;          /* straddle the seam: half in, half out */
  padding: 0;
  border: none;
  background: transparent;
  cursor: col-resize;
  touch-action: none;          /* the pointer handlers own this gesture */
  z-index: 2;
}
.codex-drawer-resize::before {
  content: "";
  position: absolute;
  inset: 0 4px;
  background: var(--accent);
  opacity: 0;
  transition: opacity .12s ease;
}
.codex-drawer-resize:hover::before,
.codex-drawer-resize:focus-visible::before,
:root.is-codex-resizing .codex-drawer-resize::before { opacity: .7; }
.codex-drawer-resize:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
/* A tucked-away panel shouldn't offer a resize target. */
#codex-drawer:not(.is-open) .codex-drawer-resize { display: none; }

/* During a drag the 260ms slide/dock transitions make the panel edge, the
   tab and the shell gutter all LAG the pointer. Kill them for the
   duration. The id in the selector out-specifies
   #codex-drawer.codex-drawer-ready, so no !important is needed. */
:root.is-codex-resizing #codex-drawer .codex-drawer-panel,
:root.is-codex-resizing #codex-drawer .codex-drawer-tab,
:root.is-codex-resizing .editor-shell { transition: none; }
/* Suppresses the text-selection drag WITHOUT preventDefault() on
   pointerdown — which in some engines also swallows the dblclick the grip
   uses to snap between the default and compact widths. */
:root.is-codex-resizing,
:root.is-codex-resizing * {
  user-select: none;
  cursor: col-resize !important;
}

.codex-drawer-head {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--bg-topbar);
  color: #f5f1e6;
  padding: 10px 14px;
  border-bottom: 1px solid var(--bg-topbar-soft);
  flex: 0 0 auto;
}
.codex-drawer-title {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .18em;
}
.codex-drawer-close {
  margin-left: auto;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  border: none;
  color: #f5f1e6;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
  opacity: .8;
  transition: opacity .12s ease;
}
.codex-drawer-close:hover { opacity: 1; }

/* Body hosts the existing .bible-sidebar + .bible-main as a two-pane
   grid. min-height:0 lets the panes scroll internally instead of
   pushing the panel taller than the viewport. */
.codex-drawer-body {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  /* 260px list + the rest for the detail form — the portrait head needs
     the width more than the two-line list rows do. */
  grid-template-columns: 260px 1fr;
  overflow: hidden;
}
/* The main pane's full-page padding is too generous inside the narrower
   drawer — tighten it so the form fields have room to breathe. */
.codex-drawer .bible-main { padding: 24px 28px; }
.codex-drawer .bible-empty-state { margin: 56px auto; }

/* ---- Narrow-panel tiers (container queries, not media queries) ----
   The inner layout has to answer to the PANEL's width, now that the grip
   can drag it down to 320px. Every fixed-px rule below had a viewport
   @media escape that could never fire for a 420px panel on a 1920px
   screen — .bible-form-head's own (max-width:560px) at ~6708 is the
   clearest example.

   Container queries only match descendants of the container, so the
   shared .bible-* selectors here are automatically scoped to the drawer
   and cannot leak into any other render of the same markup. */
@container codex (max-width: 780px) {
  .codex-drawer-body { grid-template-columns: 220px 1fr; }
  .codex-drawer .bible-main { padding: 20px 18px; }
  /* The portrait's 180px column is the first thing to starve. */
  .bible-form-head { grid-template-columns: 1fr auto; }
  .bible-form-portrait { grid-column: 1 / -1; flex-direction: row; align-items: center; }
  .bible-form-image { width: 120px; height: 160px; }
  .bible-form-body { padding: 16px; }
  .bible-detail-row { grid-template-columns: 110px 1fr auto; }
}
@container codex (max-width: 640px) {
  /* The stacking the phone media query used to do — now it also fires for
     a narrow panel on a wide screen, which was always the intent. */
  .codex-drawer-body {
    grid-template-columns: 1fr;
    grid-template-rows: minmax(120px, 38%) 1fr;
  }
  .codex-drawer .bible-sidebar {
    border-right: none;
    border-bottom: 1px solid var(--line);
  }
  .bible-form-head { padding: 14px; }
  .bible-detail-row { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  /* Deliberately decoupled from --codex-w: on a phone the panel is the
     whole screen regardless of the stored width. Harmless because docking
     never engages below 1180px. */
  .codex-drawer-panel { width: 100vw; max-width: 100vw; }
  /* At full width the left-anchored tab would vanish offscreen, so when
     open, flip it to the right edge where it stays reachable. */
  #codex-drawer.is-open .codex-drawer-tab {
    left: auto;
    right: 0;
    border-radius: var(--radius) 0 0 var(--radius);
    border-left: 1px solid var(--bg-topbar);
    border-right: none;
  }
  /* The two-pane stacking that used to live here now comes from the
     @container 640px tier above — a 100vw panel on a ≤640px viewport is a
     ≤640px container, so it fires identically. */
}

/* Dark theme — same treatment as the scratchpad panel. */
:root[data-theme="dark"] .codex-drawer-panel {
  border-right-color: var(--line);
  box-shadow: 8px 0 24px rgba(0,0,0,.45);
}

/* ============================================================
   Admin-broadcast announcement banner.
   Renders at the top of every authenticated page until dismissed.
   Three tints (info/success/warning) match the kinds in the
   announcements table. Banner flows with the page (not sticky) so
   it doesn't permanently steal vertical space in the editor.
   ============================================================ */
.app-announcement {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 14px;
  padding: 12px 22px;
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.5;
  border-bottom: 1px solid var(--line);
  transition: opacity .2s ease, transform .2s ease;
  /* z-index keeps it above any sticky topbar that might cover it */
  position: relative;
  z-index: 80;
}
.app-announcement.is-dismissing {
  opacity: 0;
  transform: translateY(-6px);
}
.app-announcement-content {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 6px 14px;
  flex: 1;
  max-width: 1100px;
  text-align: center;
}
.app-announcement-title {
  font-weight: 600;
  color: var(--ink-1);
}
.app-announcement-body {
  color: var(--ink-2);
}
.app-announcement-cta {
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  padding: 2px 0;
  border-bottom: 1px solid currentColor;
  transition: opacity .12s ease;
}
.app-announcement-cta:hover { opacity: .75; }
.app-announcement-dismiss {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: none;
  background: transparent;
  color: var(--ink-3);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-top: -2px;
  transition: background .12s ease, color .12s ease;
}
.app-announcement-dismiss:hover {
  background: rgba(0, 0, 0, .08);
  color: var(--ink-1);
}
.app-announcement-dismiss:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Kind tints. Backgrounds picked to read clearly in both light
   and dark themes (dark-theme overrides further down). */
.app-announcement-info {
  background: #eef4f9;
  border-bottom-color: #cfdee9;
}
.app-announcement-info .app-announcement-cta { color: #1f4a6a; }

.app-announcement-success {
  background: #eef7ec;
  border-bottom-color: #cfe2c4;
}
.app-announcement-success .app-announcement-cta { color: #2d5a1f; }

.app-announcement-warning {
  background: var(--accent-bg);
  border-bottom-color: color-mix(in srgb, var(--accent) 40%, transparent);
}
.app-announcement-warning .app-announcement-cta { color: var(--accent); }

/* Snooze controls inside the Amazon ad-campaign reminder banner. The
   "Snooze" button reuses .app-announcement-cta but is a <button>, so
   strip the native chrome to match the adjacent link CTA. */
button.app-announcement-cta {
  background: none;
  border: none;
  border-bottom: 1px solid currentColor;
  cursor: pointer;
  font: inherit;
}
.app-campaign-reminder-snooze {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
}
.app-campaign-reminder-snooze-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--ink-2);
  font-size: .92em;
}
.app-campaign-reminder-date {
  font: inherit;
  padding: 2px 6px;
  border: 1px solid color-mix(in srgb, var(--ink-3) 50%, transparent);
  border-radius: 6px;
  background: var(--surface-1, #fff);
  color: var(--ink-1);
}

:root[data-theme="dark"] .app-announcement-info {
  background: #1d2934;
  border-bottom-color: #2f4253;
}
:root[data-theme="dark"] .app-announcement-info .app-announcement-cta { color: #82b3da; }
:root[data-theme="dark"] .app-announcement-success {
  background: #1f2c1f;
  border-bottom-color: #3a4f3a;
}
:root[data-theme="dark"] .app-announcement-success .app-announcement-cta { color: #93c777; }
:root[data-theme="dark"] .app-announcement-warning {
  background: #2c2516;
  border-bottom-color: color-mix(in srgb, var(--accent) 50%, transparent);
}
:root[data-theme="dark"] .app-announcement-dismiss:hover {
  background: rgba(255, 255, 255, .08);
}

@media (max-width: 640px) {
  .app-announcement { padding: 10px 14px; font-size: 13px; gap: 10px; }
  .app-announcement-content { text-align: left; justify-content: flex-start; }
}

/* ============================================================
   Sources view (Workshop → Sources: bibliography manager)
   Same shell as every other Workshop tab now (v3.19): a
   publishing-head banner up top, then the source list and its
   edit form side by side below it, all in one scrolling column
   — no longer a standalone full-height grid.
   ============================================================ */
.editor-view.sources-view {
  flex-direction: column;
  background: var(--bg-page);
  overflow: hidden;
}
.sources-view[hidden] { display: none !important; }

.sources-columns {
  display: flex;
  align-items: flex-start;
  gap: 24px;
}

/* ----- Sidebar ----- */
.sources-sidebar {
  display: flex;
  flex-direction: column;
  flex: 0 0 320px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius, 10px);
  overflow: hidden;
}
.sources-sidebar-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 18px 10px;
  border-bottom: 1px solid var(--line);
}
.sources-sidebar-title {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--ink-2);
}
.sources-sidebar-search {
  padding: 10px 14px;
  border-bottom: 1px solid var(--line-soft);
}
.sources-sidebar-search input {
  width: 100%;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 13px;
  background: var(--bg-paper);
}
.sources-sidebar-search input:focus { outline: none; border-color: var(--accent); }
.sources-sidebar-scroll {
  flex: 1;
  overflow-y: auto;
  padding: 8px 0 16px;
}
.sources-empty {
  padding: 18px 16px;
  margin: 0;
  font-size: 13px;
  color: var(--ink-3);
  font-style: italic;
}
.sources-group { margin: 12px 0 6px; }
.sources-group-head {
  margin: 0;
  padding: 6px 18px;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-3);
  display: flex;
  align-items: center;
  gap: 6px;
}
.sources-group-head .sources-count {
  margin-left: auto;
  font-weight: 500;
  color: var(--ink-3);
  font-size: 10.5px;
}
.sources-group-list { list-style: none; margin: 4px 0; padding: 0; }
.sources-row {
  padding: 8px 16px 8px 18px;
  border-left: 3px solid transparent;
  cursor: pointer;
  transition: background .12s ease;
}
.sources-row:hover { background: var(--bg-paper); }
.sources-row.is-selected {
  background: #fdf7e8;
  border-left-color: var(--accent);
}
.sources-row-title {
  font-weight: 500;
  color: var(--ink-1);
  font-size: 13.5px;
}
.sources-row-meta {
  font-size: 11.5px;
  color: var(--ink-3);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ----- Main / form ----- */
.sources-main { flex: 1; min-width: 0; }
.sources-empty-state {
  max-width: 440px;
  margin: 48px auto;
  text-align: center;
}
.sources-empty-state .empty-heading {
  font-size: 15px;
  color: var(--ink-1);
  margin: 0 0 6px;
}
.sources-empty-state .empty-sub {
  font-size: 13px;
  color: var(--ink-3);
  margin: 0 0 18px;
  line-height: 1.55;
}

.sources-form {
  max-width: 820px;
  margin: 0 auto;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-paper);
  overflow: hidden;
}
.sources-form-head {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 16px;
  align-items: start;
  padding: 18px 22px;
  border-bottom: 1px solid var(--line);
  background: var(--bg-paper);
}
.sources-form-head-fields { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.sources-title-input {
  width: 100%;
  font-family: var(--font-display);
  font-size: 21px;
  font-weight: 600;
  padding: 4px 0;
  border: none;
  border-bottom: 1px solid transparent;
  background: transparent;
  color: var(--ink-1);
}
.sources-title-input:focus { outline: none; border-bottom-color: var(--accent); }
.sources-form-row { display: flex; gap: 14px; flex-wrap: wrap; }
.sources-mini-field { display: flex; flex-direction: column; gap: 4px; }
.sources-mini-field > span {
  font-family: var(--font-sans);
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--ink-3);
}
.sources-mini-field select,
.sources-mini-field input {
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 13px;
  background: #fff;
}
.sources-mini-field select:focus,
.sources-mini-field input:focus { outline: none; border-color: var(--accent); }
.sources-mini-field #src-year { width: 80px; }
.sources-key-field { flex: 1; min-width: 140px; }
.sources-key-field input { width: 100%; }

.sources-form-delete {
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: #fff;
  color: var(--ink-3);
  cursor: pointer;
}
.sources-form-delete:hover { color: var(--danger); border-color: var(--danger); }

.sources-form-body {
  padding: 22px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.sources-field { display: flex; flex-direction: column; gap: 6px; }
.sources-field-label {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--ink-2);
}
.sources-field-hint {
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink-3);
  font-style: italic;
}
.sources-field input,
.sources-field textarea {
  padding: 8px 11px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 13.5px;
  background: var(--bg-paper);
  resize: vertical;
}
.sources-field input:focus,
.sources-field textarea:focus { outline: none; border-color: var(--accent); background: #fff; }
.sources-field-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}
.sources-field-grid-3 { grid-template-columns: 1fr 1fr 1fr; }

.sources-preview {
  margin-top: 6px;
  padding: 14px 16px;
  background: var(--bg-paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.sources-preview-label {
  font-family: var(--font-sans);
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-3);
}
.sources-preview-text {
  margin: 0;
  font-family: var(--font-serif, Georgia, serif);
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-1);
  text-indent: -1.6em;
  padding-left: 1.6em;
}
.sources-preview-muted { color: var(--ink-3); font-style: italic; }
.sources-preview-note { font-size: 11px; color: var(--ink-3); font-style: italic; }
.sources-form-foot {
  padding: 12px 22px;
  border-top: 1px solid var(--line);
  background: var(--bg-paper);
}
.sources-save-status { font-size: 12px; color: var(--ink-3); }

@media (max-width: 720px) {
  .sources-field-grid,
  .sources-field-grid-3 { grid-template-columns: 1fr; }
}

/* ----- Dark mode ----- */
:root[data-theme="dark"] .sources-sidebar,
:root[data-theme="dark"] .sources-form { background: var(--bg-rail, #1f1d1a); }
:root[data-theme="dark"] .sources-title-input,
:root[data-theme="dark"] .sources-mini-field select,
:root[data-theme="dark"] .sources-mini-field input,
:root[data-theme="dark"] .sources-field input,
:root[data-theme="dark"] .sources-field textarea { background: var(--bg-page); color: var(--ink-1); }
:root[data-theme="dark"] .sources-row.is-selected { background: rgba(255, 255, 255, .05); }

/* ============================================================
   In-text citations (citation Quill embed + picker modal)
   ============================================================ */
/* The atomic citation token inside the editor. Renders as a subtly
   tinted, non-editable chip so it's clearly one unit. In print/EPUB it
   falls back to plain text (those pipelines don't load this CSS). */
.ql-editor .cite-ref {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--ink-1);
  border-radius: 4px;
  padding: 0 3px;
  cursor: default;
  white-space: nowrap;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
.ql-editor .cite-ref:hover {
  background: color-mix(in srgb, var(--accent) 24%, transparent);
}

/* Pronunciation mark (read-aloud "pronounce as…") — editor-only cue. */
.ql-editor .pron { border-bottom: 1px dotted var(--accent); }

/* Read-aloud panel: pronunciation block + inline-mark button row. */
.tts-pron { margin-top: 8px; border-top: 1px solid var(--line); padding-top: 6px; }
.tts-pron textarea { width: 100%; font-size: 12.5px; }
.tts-pron-mark-row { margin: 6px 0 2px; }

/* The “＂” glyph the citation toolbar button uses reads small; nudge it. */
.ql-toolbar .ql-citation { font-size: 15px; }

/* ----- Picker modal ----- */
.cite-modal { position: fixed; inset: 0; z-index: 1000; }
.cite-modal[hidden] { display: none; }
.cite-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(20, 18, 15, .45);
}
.cite-modal-card {
  position: relative;
  width: min(520px, calc(100vw - 32px));
  max-height: calc(100vh - 80px);
  margin: 56px auto 0;
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 16px 48px rgba(0, 0, 0, .28);
  overflow: hidden;
}
.cite-modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--line);
}
.cite-modal-title { margin: 0; font-family: var(--font-display); font-size: 18px; }
.cite-modal-close {
  border: none;
  background: none;
  font-size: 24px;
  line-height: 1;
  color: var(--ink-3);
  cursor: pointer;
}
.cite-modal-close:hover { color: var(--ink-1); }
.cite-modal-search { padding: 12px 20px 8px; }
.cite-modal-search input {
  width: 100%;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 13.5px;
  background: var(--bg-paper);
}
.cite-modal-search input:focus { outline: none; border-color: var(--accent); }
.cite-modal-list {
  flex: 1;
  overflow-y: auto;
  padding: 4px 12px;
  min-height: 80px;
  max-height: 320px;
}
.cite-list-empty { padding: 16px; margin: 0; font-size: 13px; color: var(--ink-3); font-style: italic; }
.cite-source-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 100%;
  text-align: left;
  padding: 9px 12px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: none;
  cursor: pointer;
}
.cite-source-row:hover { background: var(--bg-paper); }
.cite-source-row.is-selected {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border-color: var(--accent);
}
.cite-source-title { font-size: 13.5px; font-weight: 500; color: var(--ink-1); }
.cite-source-meta { font-size: 11.5px; color: var(--ink-3); }
.cite-modal-empty { padding: 28px 20px; text-align: center; color: var(--ink-3); }
.cite-modal-empty p { margin: 0 0 12px; font-size: 13.5px; }
.cite-modal-options {
  padding: 12px 20px;
  border-top: 1px solid var(--line-soft);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.cite-opt-field { display: flex; flex-direction: column; gap: 5px; }
.cite-opt-field > span {
  font-family: var(--font-sans);
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--ink-3);
}
.cite-opt-field input {
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 13.5px;
  background: var(--bg-paper);
}
.cite-opt-field input:focus { outline: none; border-color: var(--accent); }
.cite-opt-check {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--ink-1);
  cursor: pointer;
}
.cite-hint { font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--ink-3); font-style: italic; }
.cite-modal-preview {
  padding: 12px 20px;
  border-top: 1px solid var(--line-soft);
  display: flex;
  align-items: baseline;
  gap: 10px;
}
.cite-preview-label {
  font-family: var(--font-sans);
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .12em;
  color: var(--ink-3);
}
.cite-preview-text {
  font-family: var(--font-serif, Georgia, serif);
  font-size: 14.5px;
  color: var(--ink-1);
}
.cite-modal-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 14px 20px;
  border-top: 1px solid var(--line);
  background: var(--bg-paper);
}

:root[data-theme="dark"] .cite-modal-card { background: var(--bg-rail, #1f1d1a); }
:root[data-theme="dark"] .cite-modal-search input,
:root[data-theme="dark"] .cite-opt-field input { background: var(--bg-page); color: var(--ink-1); }

/* ============================================================
   Rich footnote editor (footnotes.js popover + mini Quill)
   ============================================================ */
/* Existing footnotes are clickable to re-open the editor. */
.ql-editor sup.fn-ref { cursor: pointer; }
.ql-editor sup.fn-ref:hover { background: color-mix(in srgb, var(--accent) 22%, transparent); border-radius: 3px; }

.fn-modal { position: fixed; inset: 0; z-index: 950; }
.fn-modal[hidden] { display: none; }
.fn-modal-backdrop { position: absolute; inset: 0; background: rgba(20, 18, 15, .45); }
.fn-modal-card {
  position: relative;
  width: min(560px, calc(100vw - 32px));
  margin: 64px auto 0;
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 16px 48px rgba(0, 0, 0, .28);
  overflow: hidden;
}
.fn-modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--line);
}
.fn-modal-title { margin: 0; font-family: var(--font-display); font-size: 18px; }
.fn-modal-close {
  border: none; background: none; font-size: 24px; line-height: 1;
  color: var(--ink-3); cursor: pointer;
}
.fn-modal-close:hover { color: var(--ink-1); }

/* Mini Quill — keep the toolbar compact and the editing area modest. */
.fn-mini-wrap { padding: 12px 20px; }
.fn-mini-wrap .ql-toolbar.ql-snow {
  border: 1px solid var(--line);
  border-bottom: none;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  padding: 5px 8px;
}
.fn-mini-wrap .ql-container.ql-snow {
  border: 1px solid var(--line);
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  font-family: var(--font-serif, Georgia, serif);
  font-size: 14.5px;
}
.fn-mini-wrap .ql-editor {
  min-height: 110px;
  max-height: 260px;
  overflow-y: auto;
}
.fn-mini-wrap .ql-editor.ql-blank::before { font-style: italic; color: var(--ink-3); }
.fn-mini-wrap .ql-citation { font-size: 15px; }

.fn-modal-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 20px;
  border-top: 1px solid var(--line);
  background: var(--bg-paper);
}
.fn-foot-spacer { flex: 1; }
.fn-delete { color: var(--danger); }

:root[data-theme="dark"] .fn-modal-card { background: var(--bg-rail, #1f1d1a); }

/* Bibliography (preview + editor) — hanging indent per convention. */
.bibliography .bib-entry { margin: 0 0 0.7em; padding-left: 1.6em; text-indent: -1.6em; line-height: 1.5; text-align: left; }
.bibliography .bib-empty { font-style: italic; color: var(--ink-3); }

/* ============================================================
   Skip-to-content link. Visually hidden until focused, then drops in at
   the top-left as the first Tab stop so keyboard users can jump past the
   topbar/nav straight to the page's <main>. High z-index so it sits over
   sticky topbars and announcement banners.
   ============================================================ */
.skip-link {
  position: fixed;
  top: 8px;
  left: 8px;
  z-index: 100000;
  padding: 10px 16px;
  background: #111;
  color: #fff;
  border-radius: 6px;
  text-decoration: none;
  font-weight: 600;
  font-size: 14px;
  transform: translateY(-160%);
  transition: transform .15s ease;
}
.skip-link:focus {
  transform: translateY(0);
  outline: 2px solid #fff;
  outline-offset: 2px;
}
/* A container focused programmatically via the skip link shouldn't show a
   persistent focus box around the whole region. */
[tabindex="-1"]:focus { outline: none; }

/* ============================================================
   Reduced motion. Honour the OS "reduce motion" setting across the
   whole app — near-instant transitions/animations, no infinite
   spinners, no smooth-scroll. Loaded on every page (scrived.css is
   global) so it also covers marketing.css / cover-maker.css rules.
   The tiny non-zero duration keeps transitionend/animationend
   listeners firing so JS that waits on them still resolves.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ============================================================
   Help center (public /help pages). Sits inside .app-shell like
   library/settings; a sidebar (categories + search) plus a main pane
   that renders either a category grid, an article list, one article's
   prose, or search results. .help-prose mirrors .legal-prose's
   treatment of the same markdown-lite renderer output.
   ============================================================ */
.help-shell {
  display: grid;
  grid-template-columns: 260px 1fr;
  gap: 32px;
  max-width: 1160px;
  margin: 0 auto;
  padding: 32px 24px 96px;
  align-items: start;
}
@media (max-width: 860px) {
  .help-shell { grid-template-columns: 1fr; }
}

.help-nav {
  position: sticky;
  top: 24px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px;
}
.help-nav-home {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  color: var(--ink-1);
  text-decoration: none;
  padding: 4px 8px 10px;
  border-bottom: 1px solid var(--line-soft);
  margin-bottom: 6px;
}
.help-nav-home.is-active { color: var(--accent); }
.help-nav-search { margin-bottom: 10px; }
.help-search-input {
  width: 100%;
  box-sizing: border-box;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: 13.5px;
  background: var(--bg-page);
  color: var(--ink-1);
}
.help-search-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(139,105,20,.12);
}
.help-search-live {
  position: relative;
}
.help-search-live:not([hidden]) {
  display: block;
  margin: -4px 0 10px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-1);
  overflow: hidden;
  max-height: 320px;
  overflow-y: auto;
}
.help-search-live-item {
  display: block;
  padding: 8px 10px;
  text-decoration: none;
  border-bottom: 1px solid var(--line-soft);
}
.help-search-live-item:last-child { border-bottom: none; }
.help-search-live-item:hover,
.help-search-live-item:focus { background: var(--bg-page); }
.help-search-live-title { display: block; font-size: 13px; font-weight: 600; color: var(--ink-1); }
.help-search-live-cat { display: block; font-size: 11.5px; color: var(--ink-3); }
.help-search-live-empty { padding: 10px; font-size: 13px; color: var(--ink-3); font-style: italic; }

.help-nav-group { margin-top: 8px; }
.help-nav-group-label {
  display: block;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding: 8px 8px 4px;
}
.help-nav-list { list-style: none; margin: 0; padding: 0; }
.help-nav-link {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 8px;
  border-radius: var(--radius-sm);
  color: var(--ink-2);
  text-decoration: none;
  font-size: 13.5px;
}
.help-nav-link:hover { background: var(--bg-page); color: var(--ink-1); }
.help-nav-link.is-active { background: var(--bg-page); color: var(--accent); font-weight: 600; }
.help-nav-icon { font-size: 14px; }
.help-nav-back {
  display: block;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--line-soft);
  color: var(--ink-3);
  text-decoration: none;
  font-size: 13px;
}
.help-nav-back:hover { color: var(--ink-1); }

.help-main { min-width: 0; width: 100%; }
.help-hero { margin-bottom: 24px; }
.help-page-title {
  font-family: var(--font-display);
  font-size: 28px;
  font-weight: 600;
  margin: 0 0 8px;
  color: var(--ink-1);
}
.help-page-sub { margin: 0 0 18px; color: var(--ink-3); font-size: 14.5px; }
.help-hero-search {
  display: flex;
  gap: 10px;
  max-width: 640px;
  width: 100%;
}
.help-hero-search-input { flex: 1; width: 100%; padding: 10px 14px; font-size: 14.5px; }
.help-empty {
  color: var(--ink-3);
  font-style: italic;
  padding: 20px 0;
}

.help-crumb {
  font-size: 13px;
  color: var(--ink-3);
  margin-bottom: 12px;
}
.help-crumb a { color: var(--ink-3); }
.help-crumb a:hover { color: var(--accent); }

.help-category-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 16px;
}
.help-category-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px;
  text-decoration: none;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.help-category-card:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow-1);
}
.help-category-icon { font-size: 22px; }
.help-category-label {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  color: var(--ink-1);
}
.help-category-desc { font-size: 13px; color: var(--ink-3); line-height: 1.4; }
.help-category-count { margin-top: 6px; font-size: 11.5px; color: var(--ink-3); text-transform: uppercase; letter-spacing: .04em; }

.help-article-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.help-article-link {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 14px 16px;
  border-radius: var(--radius-sm);
  text-decoration: none;
  border-bottom: 1px solid var(--line-soft);
}
.help-article-link:hover { background: var(--bg-card); }
.help-article-title { font-size: 15px; font-weight: 600; color: var(--ink-1); }
.help-article-summary { font-size: 13px; color: var(--ink-3); }
.help-admin-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  vertical-align: middle;
}

.help-article {
  background: var(--bg-card);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 40px 48px;
  box-shadow: var(--shadow-1);
}
.help-article-h1 {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 600;
  margin: 0 0 20px;
  color: var(--ink-1);
}
.help-prose {
  font-family: var(--font-serif);
  font-size: 16px;
  line-height: 1.7;
  color: var(--ink-2);
}
.help-prose h2 {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 600;
  color: var(--ink-1);
  margin: 30px 0 12px;
}
.help-prose h2:first-child { margin-top: 0; }
.help-prose h3 {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  color: var(--ink-1);
  margin: 24px 0 8px;
}
.help-prose p { margin: 0 0 16px; }
.help-prose ul { margin: 0 0 18px; padding-left: 24px; }
.help-prose li { margin-bottom: 8px; }
.help-prose strong { color: var(--ink-1); font-weight: 600; }
.help-prose a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.help-prose a:hover { color: var(--accent-soft); }
.help-prose hr { border: none; border-top: 1px solid var(--line); margin: 28px 0; }

/* Rich-text media embeds — output of SiteSetting::renderRichText(),
   shared by help articles and the legal pages, so these rules are
   intentionally not scoped under .help-prose/.legal-prose. */
.rt-media { margin: 20px 0; }
.rt-media img { max-width: 100%; height: auto; border-radius: var(--radius-sm); display: block; }
.rt-video {
  position: relative;
  width: 100%;
  padding-top: 56.25%; /* 16:9 */
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: #000;
}
.rt-video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* Admin help-article editor toolbar (image upload, YouTube embed). */
.help-editor-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 6px;
}
.help-editor-toolbar-divider {
  width: 1px;
  align-self: stretch;
  background: var(--line);
  margin: 0 2px;
}

.help-editor-tabs {
  display: flex;
  gap: 4px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--line);
}
.help-editor-tab {
  padding: 7px 14px;
  border: none;
  background: none;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-3);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.help-editor-tab:hover { color: var(--ink-1); }
.help-editor-tab.is-active { color: var(--ink-1); border-bottom-color: var(--accent); }

.help-editor-preview {
  min-height: 200px;
  max-height: 520px;
  overflow-y: auto;
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-card);
}
.help-editor-preview-loading,
.help-editor-preview-empty {
  color: var(--ink-3);
  font-style: italic;
}

/* Help/legal admin textareas default to white-space: pre (no wrap) so
   hand-wrapped legal prose renders exactly as typed. Help articles are
   flowing paragraphs instead, so force wrapping here without touching
   the shared .admin-legal-textarea rule other admin pages rely on. */
/* #id, not .class: .admin-legal-textarea's white-space:pre lives in
   marketing.css, which <link>s in after scrived.css in layout.php's
   <head> — with equal class-selector specificity, that later rule
   would otherwise win regardless of source order in this file. */
#help-article-body {
  white-space: pre-wrap;
  overflow-x: hidden;
}

/* Media strip — actual thumbnails (not raw markdown) for every image
   embedded in the help-article textarea above, with a per-image size
   dropdown and remove button. See help_article_editor.js. */
.help-media-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding: 10px;
  margin-bottom: 8px;
  background: var(--bg-page);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
}
.help-media-card {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 6px 8px;
}
.help-media-thumb {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border-radius: 4px;
  background: var(--bg-page);
  flex-shrink: 0;
}
.help-media-meta { display: flex; flex-direction: column; gap: 4px; }
.help-media-alt {
  font-size: 12px;
  color: var(--ink-2);
  max-width: 160px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.help-media-size { font-size: 12px; padding: 2px 4px; }
.help-media-remove {
  align-self: flex-start;
  font-size: 11px;
  color: var(--danger);
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
}
.help-media-remove:hover { text-decoration: underline; }

.help-pager {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  margin-top: 20px;
}
.help-pager-link {
  flex: 1;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--ink-2);
  text-decoration: none;
  font-size: 13.5px;
}
.help-pager-link:hover { border-color: var(--accent); color: var(--ink-1); }
.help-pager-next { text-align: right; }

.help-search-results-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 18px; }
.help-search-result {
  background: var(--bg-card);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 16px 18px;
}
.help-search-result-title {
  display: block;
  font-size: 15.5px;
  font-weight: 600;
  color: var(--ink-1);
  text-decoration: none;
}
.help-search-result-title:hover { color: var(--accent); }
.help-search-result-cat {
  display: inline-block;
  margin: 2px 0 8px;
  font-size: 11.5px;
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: .04em;
}
.help-search-snippet {
  margin: 4px 0 0;
  font-size: 13.5px;
  color: var(--ink-3);
  line-height: 1.5;
}
.help-search-snippet mark {
  background: rgba(139,105,20,.22);
  color: var(--ink-1);
  padding: 0 2px;
  border-radius: 2px;
}

/* Admin help-editor form fields that need to span both grid columns. */
.field-wide { grid-column: 1 / -1; }


/* ============================================================
   KDP keywords (Workshop → KDP Keywords, migration 066)

   Seven slot rows, each a chip input with a byte meter. The meter's
   three tones are local tokens so the dark override lives in one
   place: green with headroom, amber as the 50-byte budget fills,
   red once KDP would reject the box.
   ============================================================ */
:root {
  --kdp-ok:   #4d7a2e;
  --kdp-near: #8a6d13;
  --kdp-over: #a93b2a;
}
:root[data-theme="dark"] {
  --kdp-ok:   #7fae5c;
  --kdp-near: #cba85a;
  --kdp-over: #d36456;
}

/* Seven stacked slots across a full-width editor read as an overwhelming wall
   of empty boxes — a keyword box is ~50 characters, so the content never needs
   more than a comfortable reading measure. The cap goes on the CHILDREN, not
   on .publishing-main: that's the scroll container, and capping it shoves the
   whole pane (scrollbar included) to the left edge of a wide screen with dead
   space beside it. Children capped + auto-margined = centered column,
   scrollbar stays at the viewport edge. */
/* The cap sits on the individual sections, NOT on .kdp-body: the description
   pane needs a wider measure (1200px, set with the pane styles) for its
   side-by-side editor+preview, and a cap on the common parent would win. */
#view-kdp .publishing-head,
#view-kdp .kdp-empty,
#view-kdp .kdp-listing-bar,
#view-kdp .kdp-steps,
#view-kdp .kdp-pane {
  max-width: 860px;
  margin-left: auto;
  margin-right: auto;
}

.kdp-head-actions {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-shrink: 0;           /* never crush the buttons into two-line pills */
}
.kdp-head-actions .btn { white-space: nowrap; }

.field-optional { color: var(--ink-4); font-weight: 400; }

/* ---------- walkthrough ---------- */
/* The "How X works" walkthrough: closed, it is the same pill as the
   Rocket helper's "Also harvest…" / "Past harvests" buttons (something you
   can see is clickable); open, the pill stays put and the steps unfold in
   a box beneath it. */
.kdp-guide { margin-bottom: 22px; }
.kdp-guide-summary {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 8px;
  font: inherit; font-size: 13px; font-weight: 500; color: var(--ink-1);
  padding: 7px 14px; border: 1px solid var(--line); border-radius: 999px; background: var(--bg-rail);
  transition: border-color .12s, background .12s;
  user-select: none;
}
.kdp-guide-summary::-webkit-details-marker { display: none; }
.kdp-guide-summary::before { content: "▸"; color: var(--ink-3); font-size: 11px; }
.kdp-guide[open] .kdp-guide-summary::before { content: "▾"; }
.kdp-guide-summary:hover,
.kdp-guide[open] .kdp-guide-summary { border-color: var(--accent); background: var(--accent-bg); }
.kdp-guide-title { font: inherit; }
.kdp-guide-toggle { display: none; }
.kdp-guide[open] .kdp-guide-steps {
  margin-top: 10px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent-soft);
  border-radius: var(--radius);
  background: var(--bg-card);
}
.kdp-guide-steps {
  margin: 0;
  padding: 12px 14px 14px 34px;
  counter-reset: kdp-step;
  list-style: none;
}
.kdp-guide-steps li {
  position: relative;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--ink-2);
  margin-bottom: 9px;
}
.kdp-guide-steps li:last-child { margin-bottom: 0; }
.kdp-guide-steps li::before {
  counter-increment: kdp-step;
  content: counter(kdp-step);
  position: absolute;
  left: -26px;
  top: 1px;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--accent-bg);
  border: 1px solid var(--accent-soft);
  color: var(--accent);
  font-size: 10.5px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}
.kdp-guide-steps strong { color: var(--ink-1); font-weight: 600; }
.kdp-guide-steps em { font-style: normal; color: var(--ink-1); font-weight: 500; }
/* The app only styles kbd inside .kbd-foot / .cmd-foot, so give ours a look. */
.kdp-guide-steps kbd {
  display: inline-block;
  padding: 0 5px;
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 3px;
  background: var(--bg-paper);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  color: var(--ink-2);
}

/* The shared .snap-link is a fixed 26x26 icon button — right for a bare ✎ or ✕,
   wrong for a worded action. This is its text sibling: same quiet treatment,
   sized to its label. */
.kdp-linkbtn {
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
  border: 1px solid var(--line);
  background: var(--bg-card);
  color: var(--ink-2);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font: inherit;
  font-size: 12px;
  padding: 4px 9px;
  transition: background .12s, color .12s, border-color .12s;
}
.kdp-linkbtn:hover { background: var(--bg-rail); border-color: var(--line-strong); color: var(--ink-1); }
.kdp-linkbtn-danger:hover { background: var(--danger); border-color: var(--danger); color: #fff; }

/* The app defines no global .btn:disabled, so a disabled primary button still
   reads as clickable. Scoped here rather than globally to avoid restyling
   every other view. */
#view-kdp .btn:disabled,
#view-kdp .kdp-linkbtn:disabled {
  opacity: .45;
  cursor: not-allowed;
}
#view-kdp .btn:disabled:hover { background: var(--accent); }
#view-kdp .btn-ghost:disabled:hover { background: transparent; }

/* ---------- empty state ---------- */
.kdp-empty {
  max-width: 520px;
  margin: 48px auto;
  text-align: center;
}
.kdp-empty-title { font-size: 15px; color: var(--ink-1); margin: 0 0 6px; }
.kdp-empty-hint  { font-size: 13px; color: var(--ink-3); line-height: 1.55; margin: 0 0 18px; }

/* ---------- KDP Select card (164) ----------
   Book-level; since v3.1 a compact section at the end of step 4
   (Publish & track) with the enrollment checkbox, term dates, the
   auto-renew mirror, and a status chip the JS keeps current. */
.kdp-select-card { padding-top: 4px; }
.kdp-select-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.kdp-select-status {
  font-size: 11.5px;
  font-weight: 600;
  padding: 4px 10px;
  border-radius: 999px;
  white-space: nowrap;
  background: var(--bg-rail);
  color: var(--ink-2);
}
/* Pinned literals, same stance as the other kdp status tints — the card
   is an opaque light surface in both themes. */
.kdp-select-status.is-ok     { background: rgba(80,160,80,.14);  color: #3f7a3f; }
.kdp-select-status.is-warn   { background: rgba(196,146,20,.16); color: #8b6914; }
.kdp-select-status.is-danger { background: rgba(190,60,50,.14);  color: #a53c31; }
.kdp-select-fields {
  display: flex;
  align-items: flex-end;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 10px;
}
.kdp-select-fields .field { margin: 0; }
.kdp-select-check {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  color: var(--ink-1);
  padding-bottom: 8px;
  cursor: pointer;
}
.kdp-select-card.is-off .kdp-select-check:not(:first-child),
.kdp-select-card.is-off .field { opacity: .5; }
.kdp-select-fields .btn { margin-bottom: 4px; }

/* ---------- listing bar ---------- */
.kdp-listing-bar {
  display: flex;
  align-items: flex-end;
  gap: 16px;
  padding: 0 0 16px;
  border-bottom: 1px solid var(--line-soft);
  margin-bottom: 20px;
  flex-wrap: wrap;
}
.kdp-listing-picker { min-width: 240px; margin: 0; }
.kdp-listing-meta { display: flex; gap: 8px; flex-wrap: wrap; flex: 1; padding-bottom: 6px; }
.kdp-meta-bit {
  font-size: 11px;
  color: var(--ink-3);
  background: var(--bg-rail);
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  padding: 2px 9px;
  white-space: nowrap;
}
.kdp-listing-tools { display: flex; gap: 6px; padding-bottom: 4px; }

/* ---------- editor ---------- */
.kdp-editor-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  margin-bottom: 14px;
}
.kdp-section-title { font-size: 14px; font-weight: 600; color: var(--ink-1); margin: 0 0 4px; }
.kdp-section-sub   { font-size: 12px; color: var(--ink-3); margin: 0; line-height: 1.5; max-width: 70ch; }
.kdp-editor-actions { display: flex; gap: 8px; align-items: center; flex-shrink: 0; }

.kdp-slots { display: flex; flex-direction: column; gap: 10px; }

.kdp-slot {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-left: 3px solid var(--kdp-ok);
  border-radius: var(--radius);
  padding: 10px 12px;
}
.kdp-slot.is-near { border-left-color: var(--kdp-near); }
.kdp-slot.is-over { border-left-color: var(--kdp-over); }

.kdp-slot-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 6px;
}
.kdp-slot-label { font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-3); }
.kdp-slot-meter { display: flex; align-items: center; gap: 8px; }
.kdp-meter-bar {
  width: 90px;
  height: 4px;
  border-radius: 999px;
  background: var(--bg-rail);
  overflow: hidden;
}
.kdp-meter-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--kdp-ok);
  transition: width .12s linear;
}
.kdp-slot.is-near .kdp-meter-fill { background: var(--kdp-near); }
.kdp-slot.is-over .kdp-meter-fill { background: var(--kdp-over); }
.kdp-meter-count { font-size: 11px; color: var(--ink-3); font-variant-numeric: tabular-nums; min-width: 92px; text-align: right; }
.kdp-slot.is-over .kdp-meter-count { color: var(--kdp-over); font-weight: 600; }

/* The shared .chip-input hides itself when empty; these boxes always show,
   because an empty slot still needs a click target and a text entry. */
.kdp-chip-input,
.kdp-chip-input:empty {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  min-height: 34px;
  cursor: text;
}
.kdp-chip-entry {
  flex: 1;
  min-width: 140px;
  border: none;
  background: transparent;
  font: inherit;
  font-size: 13px;
  color: var(--ink-1);
  padding: 2px 0;
  outline: none;
}
.kdp-chip-entry::placeholder { color: var(--ink-4); }

/* A read-only slot (no draft open) must not impersonate a text field: a
   dashed, untinted box, and no hover — clicking it does nothing (the Start
   new version button is the only way into a draft). */
.kdp-slot.is-readonly .kdp-chip-input {
  border-style: dashed;
  background: transparent;
  cursor: default;
}
/* Nothing to measure and nothing to copy: collapse the row to its essentials. */
.kdp-slot.is-bare { padding: 8px 12px; border-left-color: var(--line); }
.kdp-slot.is-bare .kdp-slot-head { margin-bottom: 4px; }
.kdp-slot.is-bare .kdp-chip-input { margin-bottom: 0; min-height: 30px; }
.kdp-slot-hint {
  font-size: 12.5px;
  color: var(--ink-4);
  font-style: italic;
  align-self: center;
}

/* Elsewhere .chip-x is a <span role="button">; here it's a real <button> so it
   is keyboard-reachable, which means its UA chrome needs clearing. */
.kdp-chip-input .chip-x {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  font-size: 14px;
  line-height: 1;
}

.kdp-slot-foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px dashed var(--line-soft);
}
.kdp-slot-rendered {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11.5px;
  color: var(--ink-3);
  overflow-x: auto;
  white-space: nowrap;
}

/* ---------- submit-ready block ---------- */
.kdp-submit-block {
  margin-top: 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-paper);
  overflow: hidden;
}
.kdp-submit-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 12px;
  background: var(--bg-rail);
  border-bottom: 1px solid var(--line-soft);
}
.kdp-submit-label { font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-3); }
.kdp-submit-tools { display: flex; align-items: center; gap: 12px; }
.kdp-total { font-size: 11px; color: var(--ink-4); font-variant-numeric: tabular-nums; }
.kdp-submit-pre {
  margin: 0;
  padding: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  line-height: 1.7;
  color: var(--ink-2);
  white-space: pre;
  overflow-x: auto;
  min-height: 40px;
}
.kdp-submit-pre.is-empty { color: var(--ink-4); font-style: italic; white-space: normal; }

/* ---------- validator output ---------- */
.kdp-warnings { margin-top: 16px; display: flex; flex-direction: column; gap: 10px; }
.kdp-warn-group {
  border: 1px solid var(--line);
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  background: var(--bg-card);
}
.kdp-warn-group.is-error { border-left-color: var(--kdp-over); }
.kdp-warn-group.is-warn  { border-left-color: var(--kdp-near); }
.kdp-warn-heading {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  margin: 0 0 6px;
  color: var(--ink-3);
}
.kdp-warn-group.is-error .kdp-warn-heading { color: var(--kdp-over); }
.kdp-warn-list { margin: 0; padding-left: 18px; }
.kdp-warn-list li { font-size: 12.5px; color: var(--ink-2); line-height: 1.6; margin-bottom: 3px; }
.kdp-warn-clean { font-size: 12.5px; color: var(--kdp-ok); margin: 0; }

/* ---------- history ---------- */
.kdp-history { margin-top: 32px; }
.kdp-history .kdp-section-sub { margin-bottom: 12px; }
.publishing-row.is-current { background: var(--accent-bg); }

/* The description pane runs its editor and preview side by side, which the
   860px column can't fit — it alone gets a wider measure (overrides the
   shared .kdp-pane cap above; same specificity, declared later). */
#view-kdp .kdp-pane.kdp-pane-wide { max-width: 1200px; }

/* ---------- verdicts (good / bad keyword) ---------- */
.kdp-dot {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  margin-right: 5px;
  vertical-align: baseline;
  flex-shrink: 0;
}
.kdp-dot-testing         { background: var(--kdp-near); }
.kdp-dot-working         { background: var(--kdp-ok); }
.kdp-dot-underperforming { background: var(--kdp-over); }

.kdp-verdict-select {
  font: inherit;
  font-size: 11.5px;
  padding: 2px 4px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-card);
  color: var(--ink-2);
  max-width: 132px;
}

/* Segmented verdict picker in the performance modal. */
.kdp-obs-verdict { display: flex; gap: 6px; margin: 4px 0 10px; }
.kdp-verdict-btn {
  display: inline-flex;
  align-items: center;
  font: inherit;
  font-size: 12px;
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg-card);
  color: var(--ink-2);
  cursor: pointer;
}
.kdp-verdict-btn:hover { background: var(--bg-rail); }
.kdp-verdict-btn.is-active { border-color: var(--ink-3); background: var(--bg-rail); color: var(--ink-1); font-weight: 600; }

/* ---------- description pane ---------- */
.kdpd-workbench {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 16px;
  /* start + a height-capped sticky preview: the preview re-renders on every
     keystroke, and if its column can grow the page reflows under the caret. */
  align-items: start;
}
@media (max-width: 980px) {
  .kdpd-workbench { grid-template-columns: 1fr; }
}
.kdpd-toolbar {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.kdpd-body {
  width: 100%;
  min-height: 320px;
  resize: vertical;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px;
  line-height: 1.6;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-paper);
  color: var(--ink-1);
}
.kdpd-body:disabled { opacity: .65; cursor: not-allowed; }
.kdpd-meter {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-top: 6px;
}
#kdpd-counter {
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--kdp-ok);
}
#kdpd-counter.is-near { color: var(--kdp-near); }
#kdpd-counter.is-over { color: var(--kdp-over); }
.kdpd-meter-note { font-size: 11.5px; color: var(--ink-4); }

.kdpd-preview-col {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-card);
  overflow: hidden;
  /* Capped so a long blurb scrolls INSIDE the preview instead of growing the
     page; sticky keeps it beside the textarea while the author scrolls. */
  position: sticky;
  top: 12px;
  max-height: min(70vh, 560px);
}
.kdpd-preview-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 12px;
  background: var(--bg-rail);
  border-bottom: 1px solid var(--line-soft);
}
.kdpd-preview {
  flex: 1;
  padding: 14px 16px;
  overflow-y: auto;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--ink-2);
  min-height: 200px;
}
.kdpd-preview h4, .kdpd-preview h5, .kdpd-preview h6 { margin: 0 0 8px; color: var(--ink-1); }
.kdpd-preview p  { margin: 0 0 10px; }
.kdpd-preview hr { border: none; border-top: 1px solid var(--line); margin: 12px 0; }
.kdpd-preview ul, .kdpd-preview ol { margin: 0 0 10px; padding-left: 22px; }
.kdpd-preview-empty { color: var(--ink-4); font-style: italic; }
.kdpd-fold {
  display: inline-block;
  margin: 0 4px;
  padding: 0 8px;
  border: 1px dashed var(--kdp-near);
  border-radius: 999px;
  font-size: 10.5px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--kdp-near);
  white-space: nowrap;
  user-select: none;
}

/* ---------- categories pane ---------- */
.kdpc-slots { display: flex; flex-direction: column; gap: 10px; }
.kdpc-slot {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 12px;
}
.kdpc-slot-meta { display: flex; align-items: center; gap: 8px; }
.kdpc-bsr { font-size: 11.5px; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.kdpc-slot-input {
  width: 100%;
  font: inherit;
  font-size: 13px;
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-paper);
  color: var(--ink-1);
}
.kdpc-slot-path {
  font-size: 13px;
  color: var(--ink-1);
  padding: 7px 2px;
}
.kdpc-slot-path.is-empty { color: var(--ink-4); font-style: italic; }
.kdpc-slot-hint { margin: 4px 2px 0; font-size: 11.5px; color: var(--kdp-near); }

/* "Shown on Amazon" — the display categories the product page actually lists. */
.kdpc-shown { margin-top: 22px; }
.kdpc-shown-list { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.kdpc-shown-empty { font-size: 12.5px; color: var(--ink-4); font-style: italic; margin: 6px 0 0; }
.kdpc-shown-row {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 9px 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.kdpc-shown-main { display: flex; align-items: center; gap: 8px; min-width: 0; flex-wrap: wrap; }
.kdpc-shown-path { font-size: 13px; color: var(--ink-1); }
.kdpc-shown-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 11.5px;
  color: var(--ink-3);
}
.kdpc-shown-checked { color: var(--ink-4); }
.kdpc-shown-meta .snap-link { margin-left: auto; }
.kdpc-shown-add { display: flex; gap: 8px; margin-top: 10px; }
.kdpc-shown-add input {
  flex: 1;
  font: inherit;
  font-size: 13px;
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-paper);
  color: var(--ink-1);
}

@media (max-width: 700px) {
  .kdp-slot-head { flex-wrap: wrap; gap: 6px; }
  .kdp-meter-bar { width: 60px; }
  .kdp-editor-head { flex-direction: column; }
  .kdp-listing-bar { flex-direction: column; align-items: stretch; }
}

/* ==========================================================================
   Workshop dashboard (Workshop → Dashboard)
   Card grid summarizing every Workshop tab. All colors via vars — dark mode
   needs no extra rules here.
   ========================================================================== */
.wd-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr));
  /* dense: the 2-column KDP card wraps; let later cards backfill the hole */
  grid-auto-flow: dense;
  gap: 16px;
  max-width: 1200px;
}
.wd-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;                    /* long labels ellipsize instead of overflowing */
  padding: 16px 18px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  text-align: left;
}
.wd-card:hover,
.wd-card:focus-visible {
  border-color: var(--line-strong);
  box-shadow: var(--shadow-1);
}
.wd-card-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}
.wd-card-title {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-3);
}
.wd-card-arrow { color: var(--ink-4); font-size: 14px; }
.wd-card:hover .wd-card-arrow { color: var(--accent); }

.wd-stat { display: flex; align-items: baseline; gap: 8px; }
.wd-stat-num {
  font-family: var(--font-display);
  font-size: 30px;
  line-height: 1;
  color: var(--ink-1);
}
.wd-stat-label { font-size: 12.5px; color: var(--ink-3); }
.wd-pills { display: flex; flex-wrap: wrap; gap: 6px; }
.wd-meta { margin: 0; font-size: 12.5px; color: var(--ink-3); }
.wd-next { margin: 0; font-size: 12.5px; color: var(--ink-2); }
.wd-next.wd-due { color: var(--danger); font-weight: 600; }

/* KDP small multiples — one series per spark, accent hue, lower-is-better
   axis is inverted in JS so up always means improving. */
.wd-sparks {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 2px;
  padding-top: 10px;
  border-top: 1px solid var(--line-soft);
}
.wd-sparks-title { margin: 0; font-size: 11px; color: var(--ink-4); }
.wd-spark-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
.wd-spark-label {
  flex: 1 1 40%;
  min-width: 0;
  font-size: 12px;
  color: var(--ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.wd-spark { flex: 0 0 100px; }
.wd-spark svg { display: block; width: 100%; height: 30px; }
.wd-spark-hint { flex: 1; font-size: 11.5px; color: var(--ink-4); font-style: italic; }
.wd-spark-latest {
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-1);
  white-space: nowrap;
}
.wd-delta { font-size: 11.5px; font-variant-numeric: tabular-nums; color: var(--ink-3); white-space: nowrap; }
.wd-delta.is-up   { color: var(--success); }
.wd-delta.is-down { color: var(--danger); }

/* To-do card — interactive checkboxes, same strikethrough as the drawer. */
.wd-todos { display: flex; flex-direction: column; gap: 6px; }
.wd-todo {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 13px;
  color: var(--ink-1);
  line-height: 1.4;
  cursor: pointer;
}
.wd-todo input { margin-top: 2px; accent-color: var(--accent); cursor: pointer; flex: 0 0 auto; }
.wd-todo span { overflow-wrap: anywhere; }
.wd-todo.is-done span { text-decoration: line-through; color: var(--ink-4); }

/* Cover checklist */
.wd-check { display: flex; align-items: center; gap: 8px; margin: 0; font-size: 13px; color: var(--ink-3); }
.wd-check.is-done { color: var(--ink-1); }
.wd-check-mark { color: var(--ink-4); }
.wd-check.is-done .wd-check-mark { color: var(--success); font-weight: 700; }

/* Empty / error / skeleton states */
.wd-card-empty { border-style: dashed; }
.wd-empty-text { margin: 0; font-size: 13px; color: var(--ink-4); }
.wd-setup { font-size: 13px; font-weight: 500; color: var(--accent); }
.wd-card-error { cursor: default; }
.wd-card-error .btn { align-self: flex-start; }
.wd-card-skeleton { cursor: default; }
.wd-skel-line {
  height: 12px;
  border-radius: 6px;
  background: var(--bg-rail);
  animation: wd-skel 1.2s ease-in-out infinite alternate;
}
.wd-skel-wide { height: 26px; width: 55%; }
.wd-skel-short { width: 35%; }
@keyframes wd-skel { from { opacity: .55; } to { opacity: 1; } }

/* The KDP card carries the sparkline block — give it room at wide widths. */
@media (min-width: 900px) {
  .wd-card[data-target="kdp"] { grid-column: span 2; }
}

/* ==========================================================================
   Final cover files (Workshop → Cover → Final Files)
   ========================================================================== */
.cvf-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr));
  gap: 16px;
  margin-top: 14px;
}
.cvf-card {
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px 18px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}
.cvf-title { margin: 0; font-size: 15px; color: var(--ink-1); }
.cvf-spec { margin: 0; font-size: 12px; color: var(--ink-4); }
.cvf-none { margin: 0; font-size: 13px; color: var(--ink-4); font-style: italic; }
.cvf-thumb img {
  display: block;
  max-width: 120px;
  max-height: 180px;
  border: 1px solid var(--line);
  border-radius: 4px;
}
/* TIFF (or otherwise unrenderable) covers get a file badge, not a broken img. */
.cvf-thumb-file {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 120px;
  height: 150px;
  border: 1px dashed var(--line-strong);
  border-radius: 4px;
  background: var(--bg-rail);
  text-decoration: none;
  padding: 8px;
  text-align: center;
}
.cvf-thumb-ext {
  font-size: 16px;
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--ink-2);
}
.cvf-thumb-note { font-size: 10.5px; color: var(--ink-4); line-height: 1.4; }
.cvf-fileline { margin: 0; font-size: 13px; overflow-wrap: anywhere; }
.cvf-fileline a { color: var(--accent); }
.cvf-meta { display: block; font-size: 11.5px; color: var(--ink-4); margin-top: 2px; }
.cvf-checks { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.cvf-check { font-size: 12.5px; color: var(--ink-2); display: flex; gap: 7px; align-items: baseline; }
.cvf-check-glyph { flex: 0 0 auto; }
.cvf-check.is-ok   .cvf-check-glyph { color: var(--success); font-weight: 700; }
.cvf-check.is-warn { color: var(--danger); }
.cvf-check.is-info { color: var(--ink-3); }
.cvf-actions { display: flex; gap: 10px; align-items: center; margin-top: auto; }
.cvf-limit { margin: 12px 0 0; font-size: 11.5px; color: var(--ink-4); }

/* ==========================================================================
   To-Do drawer — rides the scratchpad's slide mechanics (#todo-drawer reuses
   .scratchpad-tab/.scratchpad-panel classes); only the tab position and the
   list content are its own.
   ========================================================================== */
#todo-drawer {
  position: fixed;
  top: 0;
  right: 0;
  height: 100vh;
  z-index: 1000;
  display: flex;
  align-items: flex-start;
  pointer-events: none;
  font-family: var(--font-sans);
}
#todo-drawer .scratchpad-tab,
#todo-drawer .scratchpad-panel { pointer-events: auto; }
/* Above the SCRATCHPAD tab (which is anchored at bottom: 72px). */
#todo-drawer .todo-tab { bottom: 220px; }
#todo-drawer .scratchpad-panel { transform: translateX(100%); }
#todo-drawer.scratchpad-ready .scratchpad-panel { transition: transform 260ms ease; }
#todo-drawer.is-open .scratchpad-panel { transform: translateX(0); }
#todo-drawer.is-open .todo-tab { right: 360px; }
#todo-drawer.scratchpad-ready .todo-tab { transition: right 260ms ease, background .15s ease; }

.todo-count { font-size: 11.5px; opacity: .75; }
.todo-add {
  display: flex;
  gap: 8px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--line-soft);
}
.todo-add input {
  flex: 1;
  font: inherit;
  font-size: 13px;
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-paper);
  color: var(--ink-1);
}
.todo-list {
  list-style: none;
  margin: 0;
  padding: 8px 6px;
  overflow-y: auto;
  flex: 1;
}
.todo-empty { padding: 10px 10px; font-size: 12.5px; color: var(--ink-4); font-style: italic; }
.todo-item {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 8px 8px;
  border-radius: 6px;
}
.todo-item:hover { background: var(--bg-rail); }
.todo-check { margin-top: 3px; accent-color: var(--accent); cursor: pointer; flex: 0 0 auto; }
.todo-text {
  flex: 1;
  font-size: 13.5px;
  color: var(--ink-1);
  line-height: 1.45;
  overflow-wrap: anywhere;
}
.todo-item.is-done .todo-text {
  text-decoration: line-through;
  color: var(--ink-4);
}
.todo-delete {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  padding: 3px 4px;
  border-radius: 4px;
  opacity: .45;
  flex: 0 0 auto;
}
.todo-item:hover .todo-delete { opacity: 1; }
.todo-delete:hover { background: var(--bg-card); }
/* Inline edit (v2.50): pencil shares the delete button's hover-reveal;
   the input replaces .todo-text in place (scratchpad rename idiom —
   never classed .scratchpad-pad-*, see the coupling note there). */
.todo-edit {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 12px;
  line-height: 1;
  padding: 3px 4px;
  border-radius: 4px;
  opacity: .45;
  flex: 0 0 auto;
  color: var(--ink-2);
}
.todo-item:hover .todo-edit { opacity: 1; }
.todo-edit:hover { background: var(--bg-card); color: var(--accent); }
.todo-edit-input {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: 13.5px;
  color: var(--ink-1);
  background: var(--bg-card);
  border: 1px solid var(--accent);
  border-radius: 5px;
  padding: 3px 6px;
}
.todo-edit-input:focus { outline: none; }

@media (max-width: 480px) {
  #todo-drawer.is-open .todo-tab { right: 80vw; }
}


/* ============================================================
   OUTLINE VIEW (v2.49, migration 152)
   Scrivener-style outliner: chapters as editable table rows.
   Everything uses the theme variables so :root[data-theme="dark"]
   flips it for free. The table scrolls INSIDE .outline-stage
   (house rule) and the popovers/hovercard are body-mounted
   position:fixed — never transform an ancestor.
   ============================================================ */
.outline-view {
  display: flex;
  flex-direction: column;
  background: var(--bg-page);
  overflow: hidden;
  /* REQUIRED: hosts the optional .editor-backdrop layer (absolute,
     inset:0). Without this the backdrop escapes the section and paints
     over the topbar — the exact bug shipped on 2026-08-07. Same rule
     as .corkboard-view / .plot-view. */
  position: relative;
}
.outline-view[hidden] { display: none !important; }

.outline-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 22px;
  border-bottom: 1px solid var(--line-soft, var(--line));
  background: var(--bg-card);
  flex-wrap: wrap;
  position: relative; /* sits above the backdrop layer */
  /* Each strip here is its own stacking context, so a data-tip bubble
     is CAPPED at its section's z-index — a tooltip hanging down from
     one strip must not slide under the next. Hence the descending
     ladder: toolbar 4 > stats 3 > drag-hint 2 > stage 1 (v2.52). */
  z-index: 4;
}
.outline-title {
  font-family: var(--font-display);
  margin: 0 0 2px;
  font-size: 22px;
  font-weight: 600;
  color: var(--ink-1);
}
.outline-sub { color: var(--ink-3); font-size: 12.5px; }
.outline-toolbar-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.outline-search {
  padding: 5px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg-paper);
  font: inherit;
  font-size: 12.5px;
  color: var(--ink-1);
  width: 190px;
}
.outline-search:focus { outline: none; border-color: var(--accent); }

/* Stats strip — the status chips ARE the status filter. */
.outline-stats {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 8px 22px;
  background: var(--bg-paper);
  border-bottom: 1px solid var(--line-soft, var(--line));
  position: relative;
  z-index: 3;   /* below the toolbar, above the stage — see the ladder note */
}
.outline-stats-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.outline-stats-chips .res-chip em {
  font-style: normal;
  opacity: .75;
  margin-left: 2px;
}
.outline-stats-roll { color: var(--ink-2); font-size: 12.5px; }
.outline-stats-roll strong { color: var(--ink-1); }

.outline-drag-hint {
  padding: 6px 22px;
  font-size: 12px;
  color: var(--ink-3);
  background: var(--accent-soft);
  border-bottom: 1px solid var(--line-soft, var(--line));
  position: relative;
  z-index: 2;   /* the stacking ladder — see .outline-toolbar */
}

/* ---------- Stage + table ---------- */
.outline-stage {
  flex: 1;
  overflow: auto;           /* both axes — wide tables scroll HERE, not the page */
  padding: 0 0 40px;
  position: relative;
  z-index: 1;
}
.outline-table {
  width: 100%;
  min-width: 900px;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 13px;
  color: var(--ink-1);
}
.outline-table thead th {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--bg-card);
  border-bottom: 2px solid var(--line);
  padding: 8px 10px;
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
}
.outline-th-sort { cursor: pointer; user-select: none; }
.outline-th-sort:hover { color: var(--accent); }
.outline-th-sort.is-sorted-asc  .outline-sort-arrow::after { content: '▲'; font-size: 9px; margin-left: 4px; }
.outline-th-sort.is-sorted-desc .outline-sort-arrow::after { content: '▼'; font-size: 9px; margin-left: 4px; }
.outline-th-sort.is-sorted-asc, .outline-th-sort.is-sorted-desc { color: var(--accent); }

.outline-table td {
  padding: 4px 10px;
  border-bottom: 1px solid var(--line-soft, var(--line));
  vertical-align: middle;
  background: var(--bg-card);
}
.outline-row:hover td { background: var(--bg-paper); }
.outline-row.is-dragging td { opacity: .45; background: var(--accent-soft); }

/* Section header + subtotal rows */
.outline-section-row td {
  background: var(--bg-rail, var(--bg-paper));
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-2);
  padding: 10px;
  border-bottom: 1px solid var(--line);
}
.outline-section-count { font-weight: 500; color: var(--ink-3); margin-left: 8px; }
.outline-subtotal td {
  background: var(--bg-paper);
  color: var(--ink-3);
  font-size: 12px;
  padding: 6px 10px;
}
.outline-subtotal strong { color: var(--ink-1); }

/* Part divider rows */
.outline-row-part td {
  background: var(--accent-soft);
  font-weight: 600;
}
.outline-row-part .outline-title-cell input { font-weight: 700; }
.outline-part-caret {
  border: 0;
  background: transparent;
  cursor: pointer;
  color: var(--ink-2);
  font-size: 12px;
  padding: 2px 6px;
  border-radius: 4px;
  transition: transform .12s ease;
}
.outline-part-caret:hover { background: rgba(0,0,0,.06); color: var(--ink-1); }
.outline-part-caret.is-collapsed { transform: rotate(-90deg); }

/* ---------- Cells ---------- */
.outline-num { color: var(--ink-3); font-variant-numeric: tabular-nums; }
.outline-num-cell { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.outline-center { text-align: center; }
.outline-dim { color: var(--ink-3); opacity: .6; }

.outline-table input[type="text"],
.outline-table input[type="number"],
.outline-table select,
.outline-table textarea {
  width: 100%;
  border: 1px solid transparent;
  border-radius: var(--radius-sm, 6px);
  background: transparent;
  font: inherit;
  font-size: 13px;
  color: var(--ink-1);
  padding: 3px 6px;
}
.outline-table input:hover, .outline-table select:hover, .outline-table textarea:hover {
  border-color: var(--line);
}
.outline-table input:focus, .outline-table select:focus, .outline-table textarea:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg-paper);
}
.outline-table input[type="number"] { text-align: right; }
.outline-table input[type="checkbox"] { width: auto; accent-color: var(--accent); }

.outline-title-cell { display: flex; align-items: center; gap: 6px; }
.outline-title-cell input { font-weight: 600; min-width: 0; }
.outline-kind-badge {
  flex: 0 0 auto;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--ink-3);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 1px 5px;
}
.outline-open {
  flex: 0 0 auto;
  border: 0;
  background: transparent;
  color: var(--ink-3);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  padding: 2px 7px;
  border-radius: 5px;
  opacity: 0;
  transition: opacity .1s ease;
}
.outline-row:hover .outline-open { opacity: 1; }
.outline-open:hover { color: var(--accent); background: var(--accent-soft); }

/* Synopsis: one clamped line at rest, grows while editing */
.outline-table td[data-col="synopsis"] textarea {
  resize: none;
  overflow: hidden;
  height: 26px;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.outline-table td[data-col="synopsis"].is-editing textarea {
  white-space: pre-wrap;
  overflow: auto;
}

/* Label cell: colour dot + select */
.outline-label-wrap { display: flex; align-items: center; gap: 6px; }
.outline-label-dot {
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 1px solid var(--line);
}

/* Status select tinted by value */
.outline-status.is-draft    { color: var(--ink-2); }
.outline-status.is-revising { color: #b07818; }
.outline-status.is-revised  { color: #1f5f8b; }
.outline-status.is-final    { color: #2e6b3f; }

/* Progress bar */
.outline-progress {
  position: relative;
  height: 18px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg-paper);
  overflow: hidden;
  min-width: 90px;
}
.outline-progress-bar {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  background: var(--accent-soft);
  border-right: 2px solid var(--accent);
}
.outline-progress.is-done .outline-progress-bar {
  background: rgba(46, 107, 63, .18);
  border-right-color: #2e6b3f;
}
.outline-progress-text {
  position: relative;
  z-index: 1;
  display: block;
  text-align: center;
  font-size: 10.5px;
  line-height: 18px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}
.outline-progress.is-done .outline-progress-text { color: #2e6b3f; font-weight: 700; }

/* Plot chips */
.outline-plot-cell { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
.outline-plotchip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg-paper);
  color: var(--ink-2);
  font: inherit;
  font-size: 11.5px;
  padding: 2px 9px;
  cursor: pointer;
  max-width: 150px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.outline-plotchip:hover { border-color: var(--accent); color: var(--ink-1); }
.outline-plotchip em { font-style: normal; font-weight: 700; color: var(--ink-3); }
.outline-plotchip-dot {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 50%;
}
.outline-plotchip-add {
  padding: 2px 8px;
  color: var(--ink-3);
  opacity: 0;
  transition: opacity .1s ease;
}
.outline-row:hover .outline-plotchip-add { opacity: 1; }
.outline-plotchip-add:hover { color: var(--accent); }

/* The pinned hovercard needs the pointer back (.plot-hovercard sets
   pointer-events:none so the roaming tooltip can't eat hovers). */
.outline-hovercard.is-pinned { pointer-events: auto; }
.outline-hover-beat {
  display: block;
  width: 100%;
  text-align: left;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--bg-paper);
  font: inherit;
  color: var(--ink-1);
  padding: 6px 9px;
  margin-top: 6px;
  cursor: pointer;
}
.outline-hover-beat:hover { border-color: var(--accent); background: var(--accent-soft); }
.outline-hover-beat .plot-hovercard-body {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.outline-hover-add {
  display: block;
  width: 100%;
  margin-top: 8px;
  border: 1px dashed var(--line);
  border-radius: 8px;
  background: transparent;
  color: var(--ink-3);
  font: inherit;
  font-size: 12px;
  padding: 5px 9px;
  cursor: pointer;
}
.outline-hover-add:hover { color: var(--accent); border-color: var(--accent); }

/* Drag handle */
.outline-drag {
  border: 0;
  background: transparent;
  color: var(--ink-3);
  cursor: grab;
  font-size: 12px;
  letter-spacing: -2px;
  padding: 4px 6px;
  border-radius: 5px;
  opacity: .35;
}
.outline-row:hover .outline-drag:not(:disabled) { opacity: 1; }
.outline-drag:disabled { cursor: not-allowed; opacity: .15; }
.outline-drag:not(:disabled):active { cursor: grabbing; }

/* ---------- Popovers (columns picker, label manager) ---------- */
.outline-popover {
  position: fixed;
  z-index: 80;
  min-width: 220px;
  max-width: 320px;
  max-height: 60vh;
  overflow-y: auto;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 10px 34px rgba(0,0,0,.28), 0 2px 8px rgba(0,0,0,.16);
  padding: 12px;
}
.outline-popover-title {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 8px;
}
.outline-popover-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 2px;
  font-size: 13px;
  color: var(--ink-1);
}
.outline-popover-row input[type="checkbox"] { accent-color: var(--accent); }
.outline-popover-empty { color: var(--ink-3); font-size: 12.5px; margin: 4px 0; }
.outline-label-row input[type="color"] {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: transparent;
  padding: 1px;
  cursor: pointer;
}
.outline-label-row input[type="text"] {
  flex: 1;
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm, 6px);
  background: var(--bg-paper);
  font: inherit;
  font-size: 12.5px;
  color: var(--ink-1);
  padding: 4px 7px;
}
.outline-label-del {
  flex: 0 0 auto;
  border: 0;
  background: transparent;
  color: var(--ink-3);
  font-size: 15px;
  cursor: pointer;
  padding: 2px 6px;
  border-radius: 4px;
}
.outline-label-del:hover { color: #b03030; background: rgba(176,48,48,.08); }
.outline-labels-add { margin-top: 8px; width: 100%; }

/* Corkboard card label stripe (152) — left edge in the label's colour. */
.corkboard-card.has-label { border-left: 4px solid var(--card-label, transparent); }

@media (max-width: 900px) {
  .outline-toolbar { padding: 12px 14px; }
  .outline-stats { padding: 8px 14px; }
  .outline-search { width: 140px; }
}



/* ============================================================
   OUTLINE v2.50 — Scrivener-style rows + Inspector (migration 154 round)
   Overrides/extends the v2.49 outline block above by source order.
   The Inspector is a flex-row TRACK beside the stage (rail-panel
   pattern) — never a fixed overlay, no containing-block hazard.
   ============================================================ */

/* Stage + inspector share a flex row inside the column-flex view. */
.outline-main {
  display: flex;
  flex: 1;
  min-height: 0;          /* lets the stage scroll instead of growing */
  position: relative;
  z-index: 1;             /* above the backdrop layer */
}
.outline-main .outline-stage { flex: 1; min-width: 0; }

/* --- Scrivener-style rows ------------------------------------- */
.outline-table td { padding: 5px 10px; vertical-align: top; }
.outline-table td[data-col="handle"],
.outline-table td[data-col="num"] { padding-top: 8px; }
.outline-row.is-selected td { background: var(--accent-soft); }
.outline-row.is-selected td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
.outline-row { cursor: pointer; }
.outline-row .outline-title-cell input { font-size: 13.5px; }

/* Synopsis under the title: small grey 2-line clamp at rest, the
   autosizing textarea while editing (swap handled in outline.js). */
.outline-syn-wrap { margin: 1px 0 2px 2px; }
.outline-syn {
  font-size: 12px;
  line-height: 1.45;
  color: var(--ink-3);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  cursor: text;
  padding: 1px 6px;
  border-radius: 5px;
}
.outline-syn:hover { background: var(--accent-soft); color: var(--ink-2); }
.outline-syn.is-empty { color: var(--ink-4, var(--ink-3)); opacity: 0; font-style: italic; }
.outline-row:hover .outline-syn.is-empty { opacity: .7; }
.outline-syn-wrap textarea {
  width: 100%;
  font-size: 12px;
  line-height: 1.45;
  resize: none;
  overflow: hidden;
  border-color: var(--accent);
  background: var(--bg-paper);
}

/* Label chip (replaces the raw select in rows). */
.outline-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg-paper);
  color: var(--ink-2);
  font: inherit;
  font-size: 11.5px;
  padding: 2px 9px;
  cursor: pointer;
  margin-top: 2px;
}
.outline-chip:hover { border-color: var(--accent); color: var(--ink-1); }
.outline-chip-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.outline-label-chip.is-empty {
  border-style: dashed;
  color: var(--ink-3);
  opacity: 0;
  padding: 2px 8px;
}
.outline-row:hover .outline-label-chip.is-empty { opacity: .8; }

/* Status pill — tinted, click-cycles (corkboard idiom). */
.outline-status-pill {
  display: inline-block;
  border: 1px solid transparent;
  border-radius: 999px;
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .02em;
  padding: 2px 10px;
  cursor: pointer;
  margin-top: 2px;
}
.outline-status-pill.is-draft    { background: rgba(120,120,120,.12); color: var(--ink-2); }
.outline-status-pill.is-revising { background: rgba(176,120,24,.14);  color: #b07818; }
.outline-status-pill.is-revised  { background: rgba(31,95,139,.14);   color: #1f5f8b; }
.outline-status-pill.is-final    { background: rgba(46,107,63,.16);   color: #2e6b3f; }
.outline-status-pill:hover { border-color: currentColor; }

/* Label pick popover rows. */
.outline-pick-row {
  width: 100%;
  border: 0;
  background: transparent;
  cursor: pointer;
  text-align: left;
  border-radius: 6px;
  font: inherit;
}
.outline-pick-row:hover { background: var(--accent-soft); }
.outline-pick-row.is-current { font-weight: 600; }
.outline-pick-check { margin-left: auto; color: var(--accent); }
.outline-pick-manage {
  margin-top: 6px;
  border-top: 1px solid var(--line);
  border-radius: 0 0 6px 6px;
  color: var(--ink-3);
  font-size: 12px;
  padding-top: 8px;
}
.outline-pick-manage:hover { color: var(--accent); }

/* Slimmer progress bar to match the tighter rows. */
.outline-progress { height: 16px; min-width: 84px; margin-top: 3px; }
.outline-progress-text { font-size: 10px; line-height: 16px; }

/* --- Inspector -------------------------------------------------- */
.outline-inspector {
  width: var(--outline-inspector-w, 340px);
  flex: 0 0 var(--outline-inspector-w, 340px);
  border-left: 1px solid var(--line);
  background: var(--bg-card);
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.outline-inspector[hidden] { display: none; }
.outline-insp-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--line-soft, var(--line));
  background: var(--bg-paper);
}
.outline-insp-num { color: var(--ink-3); font-size: 12px; font-variant-numeric: tabular-nums; }
.outline-insp-spacer { flex: 1; }
.outline-insp-close {
  border: 0;
  background: transparent;
  color: var(--ink-3);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  padding: 2px 8px;
  border-radius: 5px;
}
.outline-insp-close:hover { color: var(--ink-1); background: var(--accent-soft); }
.outline-insp-body {
  flex: 1;
  overflow-y: auto;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.outline-insp-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.outline-insp-field > span,
.outline-insp-label {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.outline-insp-private { font-weight: 400; text-transform: none; letter-spacing: 0; font-style: italic; }
.outline-insp-field input[type="text"],
.outline-insp-field input[type="number"],
.outline-insp-field select,
.outline-insp-field textarea {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm, 6px);
  background: var(--bg-paper);
  font: inherit;
  font-size: 13px;
  color: var(--ink-1);
  padding: 6px 8px;
}
.outline-insp-field input:focus,
.outline-insp-field select:focus,
.outline-insp-field textarea:focus { outline: none; border-color: var(--accent); }
.outline-insp-field textarea { resize: vertical; min-height: 60px; }
.outline-insp-row { display: flex; gap: 10px; }
.outline-insp-row > * { flex: 1; min-width: 0; }
.outline-insp-check {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  color: var(--ink-1);
  align-self: flex-end;
  padding-bottom: 7px;
  cursor: pointer;
}
.outline-insp-check input { accent-color: var(--accent); }
.outline-insp-beats { display: flex; flex-direction: column; gap: 6px; }
.outline-insp-beat {
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  text-align: left;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--bg-paper);
  font: inherit;
  font-size: 12.5px;
  color: var(--ink-1);
  padding: 6px 9px;
  cursor: pointer;
}
.outline-insp-beat:hover { border-color: var(--accent); background: var(--accent-soft); }
.outline-insp-beat-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.outline-insp-none { color: var(--ink-3); font-size: 12.5px; margin: 0; }
.outline-insp-addbeat {
  border: 1px dashed var(--line);
  border-radius: 7px;
  background: transparent;
  color: var(--ink-3);
  font: inherit;
  font-size: 12px;
  padding: 5px 9px;
  cursor: pointer;
}
.outline-insp-addbeat:hover { color: var(--accent); border-color: var(--accent); }
.outline-insp-meta {
  padding: 10px 14px;
  border-top: 1px solid var(--line-soft, var(--line));
  color: var(--ink-3);
  font-size: 12px;
}
.outline-insp-meta strong { color: var(--ink-1); }

/* Under ~980px the inspector hides entirely (rail-panel tiers) —
   the table is the priority surface on small screens. */
@media (max-width: 980px) {
  .outline-inspector { display: none; }
  .outline-view.is-inspector-open .outline-inspector { display: none; }
}



/* ============================================================
   CHAPTER HIERARCHY v2.51 (migration 156)
   Folders, nesting, and the "cut from the exported book" state,
   across the sidebar, the outline and the corkboard.

   Indentation is done with a GRID TRACK, never padding-left:
   .chapter-item already uses padding-left for the find-in-book
   badge (.has-find-badge), and two rules fighting over the same
   property is how that badge would start jumping. The first
   track is the indent + caret slot; everything else stays pinned
   exactly where it was.
   ============================================================ */

/* ---- Sidebar rows ------------------------------------------ */
.chapter-item {
  /* indent+caret | number | name | cut | dot | badge | words | delete */
  grid-template-columns: calc(var(--tree-depth, 0) * 14px + 16px) 20px 1fr auto auto auto auto auto;
}
.chapter-item > .chapter-caret        { grid-column: 1; }
.chapter-item > .chapter-number,
.chapter-item > .chapter-bullet       { grid-column: 2; }
.chapter-item > .chapter-name         { grid-column: 3; }
.chapter-item > .chapter-cut-tag      { grid-column: 4; }
.chapter-item > .chapter-status-dot   { grid-column: 5; }
.chapter-item > .chapter-comments-badge { grid-column: 6; }
.chapter-item > .chapter-words        { grid-column: 7; }
.chapter-item > .chapter-delete       { grid-column: 8; }

.chapter-caret {
  justify-self: end;
  border: 0;
  background: transparent;
  color: var(--ink-3);
  font-size: 10px;
  line-height: 1;
  padding: 2px;
  cursor: pointer;
  border-radius: 4px;
  transition: transform .12s ease, color .12s ease;
}
.chapter-caret:hover { color: var(--accent); background: var(--accent-soft); }
.chapter-item.is-collapsed > .chapter-caret { transform: rotate(-90deg); }

.chapter-folder-mark { color: var(--accent); font-size: 11px; }
.is-folder-row > .chapter-name { font-weight: 600; color: var(--ink-1); }
.is-folder-row > .chapter-words { opacity: .55; }

/* Cut from the export: still fully editable, just visibly not in the book. */
.chapter-item.is-excluded > .chapter-name { opacity: .62; text-decoration: line-through; }
.chapter-cut-tag {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-3);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0 5px;
  line-height: 15px;
}

/* Drop zones: a line above / below for "beside", a ring for "inside". */
.chapter-item.drag-over { box-shadow: none; }
.chapter-item.drop-before { box-shadow: inset 0 2px 0 var(--accent); }
.chapter-item.drop-after  { box-shadow: inset 0 -2px 0 var(--accent); }
.chapter-item.drop-inside {
  box-shadow: inset 0 0 0 2px var(--accent);
  background: var(--accent-soft);
}

/* The "new folder" button sits beside the section "+". */
.sidebar-section-add-folder { display: inline-flex; align-items: center; justify-content: center; }
.sidebar-section-add-folder svg { display: block; }

/* ---- Outline rows ------------------------------------------ */
/* The title cell carries the indent — the # column stays a fixed
   ruler down the left edge so numbers still line up at any depth. */
.outline-row .outline-title-cell,
.outline-row .outline-syn-wrap { margin-left: calc(var(--tree-depth, 0) * 16px); }
.outline-row.is-nested .outline-title-cell::before {
  content: '';
  flex: 0 0 auto;
  width: 8px;
  height: 1px;
  margin-right: 2px;
  background: var(--line);
}
.outline-row-folder .outline-title-cell input[data-field="title"] { font-weight: 600; }
.outline-kind-badge.is-folder { border-color: var(--accent); color: var(--accent); }
.outline-kind-badge.is-cut { text-transform: uppercase; letter-spacing: .06em; opacity: .8; }
.outline-row.is-cut .outline-title-cell input[data-field="title"] { opacity: .62; text-decoration: line-through; }
/* Colour, NOT opacity: a translucent <td> lets the editor backdrop through
   and the row stops being readable at all. Same reason the row tints are
   color-mix() rather than rgba(). */
.outline-row.is-cut td { color: var(--ink-3); }

.outline-num { white-space: nowrap; }
.outline-num-text { font-variant-numeric: tabular-nums; }
/* The caret shares the # cell with the number, so a parent chapter keeps
   both. It has to stay narrow or the column widens at depth. */
.outline-part-caret + .outline-num-text { margin-left: 2px; }

.outline-row.outline-drop-before td { box-shadow: inset 0 2px 0 var(--accent); }
.outline-row.outline-drop-after  td { box-shadow: inset 0 -2px 0 var(--accent); }
.outline-row.outline-drop-inside td { background: var(--accent-soft); }
.outline-row.outline-drop-inside td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

/* Inspector: the export switch gets its own row plus an explanation line
   for the "cut by an ancestor" case. */
.outline-insp-check-block { align-self: flex-start; padding-bottom: 0; }
.outline-insp-note {
  margin: -6px 0 0;
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--ink-3);
  font-style: italic;
}

/* ---- Corkboard cards --------------------------------------- */
/* Cards are a flat grid on purpose — nesting is edited where the tree is
   visible — so depth reads as a small inset stripe rather than a layout
   change that would break the grid's even columns. */
.corkboard-card.is-folder-card { border-style: dashed; }
.corkboard-card-folder { color: var(--accent); }
.corkboard-card[data-depth="2"] { box-shadow: inset 4px 0 0 rgba(139, 105, 20, .28); }
.corkboard-card[data-depth="3"] { box-shadow: inset 8px 0 0 rgba(139, 105, 20, .28); }
.corkboard-card[data-depth="4"] { box-shadow: inset 12px 0 0 rgba(139, 105, 20, .28); }
.corkboard-card[data-depth="5"] { box-shadow: inset 16px 0 0 rgba(139, 105, 20, .28); }
/* Not `opacity` on the card: the corkboard's whole point is a busy
   photographic board behind it, and a see-through card is unreadable.
   Desaturate the card's own surface and strike the title instead. */
.corkboard-card.is-cut { filter: saturate(.55); }
.corkboard-card.is-cut .corkboard-card-title { text-decoration: line-through; color: var(--ink-3); }


/* ============================================================
   CORKBOARD SECTION HEADERS — plaque pills
   The heads used to be bare uppercase text on a full-width hairline.
   Over a cork photograph or a backdrop that's small light type on a
   busy mid-tone field, which is exactly the contrast case text-shadow
   can't rescue. They're plaques now: an opaque dark chip that carries
   its own contrast, so one treatment reads on every board skin
   (default / cork / linen / dark / photo texture / backdrop) in both
   light and dark mode.

   SPECIFICITY NOTE: the per-skin colour rules above are
   `.corkboard-view.skin-X .corkboard-section-head h3` — three classes
   plus an element. The overrides below deliberately match that weight
   (`.corkboard-stage .corkboard-section .corkboard-section-head h3`)
   and win on source order. A shorter selector would silently lose on
   every skin except the default, which is the trap the skin
   source-order rule exists to prevent.
   ============================================================ */

.corkboard-section-head {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin: 0 0 14px;
  padding: 5px 14px;
  border: 1px solid rgba(255, 255, 255, .16);
  border-bottom: 1px solid rgba(255, 255, 255, .16);
  border-radius: 999px;
  background: rgba(26, 21, 14, .84);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .22);
}

.corkboard-stage .corkboard-section .corkboard-section-head h3 {
  color: #f4efe3;
  text-shadow: none;
  font-size: 11.5px;
  letter-spacing: .15em;
}

/* The count sits behind a hairline divider inside the same plaque, so
   "BODY" and "13 cards" read as one label rather than two stray words. */
.corkboard-stage .corkboard-section .corkboard-section-count {
  color: #c6bba3;
  text-shadow: none;
  padding-left: 9px;
  border-left: 1px solid rgba(255, 255, 255, .18);
}


/* ============================================================
   OUTLINE ROW STATES v2.51b — telling structure from selection
   `--accent-soft` is #b58a2f: a fully saturated gold, not the light
   tint the name suggests. Both "this row is a folder/Part" and "this
   row is selected" filled the entire row with it, so two unrelated
   meanings looked identical and neither read as anything in
   particular. Split by WEIGHT, not hue:

     structure (folder / Part) — permanent, so it whispers: a faint
       wash plus a bold title and its badge.
     selection — transient and you just caused it, so it speaks: a
       stronger wash plus the accent bar down the left edge.

   THE TINTS MUST BE OPAQUE. `.outline-table td` is
   `background: var(--bg-card)`, and that opacity is doing real work: the
   Outline sits over the editor BACKDROP layer, so a translucent row lets
   the artwork through and the title becomes unreadable — which is exactly
   what the first cut of this block did with rgba(). Mixing the accent INTO
   the card colour keeps every row a solid surface while still reading as a
   tint, and it follows light/dark for free because both inputs are theme
   vars (no --accent-soft redefinition: that var is shared app-wide, per the
   band-scoped palette law).

   Specificity is `.outline-table` + class + element so these sit above
   `.outline-row:hover td`; otherwise hovering a folder dropped its tint.
   ============================================================ */

.outline-table .outline-row-part td {
  background: color-mix(in srgb, var(--accent) 14%, var(--bg-card));
  font-weight: 600;
}
.outline-table .outline-row-folder td {
  background: color-mix(in srgb, var(--accent) 9%, var(--bg-card));
}

/* Selection speaks louder than structure, and wins when a folder is the
   selected row — otherwise clicking a folder appears to do nothing. */
.outline-table .outline-row.is-selected td,
.outline-table .outline-row-part.is-selected td,
.outline-table .outline-row-folder.is-selected td {
  background: color-mix(in srgb, var(--accent) 28%, var(--bg-card));
}
.outline-row.is-selected td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

/* Nesting guide. The first pass drew an 8px dash before the title,
   which read as a stray em dash sitting in the data — exactly the
   punctuation the v2.50 redesign removed from empty cells. A vertical
   rule down the indent is a tree guide and can't be mistaken for
   content. */
.outline-row.is-nested .outline-title-cell::before { content: none; }
.outline-row.is-nested .outline-title-cell,
.outline-row.is-nested .outline-syn-wrap {
  border-left: 2px solid var(--line);
  padding-left: 10px;
}

/* Drop indicators: thick enough to see mid-drag, and "inside" reads as
   a filled target rather than another line. */
.outline-row.outline-drop-before td { box-shadow: inset 0 3px 0 var(--accent); }
.outline-row.outline-drop-after  td { box-shadow: inset 0 -3px 0 var(--accent); }
.outline-table .outline-row.outline-drop-inside td {
  background: color-mix(in srgb, var(--accent) 38%, var(--bg-card));
}
.outline-row.outline-drop-inside td:first-child { box-shadow: inset 4px 0 0 var(--accent); }
.outline-row.outline-drop-inside td:last-child  { box-shadow: inset -4px 0 0 var(--accent); }

.chapter-item.drop-before { box-shadow: inset 0 3px 0 var(--accent); }
.chapter-item.drop-after  { box-shadow: inset 0 -3px 0 var(--accent); }
.chapter-item.drop-inside {
  box-shadow: inset 0 0 0 2px var(--accent);
  background: color-mix(in srgb, var(--accent) 24%, var(--bg-rail, var(--bg-card)));
}

/* ============================================================
   OUTLINE v2.52 — centered document table, resizable columns,
   discoverable Inspector.

   table-layout:fixed is what makes the <colgroup> widths REAL
   (auto layout treats them as suggestions) and makes a per-frame
   col.style.width write cheap during a resize drag. The one
   width-less column (title) absorbs all leftover space, so the
   max-width cap is what actually bounds title line length.

   Centering: the table used to span the full stage. Capping and
   auto-centering it leaves the stage background visible left and right —
   deliberate "document on a desk" framing. When dragged widths sum
   past the cap, a fixed-layout table grows to fit its columns and
   the stage's existing overflow:auto scrolls; nothing special
   needed. NO blanket td{overflow:hidden} here: it would clip the
   data-tip bubbles; the synopsis clamp and chip ellipsis already
   contain the cells that can actually overflow.
   ============================================================ */

.outline-table {
  table-layout: fixed;
  max-width: 1280px;
  margin-inline: auto;
  border-inline: 1px solid var(--line);
}

/* Resize grip on each header's right edge. The th is sticky
   (positioned), so the absolute grip needs no extra wrapper. The
   10px hit area straddles the column boundary. The marker line is
   ALWAYS visible — a muted hairline so first-timers can see the
   columns are adjustable — and turns full accent on hover/drag. */
.outline-col-grip {
  position: absolute;
  right: -5px;
  top: 0;
  bottom: 0;
  width: 10px;
  cursor: col-resize;
  z-index: 2;
}
.outline-col-grip::before {
  content: '';
  position: absolute;
  left: 4px;
  top: 15%;
  bottom: 15%;
  width: 2px;
  border-radius: 1px;
  background: color-mix(in srgb, var(--accent) 35%, var(--bg-card));
}
.outline-col-grip:hover::before,
:root.is-outline-resizing .outline-col-grip::before { background: var(--accent); }
:root.is-outline-resizing { user-select: none; cursor: col-resize; }

/* Plot chips under fixed layout: a long line name used to run past the
   cell and clip raw at the table edge. The name is its own span so it
   can ellipsize INSIDE the chip (a flex button can't ellipsize a bare
   text node), the chip is capped at its column, and the last column
   keeps breathing room against the table's right border. The full name
   still reads from the chip's tooltip. */
/* Roaming (unpinned) hovercard: with several beats in one chapter,
   one beat's summary used to butt straight into the next beat's bold
   title. A thin rule + air separates each beat from the one before;
   the first beat sits directly under the line-name head with neither.
   (The pinned variant already separates beats — each is a bordered
   .outline-hover-beat button.) */
.outline-hovercard p.plot-hovercard-title {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--line-soft, var(--line));
}
.outline-hovercard .plot-hovercard-head + p.plot-hovercard-title {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}

.outline-plotchip { max-width: 100%; }
.outline-plotchip-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.outline-table td:last-child,
.outline-table thead th:last-child { padding-right: 18px; }

/* Each sticky th is a stacking context (position:sticky + z-index:3),
   which traps its tooltip UNDER the next header cell. Raising the
   hovered th lets its bubble paint over its siblings and the rows. */
.outline-table thead th:hover { z-index: 4; }
/* Hovering a grip sits inside a sortable th that carries its own tip —
   showing both at once is noise. The grip's tip wins. */
.outline-table thead th:has(.outline-col-grip:hover)::after { opacity: 0; }

/* Per-row ⓘ into the Inspector. Lives in the title cell (a fixed
   column — can never be hidden away by the Columns menu). Quiet at
   rest, revealed on row hover/selection like the empty-synopsis hint. */
.outline-insp-btn {
  border: 0;
  background: none;
  padding: 0 4px;
  font-size: 14px;
  line-height: 1;
  color: var(--accent);
  cursor: pointer;
  opacity: 0;
  transition: opacity .12s ease;
}
.outline-row:hover .outline-insp-btn,
.outline-row.is-selected .outline-insp-btn,
.outline-insp-btn:focus-visible { opacity: .85; }
.outline-insp-btn:hover { opacity: 1; }

/* The ≤980px tier hides the Inspector panel entirely (see the v2.50
   block) — so hide every way of opening it too: a button that opens
   nothing reads as broken. */
@media (max-width: 980px) {
  #outline-insp-toggle,
  .outline-insp-btn { display: none; }
}

/* ---------- Custom voice panel (read-aloud popover; chatterbox-engine.js) ---------- */
.tts-status { white-space: pre-line; font-variant-numeric: tabular-nums; }

/* ==========================================================
   Text-feature panels (v2.65): the ✳ menu and the per-block gear.

   All three live on document.body with position: fixed, placed from
   viewport rects. The ✳ panel cannot be a child of .ql-toolbar:
   relocateQuillToolbar() moves that toolbar into a
   `position: sticky; z-index: 6` host, which would clip it.
   ========================================================== */
/* ---- PDF import preview (v2.79) ----

   The list is the point of this screen. A PDF gives no chapters, no
   paragraphs and often no spaces, so what the reader found is a guess —
   showing it beside the first words of each chapter is the only way an
   author can tell a good split from a bad one before a book exists. */
.pdf-import-card {
  max-width: min(900px, 94vw);
  width: min(900px, 94vw);
  max-height: 82vh;
  display: flex;
  flex-direction: column;
}
.pdf-import-sub {
  margin: 4px 0 14px;
  font-family: var(--font-sans);
  font-size: 12.5px;
  color: var(--ink-3);
}
.pdf-import-bar {
  height: 6px;
  border-radius: 999px;
  background: var(--bg-rail);
  overflow: hidden;
}
.pdf-import-bar > span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--accent);
  transition: width .18s ease;
}
.pdf-import-note {
  margin: 8px 0 0;
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--ink-3);
}
.pdf-import-result {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
/* Warnings are not errors — they are the things the reader had to decide
   for itself. Amber, listed, and never hidden behind a disclosure: they are
   the difference between trusting this screen and being surprised later. */
.pdf-import-warnings {
  margin: 0 0 12px;
  padding: 10px 12px 10px 28px;
  list-style: disc;
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--bg-rail);
  font-family: var(--font-sans);
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-2);
}
.pdf-import-warnings:empty { display: none; }
.pdf-import-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-paper);
}
.pdf-import-row {
  display: flex;
  gap: 10px;
  align-items: baseline;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line);
}
.pdf-import-row:last-child { border-bottom: 0; }
.pdf-import-n {
  flex: 0 0 auto;
  min-width: 22px;
  font-family: var(--font-sans);
  font-size: 11px;
  color: var(--ink-3);
  text-align: right;
}
.pdf-import-ch { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pdf-import-ch strong { font-size: 13px; color: var(--ink-1); }
/* The opening words, so a chapter is recognisable at a glance. One line;
   a wrapped snippet turns the list into a wall and defeats the purpose. */
.pdf-import-ch em {
  font-size: 12px;
  font-style: normal;
  color: var(--ink-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---- Emoji & symbol picker (v2.75) ----
   Shares the panel chrome below but is its own element, because it is the
   only one of these that SCROLLS — the shared close handler is wired to
   window scroll in the capture phase, which would slam this shut the
   moment the author scrolled the grid. */
.emoji-pop {
  position: fixed;
  z-index: 220;
  width: 336px;
  padding: 8px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 12px 34px rgba(0,0,0,.24), 0 2px 8px rgba(0,0,0,.14);
}
.emoji-pop[hidden] { display: none; }
.emoji-head { display: flex; flex-direction: column; gap: 6px; }
.emoji-search {
  width: 100%;
  padding: 6px 9px;
  font: inherit;
  font-size: 13px;
  color: var(--ink-1);
  background: var(--bg-paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.emoji-filter {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-sans);
  font-size: 11.5px;
  color: var(--ink-3);
  cursor: pointer;
}
.emoji-tabs {
  display: flex;
  gap: 4px;
  margin: 8px 0 6px;
  padding-bottom: 4px;
  overflow-x: auto;
  border-bottom: 1px solid var(--line);
  scrollbar-width: thin;
}
.emoji-tab {
  flex: 0 0 auto;
  padding: 4px 9px;
  font-family: var(--font-sans);
  font-size: 11.5px;
  white-space: nowrap;
  color: var(--ink-2);
  background: none;
  border: 1px solid transparent;
  border-radius: 999px;
  cursor: pointer;
}
.emoji-tab:hover { background: var(--bg-rail); }
.emoji-tab.is-on { color: var(--ink-1); background: var(--accent-soft); border-color: var(--line); }
.emoji-grid {
  display: grid;
  /* minmax(0,…) so a wide glyph cannot push the track past the popup:
     without it the vertical scrollbar's width tips the row over and the
     grid grows a horizontal scrollbar as well. */
  grid-template-columns: repeat(8, minmax(0, 1fr));
  gap: 2px;
  max-height: 232px;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: thin;
}
.emoji-cell {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 19px;
  line-height: 1;
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.emoji-cell:hover { background: var(--accent-soft); border-color: var(--line); }
/* Dimmed means "this will not be in your paperback". Opacity rather than a
   badge: there can be several hundred on screen at once, and a badge on
   every other cell would be noise rather than a warning. The title
   attribute carries the words. */
.emoji-cell.is-screen-only { opacity: .42; }
.emoji-cell.is-screen-only:hover { opacity: 1; }
.emoji-empty,
.emoji-note {
  margin: 8px 2px 0;
  font-family: var(--font-sans);
  font-size: 11px;
  line-height: 1.4;
  color: var(--ink-3);
}
.emoji-note { padding-top: 6px; border-top: 1px solid var(--line); }

/* ---- Per-line role bar (v2.76) ----
   Three icons beside the hovered line: contact name, received, sent. It
   sits OUTSIDE the text column wherever there is room, because a control
   floating over the words is worse than no control; the JS tucks it just
   inside the block only when the margin is too narrow to hold it. */
.tf-rolebar {
  position: fixed;
  z-index: 221;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 2px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: 7px;
  box-shadow: 0 4px 14px rgba(0,0,0,.16);
}
.tf-rolebar[hidden] { display: none; }
.tf-rolebar-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 20px;
  padding: 0;
  color: var(--ink-3);
  background: none;
  border: 0;
  border-radius: 4px;
  cursor: pointer;
}
.tf-rolebar-btn:hover { color: var(--ink-1); background: var(--bg-rail); }
/* The current role, so the bar reads as state rather than as three
   identical buttons you have to guess between. */
.tf-rolebar-btn.is-on { color: var(--accent); background: var(--accent-soft); }

.tf-menu,
.tf-gear-pop {
  position: fixed;
  z-index: 220;
  min-width: 262px;
  max-width: 340px;
  /* Fourteen entries outgrow a laptop window. Without this the panel
     ran past the bottom edge and its last items (Footnote, Citation)
     were unreachable — and looked, to the author, like they did not
     exist. tfPlace() sets the exact max-height for the space left. */
  max-height: calc(100vh - 16px);
  overflow-y: auto;
  padding: 6px;
  background: var(--bg-card);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 12px 34px rgba(0,0,0,.24), 0 2px 8px rgba(0,0,0,.14);
}
/* Two columns once the window can hold them. A group is one block and
   never splits across columns, so a heading is never orphaned from its
   entries. Below the breakpoint the single column scrolls instead. */
.tf-menu-group { break-inside: avoid; page-break-inside: avoid; }
@media (min-width: 900px) {
  .tf-menu { width: 660px; max-width: 660px; column-count: 2; column-gap: 10px; }
}
.tf-menu[hidden],
.tf-gear-pop[hidden],
.tf-gear-btn[hidden] { display: none; }
.tf-menu-head,
.tf-gear-sub {
  padding: 8px 10px 4px;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--ink-3);
}
.tf-menu-item,
.tf-gear-pop button {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  width: 100%;
  padding: 7px 10px;
  border: 0;
  border-radius: 7px;
  background: none;
  font: inherit;
  font-size: 13px;
  text-align: left;
  color: var(--ink-1);
  cursor: pointer;
}
.tf-menu-item:hover,
.tf-gear-pop button:hover { background: var(--accent-soft); }
.tf-menu-glyph {
  flex: 0 0 20px;
  font-size: 15px;
  line-height: 1.35;
  color: var(--accent);
  text-align: center;
}
.tf-menu-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tf-menu-text strong { font-size: 13.5px; font-weight: 600; }
.tf-menu-text span { font-size: 11.5px; line-height: 1.4; color: var(--ink-3); }
.tf-menu-empty { padding: 12px; font-size: 12.5px; color: var(--ink-3); }

.tf-gear-title {
  padding: 6px 10px 7px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--line);
  font-size: 13px;
  font-weight: 700;
  color: var(--ink-1);
}
.tf-gear-pop button { flex-wrap: wrap; }
.tf-gear-tick {
  flex: 0 0 14px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--accent);
}
.tf-gear-note {
  flex: 0 0 100%;
  padding-left: 23px;
  font-size: 11px;
  line-height: 1.4;
  color: var(--ink-3);
}
.tf-gear-rule { height: 1px; margin: 5px 8px; background: var(--line); }
.tf-gear-pop button.is-on { font-weight: 600; }

/* One body-level gear repositioned onto whatever text feature the pointer
   is over — same architecture as the scene-break and figure floaters, and
   the reason a saved chapter contains no toolbar markup. */
.tf-gear-btn {
  position: fixed;
  z-index: 210;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--bg-card);
  box-shadow: var(--shadow-1);
  font-size: 12px;
  line-height: 1;
  color: var(--ink-2);
  cursor: pointer;
}
.tf-gear-btn:hover { color: var(--accent); border-color: var(--accent); }

/* ==========================================================================
   KDP tab v3.1 — the four-step flow
   ① Keywords → ② Categories → ③ Description → ④ Publish & track
   ========================================================================== */

/* ---- step rail ---- */
.kdp-steps {
  list-style: none; margin: 0 0 22px; padding: 0;
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px;
}
.kdp-step {
  width: 100%; display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; font: inherit; text-align: left; cursor: pointer;
  background: var(--bg-card); border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--ink-3); transition: border-color .12s, background .12s, color .12s;
}
.kdp-step:hover { border-color: var(--ink-4); color: var(--ink-1); }
.kdp-step.is-active { border-color: var(--accent); background: var(--accent-bg); color: var(--ink-1); }
.kdp-step-num {
  flex-shrink: 0; width: 24px; height: 24px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 700; background: var(--bg-rail); color: var(--ink-2);
}
.kdp-step.is-active .kdp-step-num { background: var(--accent); color: #fff; }
.kdp-step[data-tone="live"] .kdp-step-num { background: var(--success); color: #fff; }
.kdp-step-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.kdp-step-label { font-size: 13px; font-weight: 600; white-space: nowrap; }
.kdp-step-state { font-size: 11px; color: var(--ink-4); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kdp-step[data-tone="live"] .kdp-step-state { color: var(--success); }
.kdp-step[data-tone="draft"] .kdp-step-state { color: var(--kdp-near, #8b6914); }
.kdp-step-mark { margin-left: auto; font-size: 13px; color: var(--ink-4); }
.kdp-step[data-tone="live"] .kdp-step-mark { color: var(--success); font-weight: 700; }
.kdp-step[data-tone="draft"] .kdp-step-mark { color: var(--kdp-near, #8b6914); }
@media (max-width: 900px) {
  .kdp-steps { grid-template-columns: repeat(2, 1fr); }
}

/* ---- step body ---- */
.kdp-step-intro { margin: 0 0 14px; }
.kdp-step-lede { margin: 0; font-size: 14px; line-height: 1.55; color: var(--ink-2); }
.kdp-step-foot {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--line-soft);
}

/* ---- Step 1 workbench (3.35): sticky slots card left, tabbed helpers
   right. Mirrors .kdpd-workbench (the description pane): same 2-col
   grid, same 980px collapse; sticky works against main.publishing-main,
   the overflow-y scroller. Card recipe from .kdp-rocket-inline. ---- */
.kdpk-workbench {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 16px;
  align-items: start;   /* required for sticky in a grid child */
  /* no overflow here — an overflow on the grid would detach sticky
     from the .publishing-main scroller */
}
.kdpk-editor-col {
  position: sticky;
  top: 12px;
  /* Cap + internal scroll: a 7-slot card taller than the viewport
     scrolls inside itself instead of pinning its bottom out of reach. */
  max-height: calc(100vh - 110px);
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-card);
  padding: 14px 16px 16px;
}
.kdpk-side-col {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-card);
  /* this padding replaces the side padding #kdp-suggestions had from
     .kdp-rocket-inline > :not(summary) before it moved out (3.35) */
  padding: 14px 16px 16px;
}
/* Same pill pattern as .bible-pane-tab (and the retired .kdp-pane-tab). */
.kdpk-side-tabs { display: flex; gap: 6px; margin-bottom: 14px; }
.kdpk-side-tab {
  font: inherit;
  font-size: 13px;
  padding: 6px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg-card);
  color: var(--ink-3);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.kdpk-side-tab:hover { background: var(--bg-rail); color: var(--ink-1); }
.kdpk-side-tab.is-active {
  background: var(--accent-bg);
  border-color: var(--accent-soft);
  color: var(--ink-1);
  font-weight: 600;
}
/* Panel-context resets: these sections were designed for the long-scroll
   layout and carry margins/borders that fight the card. */
.kdpk-side-panel > .kdp-suggestions { border-top: none; padding-top: 0; padding-bottom: 0; }
.kdpk-side-panel > .kdp-ideas { margin-top: 0; }
/* Flatten the rocket details: its tab panel is already the card. The
   form/job/ads/runs STAY inside the details so the > :not(summary)
   rules below keep working — only the doubled chrome is zeroed. */
.kdpk-side-panel > .kdp-rocket-inline { margin-top: 0; border: none; background: transparent; }
.kdpk-side-panel > .kdp-rocket-inline > :not(summary) { padding-left: 0; padding-right: 0; }
.kdpk-side-panel .kdp-rocket-summary { padding-left: 0; padding-right: 0; }
.kdpk-side-panel .kdp-rocket-inline .kdp-rocket-job { margin: 0 0 14px; }
/* The intro/guide sit above the 1200px grid — cap the guide's measure
   so its prose lines don't run the full width. */
#kdp-pane-keywords .kdp-guide { max-width: 860px; }
@media (max-width: 980px) {   /* match .kdpd-workbench's collapse */
  .kdpk-workbench { grid-template-columns: 1fr; }
  .kdpk-editor-col { position: static; max-height: none; overflow-y: visible; }
}

/* ---- Rocket helper inside step 1 ---- */
.kdp-rocket-inline {
  margin-top: 26px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg-card);
}
.kdp-rocket-summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; padding: 12px 16px;
}
.kdp-rocket-summary::-webkit-details-marker { display: none; }
.kdp-rocket-summary::before { content: "▸"; color: var(--ink-4); margin-right: 8px; }
.kdp-rocket-inline[open] .kdp-rocket-summary::before { content: "▾"; }
.kdp-rocket-inline[open] .kdp-rocket-summary { border-bottom: 1px solid var(--line-soft); }
.kdp-rocket-summary-title { font-size: 14px; font-weight: 600; color: var(--ink-1); flex: 1; }
.kdp-rocket-summary-meta { display: inline-flex; align-items: center; gap: 8px; font-size: 12px; color: var(--ink-3); }
.kdp-rocket-inline > :not(summary) { padding-left: 16px; padding-right: 16px; }
.kdp-rocket-inline .kdp-rocket-form { border-top: none; padding-top: 14px; }
.kdp-rocket-inline .kdp-rocket-job { margin: 0 16px 14px; }
.kdp-rocket-form-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.kdp-rocket-setup { padding-top: 14px; padding-bottom: 6px; border-bottom: 1px solid var(--line-soft); }
.kdp-rocket-setup-actions { display: flex; gap: 8px; flex-wrap: wrap; margin: 6px 0 12px; }
.kdp-rocket-foot { margin: 6px 0 14px; font-size: 12px; color: var(--ink-4); }
.kdp-rocket-foot-sep { margin: 0 6px; }
.kdp-rocket-ads, .kdp-rocket-runs { padding-top: 10px; padding-bottom: 12px; border-top: 1px solid var(--line-soft); }
/* The two folded-away sections open from a real button, not a bare line. */
.kdp-rocket-more {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 8px;
  font: inherit; font-size: 13px; font-weight: 500; color: var(--ink-1);
  padding: 7px 14px; border: 1px solid var(--line); border-radius: 999px; background: var(--bg-rail);
  transition: border-color .12s, background .12s;
}
.kdp-rocket-more::-webkit-details-marker { display: none; }
.kdp-rocket-more::before { content: "▸"; color: var(--ink-3); font-size: 11px; }
details[open] > .kdp-rocket-more::before { content: "▾"; }
.kdp-rocket-more:hover { border-color: var(--accent); background: var(--accent-bg); }
.kdp-rocket-ads > :not(summary), .kdp-rocket-runs > :not(summary) { margin-top: 10px; }
.kdp-rocket-runs .publishing-table-wrap { margin-top: 8px; }

/* ---- Rocket reports (3.31) — inline PDF viewers above the step panes ---- */
.kdp-reports { margin: 2px 0 16px; }
.kdp-reports > summary {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 8px;
  font-size: 13px; font-weight: 500; color: var(--ink-1);
  padding: 7px 14px; border: 1px solid var(--line); border-radius: 999px; background: var(--bg-rail);
  transition: border-color .12s, background .12s;
}
.kdp-reports > summary::-webkit-details-marker { display: none; }
.kdp-reports > summary::before { content: "▸"; color: var(--ink-3); font-size: 11px; }
.kdp-reports[open] > summary::before { content: "▾"; }
.kdp-reports > summary:hover { border-color: var(--accent); background: var(--accent-bg); }
.kdp-reports-count { color: var(--ink-3); font-weight: 400; }
.kdp-reports-list { margin-top: 10px; display: flex; flex-direction: column; gap: 8px; }
.kdp-report { border: 1px solid var(--line-soft); border-radius: 10px; padding: 8px 12px; background: var(--bg-card, #fff); }
.kdp-report > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 8px; font-size: 13.5px; font-weight: 500; }
.kdp-report > summary::-webkit-details-marker { display: none; }
.kdp-report-when { color: var(--ink-3); font-weight: 400; font-size: 12px; }
.kdp-report-tools { margin: 8px 0 0; font-size: 12.5px; color: var(--ink-3); display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* A labeled open-in-new-tab BUTTON — deliberately not .snap-link,
   which is a fixed 26px icon box and shreds any text label. */
.kdp-report-open {
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  font-size: 12.5px; font-weight: 500; color: var(--ink-2); text-decoration: none;
  padding: 4px 10px; border: 1px solid var(--line); border-radius: 999px; background: var(--bg-card, #fff);
  transition: border-color .12s, background .12s, color .12s;
}
.kdp-report-open:hover { border-color: var(--accent); background: var(--accent-bg); color: var(--ink-1); }
.kdp-report-also { color: var(--ink-3); }
.kdp-report-frame { width: 100%; height: 560px; border: 1px solid var(--line-soft); border-radius: 8px; margin-top: 8px; background: var(--bg-rail); }

/* ---- suggestions list ---- */
.kdp-suggestions { padding-top: 14px; padding-bottom: 14px; border-top: 1px solid var(--line-soft); }
.kdp-suggestions-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.kdp-suggestions-head-tools { display: inline-flex; align-items: center; gap: 12px; }
.kdp-suggestions-head input {
  font: inherit; font-size: 12.5px; padding: 5px 10px; width: 180px; max-width: 100%;
  border: 1px solid var(--line); border-radius: 999px; background: var(--bg-card); color: var(--ink-1);
}
.kdp-suggestions-list { margin-top: 6px; }
.kdp-sug-group-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.kdp-sug-group {
  font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-4); margin: 14px 0 4px; font-weight: 600;
}
.kdp-sug-when { text-transform: none; letter-spacing: 0; font-weight: 400; font-style: italic; }
.kdp-sug-row {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 7px 8px; border-radius: var(--radius-sm); border-bottom: 1px solid var(--line-soft);
}
.kdp-sug-row:hover { background: var(--bg-rail); }
.kdp-sug-row-slot { background: var(--accent-bg); }
.kdp-sug-row-slot .kdp-sug-phrase { font-family: var(--font-mono, ui-monospace, Menlo, Consolas, monospace); font-size: 12px; }
.kdp-sug-phrase { font-size: 13px; font-weight: 500; color: var(--ink-1); }
.kdp-sug-row.is-used .kdp-sug-phrase { color: var(--ink-3); }
.kdp-sug-used { font-size: 11px; color: var(--success); }
.kdp-sug-full { font-size: 11px; color: var(--ink-3); padding: 4px 10px; white-space: nowrap; }
/* Rocket value chips (208): est. searches/mo + difficulty tier, same
   bands as the companion PDF's score bar (<30 easy, <60 moderate). */
.kdp-sug-metrics { display: inline-flex; gap: 4px; align-items: center; margin-left: 6px; flex: 0 0 auto; }
.kdp-sug-chip {
  font-size: 10.5px;
  line-height: 1;
  padding: 3px 7px;
  border-radius: 999px;
  background: var(--bg-rail, #f0eee8);
  color: var(--ink-3);
  white-space: nowrap;
}
.kdp-sug-chip--easy     { background: var(--success-soft, #e5f2e5); color: var(--success, #2e7d32); }
.kdp-sug-chip--moderate { background: var(--warn-soft, #f7efd8);    color: var(--warn, #8a6d1a); }
.kdp-sug-chip--hard     { background: var(--danger-soft, #f7e2de);  color: var(--danger, #a93b2a); }
.kdp-sug-fullnote { margin-top: 10px; }
.kdp-sug-actions { margin-left: auto; display: inline-flex; gap: 10px; align-items: center; }
.kdp-sug-slotbtn {
  font: inherit; font-size: 12px; font-weight: 600; cursor: pointer; white-space: nowrap;
  padding: 4px 10px; border-radius: 999px; border: 1px solid var(--accent-soft);
  background: var(--bg-card); color: var(--accent);
}
.kdp-sug-slotbtn:hover { background: var(--accent-bg); }
.kdp-sug-slotbtn.is-in { background: var(--success); border-color: var(--success); color: #fff; }
.kdp-sug-slotbtn.is-in:hover { background: var(--danger, #c0392b); border-color: var(--danger, #c0392b); }
.kdp-sug-empty { font-size: 12.5px; color: var(--ink-4); padding: 10px 0; line-height: 1.6; }

/* ---- Keyword ideas (3.33) — the built-in, everyone-sees-it panel.
   Rows reuse the .kdp-sug-row anatomy (each idea row carries both
   classes), so only the section chrome and the bucket chip are new. */
.kdp-ideas { margin-top: 22px; }
.kdp-ideas-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.kdp-ideas-title { font-size: 14px; font-weight: 700; color: var(--ink-1); margin: 0; }
.kdp-ideas-sub { font-size: 12.5px; color: var(--ink-4); margin: 4px 0 8px; line-height: 1.6; }
.kdp-ideas-note { font-size: 12px; color: var(--warn, #8a6d1a); margin: 0 0 8px; line-height: 1.5; }
.kdp-idea-bucket {
  font-size: 10px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--ink-4); background: var(--bg-rail, #f0eee8);
  padding: 3px 7px; border-radius: 999px; white-space: nowrap; flex: 0 0 auto;
  min-width: 56px; text-align: center;
}

/* ---- step 4: publish & track ---- */
.kdp-pub { margin-top: 24px; }
.kdp-pub:first-child { margin-top: 0; }
.kdp-pub-rows { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.kdp-pub-row {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg-card);
}
.kdp-pub-mark { width: 22px; text-align: center; font-size: 14px; color: var(--ink-4); }
.kdp-pub-row[data-tone="live"] .kdp-pub-mark { color: var(--success); font-weight: 700; }
.kdp-pub-row[data-tone="draft"] .kdp-pub-mark { color: var(--kdp-near, #8b6914); }
.kdp-pub-main { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.kdp-pub-label { font-size: 13.5px; font-weight: 600; color: var(--ink-1); }
.kdp-pub-state { font-size: 12.5px; color: var(--ink-3); }
.kdp-pub-actions { display: inline-flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.kdp-checkin-actions { align-items: flex-end; }
.kdp-checkin-snooze { margin: 0; }
.kdp-checkin-snooze input { font-size: 12.5px; padding: 4px 8px; }
.kdp-checkin-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.kdp-checkin-chip {
  font: inherit; font-size: 13px; padding: 6px 12px; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--bg-card); border: 1px solid var(--line); border-radius: 999px; color: var(--ink-1);
}
.kdp-checkin-chip:hover { border-color: var(--accent); background: var(--accent-bg); }
.kdp-history-wrap { margin-top: 28px; }
.kdp-history-wrap > summary { list-style: none; cursor: pointer; font-size: 13px; }
.kdp-history-wrap > summary::-webkit-details-marker { display: none; }
.kdp-history-wrap > summary::before { content: "▸ "; color: var(--ink-4); }
.kdp-history-wrap[open] > summary::before { content: "▾ "; }
.kdp-history-wrap .kdp-history { margin-top: 18px; }
.kdp-history-title { font-size: 12px; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-3); margin: 0 0 8px; }
.kdpc-shown { margin-top: 24px; }

/* ============================================================
   Admin → Users (3.4): summary strip, toolbar, sortable table,
   activity tones, and the per-user page.
   ============================================================ */
.admin-users-stats { margin-bottom: 18px; }
.admin-users-stats .admin-stat { padding: 14px 16px 12px; }
.admin-users-stats .admin-stat-num { font-size: 26px; }
.admin-users-strip-note { margin: -8px 0 16px; font-size: 12px; color: var(--ink-3); }
.admin-stat-note { display: block; font-size: 11.5px; font-weight: 400; color: var(--ink-3); margin-top: 2px; }

.admin-users-toolbar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-bottom: 14px;
}
.admin-users-search { flex: 0 1 260px; min-width: 180px; }
.admin-users-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.users-chip {
  display: inline-flex; align-items: center; gap: 6px;
  font: inherit; font-size: 12.5px; font-weight: 500; color: var(--ink-2);
  padding: 5px 10px 5px 12px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--bg-card); cursor: pointer; transition: border-color .12s, background .12s, color .12s;
}
.users-chip:hover { border-color: var(--accent); color: var(--ink-1); }
.users-chip.is-active { background: var(--accent); border-color: var(--accent); color: #fff; }
.users-chip-n {
  font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums;
  padding: 1px 7px; border-radius: 999px; background: var(--bg-rail); color: var(--ink-3);
}
.users-chip.is-active .users-chip-n { background: rgba(255,255,255,.22); color: #fff; }
.admin-users-toolbar-right { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.admin-users-showing { font-size: 12.5px; color: var(--ink-3); white-space: nowrap; }
.admin-users-empty { margin: 14px 0 0; color: var(--ink-3); font-size: 13.5px; }

/* Sortable headers — the Outline's arrows, on the admin table. */
.admin-th-sort { cursor: pointer; user-select: none; white-space: nowrap; }
.admin-th-sort:hover { color: var(--accent); }
.admin-th-sort.is-sorted-asc  .admin-sort-arrow::after { content: '▲'; font-size: 9px; margin-left: 4px; }
.admin-th-sort.is-sorted-desc .admin-sort-arrow::after { content: '▼'; font-size: 9px; margin-left: 4px; }
.admin-th-sort.is-sorted-asc, .admin-th-sort.is-sorted-desc { color: var(--accent); }

/* The merged User cell: name line over email line. */
.admin-users-table .admin-user-cell { min-width: 240px; }
.admin-user-name { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.admin-user-link { font-weight: 600; color: var(--ink-1); text-decoration: none; }
.admin-user-link:hover { color: var(--accent); text-decoration: underline; }
.admin-user-display { color: var(--ink-3); font-size: 12.5px; }
.admin-user-email { margin-top: 3px; font-size: 12.5px; color: var(--ink-2); display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.admin-users-table tbody tr[hidden] { display: none; }

/* "3 days ago" with a tone, and a muted second line. */
.admin-age-cell { white-space: nowrap; }
.admin-age { display: block; font-weight: 600; font-variant-numeric: tabular-nums; }
.admin-age.is-fresh { color: var(--success); }
.admin-age.is-stale { color: #b5731a; }
:root[data-theme="dark"] .admin-age.is-stale { color: #d9a04a; }
.admin-age.is-gone  { color: var(--ink-3); font-weight: 500; }
.admin-age-sub { display: block; font-size: 11.5px; color: var(--ink-3); }
.admin-age-quiet { color: var(--danger); font-weight: 600; text-transform: uppercase; letter-spacing: .06em; font-size: 10.5px; }
.admin-glance .admin-age, .admin-glance .admin-age-sub { display: inline; }

/* Per-user page: at-a-glance + account side by side, the rest full width. */
.admin-user-show { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 18px; align-items: start; }
.admin-user-show > .admin-card { margin: 0; }
.admin-user-show > .admin-card:nth-child(n+3) { grid-column: 1 / -1; }
.admin-glance { margin: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px 18px; }
.admin-glance > div { display: flex; flex-direction: column; gap: 3px; }
.admin-glance dt { font-size: 10.5px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); }
.admin-glance dd { margin: 0; font-size: 13.5px; color: var(--ink-1); }
.admin-user-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line-soft); }
.admin-user-actions .admin-user-delete { margin-left: auto; }
/* The action row's buttons read as real buttons, not links — same pill
   treatment as the KDP tab's "Also harvest…" / "How X works" pills. */
.admin-user-actions .btn-ghost {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; font-weight: 500; color: var(--ink-1);
  padding: 7px 14px; border: 1px solid var(--line); border-radius: 999px; background: var(--bg-rail);
  transition: border-color .12s, background .12s;
}
.admin-user-actions .btn-ghost:hover { border-color: var(--accent); background: var(--accent-bg); color: var(--ink-1); }
.admin-user-actions .admin-user-internal .btn-ghost { border-style: dashed; }
.admin-table tr.is-muted td { color: var(--ink-3); }
.admin-timeline { list-style: none; margin: 0; padding: 0; }
.admin-timeline-item { display: grid; grid-template-columns: 150px minmax(0, 1fr); gap: 4px 14px; padding: 7px 0; border-top: 1px solid var(--line-soft); font-size: 13px; }
.admin-timeline-item:first-child { border-top: none; }
.admin-timeline-when { color: var(--ink-3); font-variant-numeric: tabular-nums; white-space: nowrap; }
.admin-timeline-what { color: var(--ink-1); font-weight: 500; }
.admin-timeline-item.is-session .admin-timeline-what { color: var(--accent); }
.admin-timeline-meta { grid-column: 2; color: var(--ink-3); font-size: 12px; }
@media (max-width: 900px) {
  .admin-user-show { grid-template-columns: 1fr; }
  .admin-user-show > .admin-card { grid-column: 1 / -1; }
  .admin-timeline-item { grid-template-columns: 1fr; }
  .admin-timeline-meta { grid-column: 1; }
}

/* ------------------------------------------------------------
   Admin tabbed pages + dashboard charts (3.6, generic since 3.7):
   .admin-dash-tabs / .dash-pane are the classes for ANY admin page's
   client-side tab strip (names kept from the dashboard, their first
   home, for back-compat) — the strip reuses .admin-tab-strip (written
   for <a>; the reset below makes the same look work on <button>) and
   panes swap via admin-tabs.js ([data-admin-tabs]). The charts are the
   dashboard's: server-rendered SVG (partials/admin_bars.php) drawing
   in currentColor so the dark theme recolors them from here alone.
   ------------------------------------------------------------ */
.admin-dash-tabs { flex-wrap: wrap; }
.admin-dash-tabs .admin-tab-link {
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
}
.admin-dash-tabs .admin-tab-link.is-active { border-bottom-color: var(--accent); }
.dash-pane[hidden] { display: none; }

.admin-chart-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 16px;
}
.admin-chart-grid .admin-card { margin: 0; }
.admin-chart-grid + .admin-recent-grid { margin-top: 18px; }
.admin-bars { display: block; width: 100%; height: 64px; color: var(--accent); margin: 6px 0 8px; }
.admin-bars rect { fill: currentColor; }
.admin-bars rect.is-zero { opacity: .3; }
.admin-bars-axis { stroke: var(--line-soft); stroke-width: 1; }

/* Funnel: label / count / bar / step-conversion, one row per stage. */
.admin-funnel { display: flex; flex-direction: column; gap: 10px; margin: 8px 0 12px; }
.admin-funnel-row {
  display: grid;
  grid-template-columns: minmax(120px, 160px) 3.5em minmax(0, 1fr) 3.5em;
  gap: 10px;
  align-items: center;
  font-size: 13px;
}
.admin-funnel-label { color: var(--ink-1); font-weight: 500; }
.admin-funnel-n { text-align: right; font-variant-numeric: tabular-nums; color: var(--ink-2); }
.admin-funnel-track { display: block; height: 8px; border-radius: 4px; background: var(--line-soft); overflow: hidden; }
.admin-funnel-bar { display: block; height: 100%; border-radius: 4px; background: var(--accent); }
.admin-funnel-pct { text-align: right; font-variant-numeric: tabular-nums; color: var(--ink-3); font-size: 12px; }

.admin-revenue-headline { border: none; padding: 0; margin: 4px 0 10px; background: none; }
.admin-revenue-lines { list-style: none; margin: 0; padding: 10px 0 0; border-top: 1px solid var(--line-soft); font-size: 13px; color: var(--ink-2); display: flex; flex-direction: column; gap: 5px; }
.admin-chart-footnote { margin: 10px 0 0; font-size: 12px; color: var(--ink-3); }
@media (max-width: 900px) {
  .admin-funnel-row { grid-template-columns: minmax(90px, 120px) 3em minmax(0, 1fr) 3em; }
}

/* Overview: four linked metrics, with detailed reports on their own pages. */
.admin-overview-glance { margin-bottom: 22px; }
.admin-overview-metrics { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; }
.admin-overview-metric { color: inherit; text-decoration: none; min-width: 0; }
.admin-overview-metric:hover { border-color: var(--accent); }
.admin-overview-metric:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.admin-overview-metric .admin-stat-label { display: flex; justify-content: space-between; gap: 8px; }
.admin-overview-timeline li { display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.admin-overview-timeline .admin-recent-meta { margin-top: 0; }
@media (max-width: 1000px) { .admin-overview-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .admin-overview-metrics { grid-template-columns: 1fr; } }
