:root {
  --bg: #f7f7f5;
  --surface: #ffffff;
  --surface-soft: #ededeb;
  --surface-dark: #151719;
  --surface-dark-2: #202326;
  --text: #202124;
  --muted: #6f7378;
  --line: #dededa;
  --accent: #2563eb;
  --accent-strong: #1d4ed8;
  --ready: #15803d;
  --pending: #a16207;
  --danger: #b42318;
  /* Folder (series/collection) identity — a warm manila that sets containers
     apart from the blue-accented single-work cards while staying in palette. */
  --folder-accent: #b07d2a;
  --folder-accent-strong: #8f6418;
  --folder-surface: #fbf7ee;
  --folder-line: #e9ddc6;
  --folder-edge: #d4b878;
  --folder-sheet: #f4ead4;
  --shadow: 0 20px 70px rgba(15, 18, 22, 0.18);
  --radius: 8px;
  --space-1: 6px;
  --space-2: 10px;
  --space-3: 16px;
  --space-4: 24px;
  --space-5: 36px;
  --max: 1180px;
}

* {
  box-sizing: border-box;
}

/* Never allow horizontal scroll / a right-side gutter anywhere. `clip` (not `hidden`)
   so it does not turn the root into a scroll container and break sticky headers. */
html {
  overflow-x: clip;
  max-width: 100%;
}

body {
  margin: 0;
  max-width: 100%;
  overflow-x: clip;
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 16px;
  line-height: 1.5;
}

a {
  color: inherit;
  text-decoration: none;
}

button,
input,
select {
  font: inherit;
}

button {
  cursor: pointer;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

.viewer-header {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 56px;
  padding: 10px max(var(--space-3), calc((100vw - var(--max)) / 2));
  background: rgba(247, 247, 245, 0.9);
  border-bottom: 1px solid var(--line);
  backdrop-filter: blur(12px);
}

.viewer-brand {
  font-weight: 700;
}

.viewer-nav {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--muted);
  font-size: 14px;
}

.maintenance-reminder {
  position: fixed;
  top: calc(env(safe-area-inset-top, 0px) + 64px);
  left: 50%;
  z-index: 45;
  display: flex;
  align-items: center;
  gap: 10px;
  width: min(680px, calc(100vw - 24px));
  padding: 10px 14px;
  transform: translateX(-50%);
  border: 1px solid rgba(161, 98, 7, 0.26);
  border-radius: var(--radius);
  color: #4b3510;
  background: rgba(255, 250, 235, 0.96);
  box-shadow: 0 8px 28px rgba(40, 32, 18, 0.16);
  pointer-events: none;
}

.maintenance-reminder-icon {
  flex: 0 0 auto;
  font-size: 21px;
  line-height: 1;
}

.maintenance-reminder-copy {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 1px;
  font-size: 13px;
  line-height: 1.35;
}

.maintenance-reminder-copy strong {
  font-size: 14px;
}

.viewer-language-select {
  min-height: 32px;
  padding: 4px 28px 4px 9px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--text);
  background: var(--surface);
}

.reader-chrome-toggle {
  position: fixed;
  top: max(4px, env(safe-area-inset-top));
  left: 5px;
  z-index: 70;
  display: none;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.24);
  border-radius: 4px;
  color: rgba(255, 255, 255, 0.96);
  background: rgba(21, 23, 25, 0.78);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
  font-size: 11px;
  line-height: 1;
  opacity: 1;
}

.viewer-reader-page .reader-chrome-toggle {
  display: inline-flex;
}

.reader-chrome-toggle:hover,
.reader-chrome-toggle:focus-visible {
  color: rgba(35, 40, 45, 0.9);
  background: rgba(255, 255, 255, 0.58);
  opacity: 1;
}

.viewer-reader-page:not(.reader-chrome-collapsed) .viewer-header {
  padding-left: max(42px, var(--space-3), calc((100vw - var(--max)) / 2));
}

.viewer-reader-page.reader-chrome-collapsed .viewer-header,
.viewer-reader-page.reader-chrome-collapsed .reader-header {
  display: none;
}

.viewer-reader-page.reader-chrome-collapsed .reader-root {
  min-height: 100vh;
}

.viewer-reader-page.reader-chrome-collapsed .reader-chrome-toggle {
  color: rgba(25, 29, 33, 0.94);
  background: rgba(247, 247, 245, 0.84);
  border-color: rgba(20, 22, 24, 0.2);
}

.viewer-reader-page.reader-chrome-collapsed .reader-chrome-toggle:hover,
.viewer-reader-page.reader-chrome-collapsed .reader-chrome-toggle:focus-visible {
  color: rgba(255, 255, 255, 0.92);
  background: rgba(20, 22, 24, 0.55);
}

.viewer-reader-page.reader-chrome-toggle-idle .reader-chrome-toggle {
  border-color: transparent;
  color: rgba(70, 76, 82, 0.56);
  background: transparent;
  box-shadow: none;
  opacity: 0.72;
}

.viewer-reader-page.reader-chrome-collapsed.reader-chrome-toggle-idle .reader-chrome-toggle {
  color: rgba(230, 234, 238, 0.5);
  background: transparent;
  border-color: transparent;
}

.viewer-reader-page.reader-chrome-toggle-idle .reader-chrome-toggle:hover,
.viewer-reader-page.reader-chrome-toggle-idle .reader-chrome-toggle:focus-visible {
  border-color: rgba(255, 255, 255, 0.24);
  color: rgba(255, 255, 255, 0.96);
  background: rgba(21, 23, 25, 0.78);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
  opacity: 1;
}

.viewer-reader-page.reader-chrome-collapsed .reader-size-panel {
  top: 44px;
}

.viewer-reader-page.reader-chrome-collapsed .reader-prepare-notice {
  top: 40px;
}

.viewer-root {
  width: min(var(--max), calc(100vw - 28px));
  margin: 0 auto;
  padding: var(--space-4) 0 var(--space-5);
}

.viewer-page-title {
  position: relative;
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
  flex-wrap: wrap;
}

/* The title block (eyebrow + name + status) may shrink so the actions keep room. */
.viewer-page-title > :first-child {
  min-width: 0;
}

.viewer-page-title h1 {
  margin: 0;
  font-size: clamp(28px, 4vw, 44px);
  line-height: 1.05;
  letter-spacing: 0;
}

.viewer-member-state {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 8px 0 0;
}

.member-status {
  display: inline-flex;
  align-items: center;
  min-height: 26px;
  padding: 3px 9px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-size: 13px;
  font-weight: 650;
}

.member-status.active {
  color: var(--ready);
  background: #edf8f0;
  border-color: #b9dfc4;
}

.member-status.inactive {
  color: var(--muted);
  background: var(--surface-soft);
}

.eyebrow {
  margin: 0 0 4px;
  color: var(--muted);
  font-size: 13px;
  letter-spacing: 0;
}

.primary-button,
.secondary-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 42px;
  padding: 9px 14px;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--text);
}

.primary-button {
  border-color: var(--accent);
  background: var(--accent);
  color: #fff;
}

.primary-button:hover {
  background: var(--accent-strong);
}

.secondary-button:hover {
  border-color: #c7c7c3;
  background: #fdfdfc;
}

.state-panel,
.account-panel {
  width: min(560px, 100%);
  margin: var(--space-5) auto;
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.state-panel h1,
.account-panel h2 {
  margin: 0 0 var(--space-2);
  font-size: 24px;
  letter-spacing: 0;
}

.state-panel p,
.account-panel p {
  color: var(--muted);
}

.state-panel.error h1 {
  color: var(--danger);
}

.maintenance-message {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.state-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.state-spinner {
  width: 26px;
  height: 26px;
  margin-bottom: var(--space-3);
  border: 3px solid var(--line);
  border-top-color: var(--accent);
  border-radius: 999px;
  animation: spin 0.9s linear infinite;
}

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

.library-search {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.search-input {
  position: relative;
  flex: 1;
}

.search-input input {
  width: 100%;
  min-height: 48px;
  padding: 10px 76px 10px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
}

.search-input button {
  position: absolute;
  top: 7px;
  right: 7px;
  min-height: 34px;
  padding: 5px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-soft);
  color: var(--muted);
}

.library-count {
  white-space: nowrap;
  color: var(--muted);
  font-size: 14px;
}

#library-grid-root[aria-busy="true"] {
  opacity: 0.62;
  pointer-events: none;
  transition: opacity 120ms ease;
}

.pagination-nav-wrap {
  display: flex;
  justify-content: center;
  width: 100%;
  padding: 40px 0 24px;
}

.pagination-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: 48px;
  padding: 0 8px;
  color: #666765;
}

.pagination-number,
.pagination-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  color: #666765;
  background: transparent;
  font-size: 16px;
  font-weight: 650;
  line-height: 1;
  text-decoration: none;
  transition: color 120ms ease, background 120ms ease, transform 120ms ease;
}

.pagination-number:hover,
.pagination-number:focus-visible,
.pagination-arrow:hover,
.pagination-arrow:focus-visible {
  color: #232524;
  background: rgba(105, 106, 105, 0.09);
  outline: none;
}

.pagination-number[aria-current="page"] {
  color: #fff;
  background: #696a69;
}

.pagination-arrow svg {
  width: 22px;
  height: 22px;
}

.library-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3);
}

.work-card-shell {
  position: relative;
  display: flex;
  min-width: 0;
  isolation: isolate;
  transition: transform 140ms ease;
}

.work-card-shell:hover {
  transform: translateY(-2px);
}

.work-card-shell .work-card {
  height: 100%;
}

.work-card {
  display: flex;
  flex-direction: column;
  width: 100%;
  overflow: hidden;
  padding: 0;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: inherit;
  transition: box-shadow 140ms ease, border-color 140ms ease;
}

.work-card-shell:hover .work-card,
.work-card:focus-visible {
  border-color: #c7c7c3;
  box-shadow: 0 12px 34px rgba(23, 27, 31, 0.12);
  outline: none;
}

.bookmark-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 42px;
  height: 48px;
  padding: 3px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: rgba(43, 47, 51, 0.42);
  box-shadow: none;
  cursor: pointer;
  transition: color 120ms ease, background 120ms ease, transform 120ms ease, filter 120ms ease;
}

.bookmark-button svg {
  width: 29px;
  height: 37px;
  overflow: visible;
}

.bookmark-button svg path {
  fill: rgba(43, 47, 51, 0.035);
  stroke: currentColor;
}

.bookmark-button:hover,
.bookmark-button:focus-visible {
  color: rgba(35, 39, 43, 0.82);
  background: rgba(35, 39, 43, 0.05);
  transform: translateY(-1px);
  outline: 2px solid rgba(80, 84, 88, 0.2);
  outline-offset: 2px;
}

.bookmark-button.is-bookmarked {
  color: rgba(35, 39, 43, 0.6);
  background: transparent;
}

.bookmark-button.is-bookmarked svg path {
  fill: currentColor;
}

.bookmark-button:disabled {
  opacity: 0.6;
  cursor: wait;
  transform: none;
}

.work-card-bookmark {
  position: absolute;
  top: 0;
  right: 10px;
  z-index: 3;
  width: 42px;
  height: 50px;
  padding-top: 0;
  color: rgba(255, 255, 255, 0.24);
  mix-blend-mode: difference;
  filter: none;
}

.work-card-bookmark svg {
  width: 33px;
  height: 44px;
  transform: translateY(-6px);
}

.work-card-bookmark svg path {
  fill: rgba(255, 255, 255, 0.012);
  stroke: rgba(255, 255, 255, 0.2);
}

.work-card-bookmark.is-bookmarked,
.work-card-bookmark.is-bookmarked:hover,
.work-card-bookmark.is-bookmarked:focus-visible {
  color: rgba(255, 214, 82, 0.34);
  background: transparent;
  mix-blend-mode: normal;
}

.work-card-bookmark.is-bookmarked svg path {
  fill: rgba(255, 214, 82, 0.34);
  stroke: rgba(255, 232, 148, 0.2);
}

.work-card-bookmark::after {
  content: "";
  position: absolute;
  inset: -2px;
  border: 5px solid #fff;
  border-radius: 8px;
  opacity: 0;
  pointer-events: none;
  mix-blend-mode: difference;
  transition: opacity 120ms ease;
}

.work-card-bookmark:hover::after,
.work-card-bookmark:focus-visible::after {
  opacity: 0.3;
}

.work-card-bookmark:hover,
.work-card-bookmark:focus-visible {
  color: rgba(255, 255, 255, 0.36);
  background: transparent;
  filter: none;
  outline: none;
}

/* 4:3 cover via the universal padding-top ratio hack rather than `aspect-ratio`. Some mobile
   browsers (older Android WebView/Chrome) don't apply `aspect-ratio` here, so the frame fell
   back to each image's natural height — cards were grid-stretched to equal size but their
   covers/titles started at different heights (the "same box, not aligned" look). padding-top
   reserves the height on every browser; the image is pinned to fill it. */
.cover-frame {
  display: block;
  position: relative;
  width: 100%;
  height: 0;
  padding-top: 75%;
  background: var(--surface-soft);
  overflow: hidden;
}

.cover-frame img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.work-card-body {
  display: grid;
  gap: 8px;
  /* Pin the rows to the top. Grid's default `align-content: normal` is resolved as `start`
     by Safari but as `stretch` by Android Chrome, which spread the rows apart (a gap between
     title and badges) once the card was grid-stretched to equal height — that was the
     iOS-aligned / Android-not-aligned difference. `start` makes both behave the same. */
  align-content: start;
  padding: var(--space-3);
}

.work-card-meta {
  color: var(--muted);
  font-size: 13px;
}

.work-card-title {
  min-height: 44px;
  font-weight: 700;
  line-height: 1.35;
}

.variant-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.variant-badges span {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding: 2px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--muted);
  font-size: 12px;
  background: #fbfbfa;
}

/* Page-count badge: same chip style as the variant/status badges but a distinct
   violet, pinned to the right of the badge row. */
.variant-badges .page-badge,
.page-badge {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  margin-left: auto;
  padding: 2px 8px;
  border: 1px solid #d6c8f5;
  border-radius: var(--radius);
  color: #6d28d9;
  font-size: 12px;
  background: #f4effd;
  white-space: nowrap;
}

.status-badge {
  display: inline-flex;
  align-items: center;
  width: fit-content;
  min-height: 26px;
  padding: 3px 9px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--muted);
  font-size: 13px;
  background: var(--surface-soft);
}

.status-badge.ready {
  color: var(--ready);
  background: #edf8f0;
  border-color: #b9dfc4;
}

.status-badge.preparing,
.status-badge.queued,
.status-badge.processing,
.status-badge.click_to_prepare,
.status-badge.deleted {
  color: var(--pending);
  background: #fff7e6;
  border-color: #ead29f;
}

.status-badge.failed,
.status-badge.unavailable {
  color: var(--danger);
  background: #fff1f0;
  border-color: #efc0bd;
}

.empty-state {
  padding: var(--space-5);
  text-align: center;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.empty-state h2 {
  margin: 0 0 var(--space-1);
  color: var(--text);
  letter-spacing: 0;
}

.viewer-title-actions {
  /* Pin to the lower-right of the hero (in the empty space beside the name/status), out
     of the way. Absolute so it never pushes page width nor takes its own line; flex-wrap
     lets future buttons stack upward from this corner instead of flying off-screen. */
  position: absolute;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: var(--space-2);
  max-width: 60%;
}

.why-page-link {
  display: inline-flex;
  align-items: center;
  min-height: 42px;
  padding: 9px 14px;
  color: var(--text);
  font-size: 14px;
  white-space: nowrap;
}

.why-page-link:hover {
  color: var(--accent);
}

.work-detail {
  display: grid;
  grid-template-columns: minmax(240px, 420px) 1fr;
  gap: var(--space-4);
  align-items: start;
}

.work-detail-cover {
  display: block;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface-soft);
}

.work-detail-cover img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.work-detail-copy {
  padding: var(--space-2) 0;
}

.work-detail-copy h1 {
  margin: 0 0 var(--space-3);
  font-size: clamp(28px, 4vw, 42px);
  line-height: 1.1;
  letter-spacing: 0;
}

.action-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 80;
  background: rgba(18, 20, 23, 0.52);
}

.variant-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 90;
  width: min(820px, calc(100vw - 28px));
  max-height: calc(100vh - 36px);
  overflow: auto;
  transform: translate(-50%, -50%);
  padding: var(--space-5);
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.modal-close {
  position: absolute;
  top: 12px;
  right: 12px;
  min-height: 34px;
  padding: 5px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-soft);
  color: var(--muted);
}

.modal-body {
  display: grid;
  grid-template-columns: 240px 1fr;
  gap: var(--space-4);
}

.modal-cover-column {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.modal-cover {
  display: block;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  background: var(--surface-soft);
}

.modal-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.modal-copy h2 {
  margin: 0 44px var(--space-3) 0;
  font-size: 24px;
  line-height: 1.2;
  letter-spacing: 0;
}

.modal-title-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  margin: 0 44px var(--space-3) 0;
}

.modal-title-row h2 {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
}

.modal-bookmark-button {
  margin-top: -9px;
  color: rgba(49, 53, 57, 0.4);
}

.modal-bookmark-button svg path {
  fill: rgba(49, 53, 57, 0.025);
}

.modal-bookmark-button.is-bookmarked {
  color: rgba(214, 173, 70, 0.62);
}

.modal-bookmark-button.is-bookmarked svg path {
  fill: rgba(247, 216, 126, 0.58);
  stroke: rgba(166, 126, 34, 0.64);
}

.variant-list {
  display: grid;
  gap: var(--space-2);
}

.variant-choice {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 48px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fbfbfa;
  color: var(--text);
}

.variant-choice:hover,
.variant-choice:focus-visible {
  border-color: var(--accent);
  outline: none;
}

.variant-choice-primary {
  border-color: var(--accent);
  background: var(--accent);
  color: #fff;
  font-weight: 600;
}

.variant-choice-primary:hover,
.variant-choice-primary:focus-visible {
  background: var(--accent-strong, var(--accent));
}

/* Download: button in the work modal + per-variant download sub-panel */
.modal-actions {
  display: flex;
  justify-content: flex-start;
  margin-top: var(--space-3);
}

.download-open-btn {
  padding: 7px 11px;
  border: 1px solid var(--folder-edge);
  border-radius: var(--radius);
  background: #fbfbfa;
  color: var(--folder-accent-strong);
  font-size: 0.8em;
  cursor: pointer;
}

.download-open-btn:hover,
.download-open-btn:focus-visible {
  border-color: var(--folder-accent);
  outline: none;
}

.download-back-btn {
  align-self: flex-start;
  margin-bottom: var(--space-2);
  padding: 4px 6px;
  border: none;
  background: none;
  color: #6b7280;
  cursor: pointer;
  font-size: 0.9em;
}

.download-back-btn:hover {
  color: var(--text);
}

.download-heading {
  font-weight: 600;
  margin: 0 0 var(--space-2);
}

/* Point completed Download users at the next step: the version buttons pulse twice,
   then stop on their own. Finite iteration-count = no runaway flashing; .download-choice
   only exists while the version list is shown, so it can never pulse before Download is
   tapped. Pure CSS, no JS timers to leak. */
@keyframes download-choice-hint {
  0%, 100% { box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); }
  50%      { box-shadow: 0 0 0 3px var(--accent); border-color: var(--accent); }
}

.download-choice {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 48px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fbfbfa;
  color: var(--text);
  cursor: pointer;
  animation: download-choice-hint 0.7s ease-in-out 2;
}

@media (prefers-reduced-motion: reduce) {
  .download-choice { animation: none; }
}

.download-choice:hover,
.download-choice:focus-visible {
  border-color: var(--accent);
  outline: none;
}

.download-choice:disabled {
  opacity: 0.6;
  cursor: default;
}

.download-progress {
  min-height: 1.2em;
  margin: var(--space-2) 0 0;
  color: #6b7280;
  font-size: 0.92em;
}

/* Quiet links back to the Catalog-matched supporter posts. These deliberately
   remain below the primary Read/Download controls and avoid platform brand colors. */
.original-post-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 7px;
  margin-top: var(--space-3);
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

.original-post-label {
  margin-right: 2px;
  color: #84888d;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.045em;
  text-transform: uppercase;
}

.original-post-links a {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: 33px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: 9px;
  color: #62666a;
  background: #fbfbfa;
  font-size: 12px;
  font-weight: 680;
  line-height: 1;
  text-decoration: none;
  transition: border-color 120ms ease, color 120ms ease, background 120ms ease;
}

.original-post-links a:hover,
.original-post-links a:focus-visible {
  border-color: #c4c5c1;
  color: #272a2d;
  background: var(--surface-soft);
  outline: none;
}

.original-post-links a:focus-visible {
  box-shadow: 0 0 0 3px rgba(65, 73, 82, 0.13);
}

.original-post-links svg {
  width: 13px;
  height: 13px;
  opacity: 0.72;
}

/* Collection / Series / Tag badges */
.modal-meta-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.meta-badge {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  border: 1px solid transparent;
  white-space: nowrap;
  text-decoration: none;
}

.badge-collection {
  background: rgba(79, 70, 229, 0.12);
  color: #4f46e5;
  border-color: rgba(79, 70, 229, 0.25);
}

.badge-collection:hover {
  background: rgba(79, 70, 229, 0.2);
}

.badge-series {
  background: rgba(147, 51, 234, 0.12);
  color: #9333ea;
  border-color: rgba(147, 51, 234, 0.25);
}

.badge-series:hover {
  background: rgba(147, 51, 234, 0.2);
}

.badge-tag {
  background: rgba(13, 148, 136, 0.12);
  color: #0d9488;
  border-color: rgba(13, 148, 136, 0.25);
}

.badge-tag:hover {
  background: rgba(13, 148, 136, 0.2);
}

.badge-bookmark {
  color: #6b4d1f;
  background: #fff7e6;
  border-color: #ead29f;
}

/* Series / Collection nav buttons (above the search bar) */
.collection-nav {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  margin-bottom: var(--space-3);
}

.collection-nav-button {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 9px 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font-weight: 600;
  font-size: 15px;
}

.collection-nav-button:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.collection-nav-bookmark {
  margin-left: auto;
}

.collection-nav-bookmark.is-active,
.collection-nav-bookmark.is-active:hover,
.collection-nav-bookmark.is-active:focus-visible {
  border-color: var(--text);
  background: var(--text);
  color: var(--surface);
}

.collection-nav-icon {
  font-size: 16px;
  line-height: 1;
  opacity: 0.7;
}

.collection-nav-icon svg {
  display: block;
  width: 17px;
  height: 17px;
}

.collection-nav-icon svg path {
  fill: currentColor;
  stroke: currentColor;
}

.bookmark-notice {
  position: fixed;
  left: 50%;
  bottom: 22px;
  z-index: 140;
  max-width: min(420px, calc(100vw - 32px));
  transform: translateX(-50%);
  padding: 10px 14px;
  border: 1px solid #e2b8b5;
  border-radius: var(--radius);
  background: #fff4f3;
  color: #8a2722;
  box-shadow: var(--shadow);
  font-size: 14px;
}

/* Folder tiles (a series / collection shown as one card).
   A folder reads as a *container* of works: warm manila surface, a colored tab,
   and sheets of paper peeking out behind the top edge. */
.folder-card {
  position: relative;
  overflow: visible;                 /* let the tab + stacked sheets peek out */
  background: var(--folder-surface);
  border: 2px solid var(--folder-edge);
}

/* Two sheets stacked behind the tile, peeking a little above the cover's top edge.
   They stop short of the tab so the tab stays the highest, clearest element. */
.folder-card::before,
.folder-card::after {
  content: "";
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  height: 16px;
  border-radius: 7px 7px 0 0;
  border: 2px solid var(--folder-edge);
  border-bottom: none;
  z-index: 0;
}

.folder-card::before {
  width: 90%;
  top: -6px;
  background: var(--folder-sheet);
}

.folder-card::after {
  width: 78%;
  top: -9px;
  background: #fbf3e1;
}

.folder-card .cover-frame {
  position: relative;
  z-index: 1;                        /* covers the lower part of the sheets */
  border-radius: 6px 6px 0 0;
  overflow: hidden;
}

/* The folder tab — the strongest "this is a folder" signal. It protrudes higher
   than the sheets and uses the solid accent so it clearly pops above the edge. */
.folder-tab {
  position: absolute;
  top: -16px;
  left: 16px;
  width: 92px;
  height: 22px;
  background: var(--folder-accent);
  border: 2px solid var(--folder-accent-strong);
  border-bottom: none;
  border-radius: 8px 8px 0 0;
  box-shadow: 0 -2px 6px rgba(143, 100, 24, 0.25);
  z-index: 5;
}

.folder-card:hover,
.folder-card:focus-visible {
  border-color: var(--folder-accent);
  box-shadow: 0 14px 36px rgba(176, 125, 42, 0.22);
}

/* Larger, manila-colored count so series/collections are unmistakable. */
.folder-count {
  position: absolute;
  top: 10px;
  right: 10px;
  min-width: 40px;
  height: 40px;
  padding: 0 11px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: var(--folder-accent);
  color: #fff;
  font-size: 18px;
  font-weight: 800;
  line-height: 1;
  box-shadow: 0 4px 12px rgba(143, 100, 24, 0.45), 0 0 0 3px rgba(255, 255, 255, 0.92);
  z-index: 4;
}

.folder-card .work-card-meta {
  color: var(--folder-accent-strong);
  font-weight: 600;
}

/* Folder titles read larger and heavier than single-work titles. */
.folder-card .work-card-title {
  font-size: 19px;
  font-weight: 800;
}

/* Browse header + active filter chip */
.browse-back {
  color: var(--muted);
  text-decoration: none;
}

.browse-back:hover {
  color: var(--accent);
}

.active-filter-chip {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-3) 0;
}

.active-filter-clear {
  font-size: 13px;
  color: var(--muted);
}

.active-filter-clear:hover {
  color: var(--accent);
}

.preparing-box {
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fbfbfa;
}

.preparing-box p {
  color: var(--muted);
}

.reader-root {
  width: 100%;
  max-width: none;
  min-height: calc(100vh - 56px);
  padding: 0 0 72px;
  background: var(--surface-dark);
  color: #f4f5f6;
}

body:has(.reader-root) {
  background: var(--surface-dark);
}

/* Click-to-turn-page must never start a text/image selection — on fast or repeated clicks
   that would otherwise flash the whole page blue. Applied to the whole reader page (not just
   .reader-root) because a drag can start on the header / toggle / body outside the reader
   container. Form controls (the page <select>) and links are unaffected. */
body.viewer-reader-page {
  -webkit-user-select: none;
  user-select: none;
}

.reader-header {
  position: sticky;
  top: 56px;
  z-index: 35;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-3);
  min-height: 52px;
  padding: 8px max(var(--space-3), calc((100vw - 1180px) / 2));
  background: rgba(21, 23, 25, 0.9);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  backdrop-filter: blur(12px);
}

.reader-header h1 {
  overflow: hidden;
  margin: 0;
  font-size: 15px;
  font-weight: 650;
  text-align: center;
  text-overflow: ellipsis;
  white-space: nowrap;
  letter-spacing: 0;
}

.reader-back,
.reader-variant-label,
.reader-progress {
  color: #c8cdd2;
  font-size: 14px;
}

.reader-back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 30px;
}

.reader-header-controls {
  display: flex;
  align-items: center;
  justify-content: end;
  gap: var(--space-2);
}

.variant-switcher select {
  min-height: 34px;
  max-width: 140px;
  color: #f4f5f6;
  background: var(--surface-dark-2);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--radius);
}

/* Mode control = device family (PC / Mobile) + sub-selectors, all icon buttons.
   PC keeps the blue accent; Mobile uses a muted accent so the two families never read
   as the same group. */
.reader-mode-control {
  --reader-mobile-accent: #5f7d72;
  display: flex;
  align-items: center;
  gap: 0;
  min-height: 34px;
  color: #c8cdd2;
}

.reader-mode-control .lab-group {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.reader-mode-control .lab-group + .lab-group {
  margin-left: 8px;
  padding-left: 8px;
  border-left: 1px solid rgba(255, 255, 255, 0.12);
}

.reader-mode-control button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  min-width: 32px;
  height: 30px;
  min-height: 30px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--radius);
  color: #c8cdd2;
  background: transparent;
  cursor: pointer;
}

/* Every active mode button gets a white glyph; the background differs per group below. */
.reader-mode-control button.active {
  color: #fff;
}
/* PC family — three stepped blues so the device / reading-mode / tap-navigation groups each
   read as their own group while staying clearly in the blue family. */
.reader-device button[data-device="pc"].active {
  background: #1d4ed8;
  border-color: #1d4ed8;
}
.reader-pc-read button.active {
  background: #2f6fe6;
  border-color: #2f6fe6;
}
.reader-pc-nav button.active {
  background: #4f83e6;
  border-color: #4f83e6;
}
/* Mobile family — two stepped greens for the device button vs the swipe-axis group. */
.reader-device button[data-device="mobile"].active {
  background: var(--reader-mobile-accent);
  border-color: var(--reader-mobile-accent);
}
.reader-mobile button.active {
  background: #789e8c;
  border-color: #789e8c;
}

.control-icon {
  display: block;
}

.reader-stage {
  position: relative;
  overflow-x: clip;
}

.reader-prepare-notice {
  position: sticky;
  top: 110px;
  z-index: 42;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: min(520px, calc(100vw - 32px));
  margin: var(--space-3) auto 0;
  padding: 10px 12px;
  color: #eef1f4;
  background: rgba(32, 35, 38, 0.94);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--radius);
  box-shadow: 0 18px 45px rgba(0, 0, 0, 0.28);
  backdrop-filter: blur(10px);
}

.reader-prepare-notice p {
  margin: 2px 0 0;
  color: #c8cdd2;
  font-size: 13px;
}

.reader-prepare-notice .state-spinner {
  width: 22px;
  height: 22px;
  margin: 0;
  border-color: rgba(255, 255, 255, 0.25);
  border-top-color: #fff;
}

.reader-pages {
  width: min(calc((100vw - 32px) * var(--size-scale, 1)), calc(900px * var(--reader-scale) * var(--size-scale, 1)));
  margin: 0 auto;
  padding: var(--space-3) 0 var(--space-5);
}

.reader-page {
  display: block;
  width: 100%;
  height: auto;
  margin: 0 auto var(--space-2);
  background: #25282c;
}

/* PC Scroll uses viewport height as its size baseline, matching the proven
   multi-page-reader behaviour: 100% = 100vh, while width follows each page's
   own aspect ratio. This is display-only and leaves image loading untouched. */
.reader-root.mode-scroll .reader-pages {
  width: 100%;
}

.reader-root.mode-scroll .reader-page {
  width: auto;
  max-width: none;
  max-height: calc(100vh * var(--size-scale, 1));
}

/* Reserve the final Scroll layout before lazy images receive a src or decode.
   Normal pages keep 100% = 100vh. Extra-tall pages use A4 portrait as the
   narrowest fit baseline, then extend vertically without distorting the image.
   The natural-width cap preserves the existing no-upscale behaviour. */
.reader-root.mode-scroll .reader-page.reader-page-sized {
  width: min(
    calc(var(--reader-page-fit-width) * var(--size-scale, 1)),
    var(--reader-page-natural-width)
  );
  height: auto;
  max-height: none;
}

.reader-size-panel {
  position: fixed;
  top: 144px;
  left: 16px;
  z-index: 45;
  width: 132px;
  padding: var(--space-2);
  color: #e6e8eb;
  background: rgba(32, 35, 38, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--radius);
  box-shadow: 0 18px 45px rgba(0, 0, 0, 0.28);
  backdrop-filter: blur(10px);
}

.reader-size-panel.collapsed {
  width: auto;
  padding: 0;
  overflow: hidden;
}

.panel-toggle {
  width: 100%;
  min-height: 32px;
  border: 0;
  border-radius: var(--radius);
  color: #e6e8eb;
  background: rgba(255, 255, 255, 0.08);
}

.reader-size-panel.collapsed .panel-content {
  display: none;
}

.panel-content p {
  margin: 10px 0 8px;
  color: #c8cdd2;
  font-size: 13px;
}

.reader-header-left {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.reader-size-control {
  position: relative;
  display: flex;
  align-items: center;
}

.size-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  min-width: 32px;
  height: 30px;
  min-height: 30px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--radius);
  color: #c8cdd2;
  background: transparent;
  cursor: pointer;
}

.size-toggle[aria-expanded="true"] {
  color: #fff;
  background: var(--accent);
  border-color: var(--accent);
}

.size-popover {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 14px 12px 10px;
  background: rgba(32, 35, 38, 0.96);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--radius);
  box-shadow: 0 18px 45px rgba(0, 0, 0, 0.34);
  backdrop-filter: blur(10px);
}

.size-popover[hidden] {
  display: none;
}

.scale-slider {
  writing-mode: vertical-lr;
  direction: rtl;
  width: 28px;
  height: 150px;
  cursor: pointer;
  accent-color: var(--accent);
  background: transparent;
  border: none;
  padding: 0;
}

.scale-value {
  font-size: 12px;
  color: #c8cdd2;
}

.reader-hint {
  position: fixed;
  left: 50%;
  bottom: 18px;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  max-width: calc(100vw - 28px);
  padding: 8px 10px;
  transform: translateX(-50%);
  color: #d9dde2;
  background: rgba(32, 35, 38, 0.94);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--radius);
  font-size: 13px;
}

.reader-hint button {
  min-height: 28px;
  border: 0;
  border-radius: var(--radius);
  color: #fff;
  background: rgba(255, 255, 255, 0.12);
}

.tap-reader {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  min-height: calc(100vh - 108px);
  padding: var(--space-3);
}

.tap-image-frame {
  position: relative;
  width: min(calc(900px * var(--reader-scale) * var(--size-scale, 1)), calc((100vw - 32px) * var(--size-scale, 1)));
}

.tap-reader img {
  display: block;
  width: 100%;
  max-width: none;
  max-height: none;
  height: auto;
  object-fit: initial;
}

.tap-zone {
  display: none;
}

.tap-image-frame[data-tap-navigation="zones"] .tap-zone {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 2;
  display: block;
  width: 50%;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  cursor: pointer;
}

.tap-image-frame[data-tap-navigation="zones"] .tap-zone:focus-visible {
  outline: 2px solid #fff;
  outline-offset: -4px;
}

.tap-prev {
  left: 0;
}

.tap-next {
  right: 0;
}

.tap-controls {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  width: min(calc(900px * var(--reader-scale)), calc(100vw - 32px));
  min-height: 64px;
  margin-top: var(--space-3);
  padding: 10px 0;
}

.tap-controls button {
  min-height: 40px;
  min-width: 120px;
  padding: 8px 14px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--radius);
  color: #f4f5f6;
  background: rgba(32, 35, 38, 0.88);
}

.reader-page-jump {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  width: min(calc(900px * var(--reader-scale)), calc(100vw - 32px));
  padding: 0 0 var(--space-3);
  color: #c8cdd2;
  font-size: 14px;
}

.reader-page-jump select {
  min-width: 0;
  min-height: 28px;
  font-size: 13px;
  padding: 3px 18px 3px 8px;
  color: #f4f5f6;
  background: transparent;
  border: none;
  border-radius: var(--radius);
}
/* The open native dropdown is drawn on a white system surface, so its options need dark text
   (the closed control's light text on the dark bar was making the open list white-on-white). */
.reader-page-jump option,
.reader-progress-jump option {
  color: #202124;
  background: #fff;
}
/* Header page selector (swipe mode): a dropdown styled to read like the plain x/y count. */
.reader-progress-jump {
  display: inline-flex;
  align-items: center;
}
.reader-progress-jump select {
  min-width: 0;
  min-height: 26px;
  padding: 2px 16px 2px 4px;
  color: #c8cdd2;
  font-size: 14px;
  background: transparent;
  border: none;
  cursor: pointer;
}

.reader-end-card {
  width: min(680px, calc(100vw - 32px));
  margin: var(--space-5) auto 96px;
  padding: var(--space-4);
  color: #eef1f4;
  text-align: center;
  background: rgba(32, 35, 38, 0.86);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--radius);
}

.reader-end-card p {
  margin: 0;
  color: #d9dde2;
}

.reader-end-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.reader-end-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 42px;
  min-width: 132px;
  padding: 9px 14px;
  color: #fff;
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
}

/* Hover styles are gated to real-hover devices. On touch, a :hover rule that changes the
   element makes the FIRST tap only apply hover (button needs a second tap) — the classic
   iOS two-tap. Gating it means touch taps fire the link's click on the first tap. */
@media (hover: hover) {
  .reader-end-button:hover {
    background: var(--accent-strong);
    border-color: var(--accent-strong);
  }
}

/* Same-series prev/next navigation flanking the end card. Styled to blend with the
   dark reader background (matches .reader-end-card). */
.reader-end-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  width: 100%;
  padding-bottom: 96px;
}

.reader-end-wrap .reader-end-card {
  margin: var(--space-5) 0 0;
}

.series-nav-slot {
  flex: 0 1 auto;
  display: flex;
}

.series-nav-slot:empty {
  display: none;
}

.series-nav-button {
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 220px;
  padding: 8px 12px;
  background: var(--surface-dark-2);
  color: #eef1f4;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--radius);
  text-decoration: none;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.series-nav-button:focus-visible {
  border-color: rgba(255, 255, 255, 0.32);
  background: #262a2e;
  outline: none;
}
@media (hover: hover) {
  .series-nav-button:hover {
    border-color: rgba(255, 255, 255, 0.32);
    background: #262a2e;
  }
}

.series-nav-next {
  flex-direction: row-reverse;
  text-align: right;
}

.series-nav-arrow {
  flex: 0 0 auto;
  font-size: 1.4em;
  line-height: 1;
  opacity: 0.65;
}

.series-nav-thumb {
  flex: 0 0 auto;
  width: 40px;
  height: 52px;
  border-radius: 4px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.06);
}

.series-nav-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.series-nav-title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 0.85em;
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

@media (max-width: 900px) {
  .viewer-header {
    padding-left: var(--space-3);
    padding-right: var(--space-3);
  }

  .library-grid {
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  }

  .work-detail,
  .modal-body {
    grid-template-columns: 1fr;
  }

  .modal-cover {
    width: min(240px, 100%);
  }

  .reader-size-panel {
    display: none;
  }

  .reader-header {
    top: 56px;
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: var(--space-2);
    padding-left: var(--space-3);
    padding-right: var(--space-3);
  }

  .reader-header h1 {
    text-align: left;
  }

  .reader-header-controls {
    gap: 6px;
  }

  .variant-switcher select {
    max-width: 92px;
    min-height: 30px;
  }

  .reader-pages {
    width: calc(100% * var(--size-scale, 1));
    padding-top: 0;
  }

  /* A phone may explicitly select PC Scroll mode. Keep the established
     viewport-width mobile sizing there; desktop's vh/A4 baseline would make
     landscape pages far wider than the phone screen. The manifest aspect
     ratio still reserves each page before its image loads. */
  .reader-root.mode-scroll .reader-pages {
    width: calc(100% * var(--size-scale, 1));
  }

  .reader-root.mode-scroll .reader-page,
  .reader-root.mode-scroll .reader-page.reader-page-sized {
    width: 100%;
    max-width: 100%;
    height: auto;
    max-height: none;
  }

  .reader-page {
    margin-bottom: 6px;
  }

  .reader-hint {
    display: none;
  }

  .tap-reader {
    padding-bottom: var(--space-3);
  }

  /* PC Tap is intentionally full-bleed on a phone. Give its reader root the
     same width as the viewport so the 100vw image frame is centred instead of
     starting after the normal mobile content gutter and clipping on the right. */
  .reader-root.mode-tap {
    width: 100%;
  }

  .reader-mode-control .lab-group {
    gap: 3px;
  }

  .reader-mode-control .lab-group + .lab-group {
    margin-left: 6px;
    padding-left: 6px;
  }

  .reader-mode-control button {
    width: 30px;
    min-width: 30px;
    padding: 0;
  }

  .tap-image-frame {
    /* Full-bleed at 100% like swipe mode (drop the 12px side gutter that read as a black edge). */
    width: min(calc(100vw * var(--reader-scale) * var(--size-scale, 1)), calc(100vw * var(--size-scale, 1)));
  }

  .tap-controls button {
    flex: 1;
    min-width: 0;
  }

  .reader-page-jump {
    width: calc(100vw - 12px);
  }
}

@media (max-width: 560px) {
  .maintenance-reminder {
    top: calc(env(safe-area-inset-top, 0px) + 58px);
    width: calc(100vw - 16px);
    gap: 8px;
    padding: 9px 11px;
  }

  .maintenance-reminder-copy {
    font-size: 12px;
  }

  .maintenance-reminder-copy strong {
    font-size: 13px;
  }

  .viewer-nav {
    gap: var(--space-2);
    font-size: 13px;
  }

  .viewer-root {
    width: min(100% - 20px, var(--max));
  }

  .viewer-page-title {
    display: flex;
    align-items: end;
    gap: var(--space-3);
  }

  .viewer-title-actions {
    top: 23.5px;
    bottom: auto;
  }

  .why-page-link {
    font-size: 10.5px;
  }

  .viewer-page-title > :first-child {
    width: 100%;
  }

  .viewer-page-title h1 {
    max-width: calc(100% - 96px);
    font-size: clamp(18px, 5.3vw, 26px);
    line-height: 1.05;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    overflow-wrap: normal;
    word-break: normal;
  }

  .collection-nav {
    gap: 6px;
  }

  .collection-nav-button {
    min-width: 0;
    padding: 8px 11px;
    font-size: 13px;
  }

  .collection-nav-bookmark {
    width: 44px;
    min-width: 44px;
    padding: 0;
    gap: 0;
    justify-content: center;
  }

  .collection-nav-bookmark .collection-nav-label {
    display: none;
  }

  .work-card-bookmark {
    display: none;
  }

  .library-search {
    display: grid;
    gap: var(--space-2);
  }

  .pagination-nav {
    gap: 2px;
    width: 100%;
    min-height: 64px;
  }

  .pagination-number,
  .pagination-arrow {
    width: 44px;
    height: 44px;
    font-size: 18px;
  }

  .library-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-2);
  }

  .work-card-body {
    padding: 12px;
  }

  .work-card-title {
    min-height: 58px;
    font-size: 14px;
  }

  .variant-badges span,
  .status-badge {
    font-size: 11px;
  }

  .variant-modal {
    width: calc(100vw - 20px);
    padding: var(--space-3);
  }

  .modal-copy h2 {
    margin-right: 52px;
    font-size: 20px;
  }

  .original-post-label {
    flex: 0 0 100%;
    margin-bottom: 1px;
  }

  .reader-header {
    gap: 8px;
  }

  .reader-header-controls {
    gap: 6px;
  }

  .variant-switcher select {
    max-width: 92px;
  }
}

/* Announcement button (viewer header) + modal */
.viewer-announce-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  min-height: 32px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--text);
  background: var(--surface);
  font: inherit;
  cursor: pointer;
}
.viewer-announce-icon {
  display: block;
}
.viewer-nav-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  color: var(--muted);
}
.viewer-nav-icon:hover {
  color: var(--text);
}
.viewer-nav-icon svg {
  display: block;
}
.viewer-announce-btn:hover {
  background: var(--surface-soft);
}
.announcement-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
}
.announcement-overlay .modal-backdrop {
  z-index: 100;
}
.announcement-panel {
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 101;
  width: min(560px, calc(100vw - 28px));
  max-height: calc(100vh - 36px);
  overflow: auto;
  transform: translate(-50%, -50%);
  padding: var(--space-5);
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.announcement-panel h2 {
  margin: 0 40px 12px 0;
}
.announcement-body {
  white-space: pre-wrap;
  word-break: break-word;
  color: var(--text);
  line-height: 1.6;
}

/* ============================================================================
   Reader modes port (from viewer_lab sandbox): PC Scroll/Tap + Mobile Swipe.
   Tap centring, fixed page bar, and the horizontal/vertical swipe pager.
   ============================================================================ */

/* ---- Tap: centre the image against the viewport; page bar pinned, out of flow ---- */
/* .reader-root hardcodes min-height: calc(100vh - 56px) + 72px bottom padding; in tap
   mode both are dead scrollable background (and the 56 is wrong when the header wraps),
   so let it collapse to content and let the measured --reader-chrome govern. */
.reader-root.mode-tap {
  min-height: 0;
  padding-bottom: 0;
}
.mode-tap .tap-reader {
  justify-content: flex-start;
  padding: 0;
  min-height: 0;
}
/* The image gets its own full-viewport stage so it is centred against the viewport
   ALONE. The end card + series prev/next flow BELOW this stage (scroll to reveal on the
   last page) and never shift the image. --reader-chrome is the measured header height
   (0 collapsed); vh, not dvh, so the image does not resize as the mobile URL bar moves. */
.mode-tap .tap-stage {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: calc(100vh - var(--reader-chrome, 108px));
}
.mode-tap .tap-page-stack {
  display: grid;
  width: 100%;
  place-items: center;
}
.mode-tap .tap-page-shell {
  display: none;
  grid-area: 1 / 1;
  width: 100%;
  place-items: center;
}
.mode-tap .tap-page-shell.active {
  display: grid;
}
.mode-tap .tap-image-frame {
  overflow: hidden;
  background: #25282c;
}
.mode-tap .tap-image-frame > .tap-reader-image {
  display: block;
  width: 100%;
  height: 100%;
  margin: 0;
  object-fit: contain;
  background: #25282c;
}

/* Page jump: fixed at the bottom, semi-transparent while the chrome is shown, gone once
   it is collapsed (pressing the corner triangle). It never moves with the image. */
.mode-tap .reader-page-jump,
.mode-swipe .reader-page-jump {
  position: fixed;
  left: 50%;
  bottom: 12px;
  z-index: 45;
  width: auto;
  margin: 0;
  padding: 3px 10px;
  transform: translateX(-50%);
  background: rgba(21, 23, 25, 0.72);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 999px;
  backdrop-filter: blur(8px);
  opacity: 0.25;
  transition: opacity 160ms ease;
}
/* Only a real mouse hover brightens it, and only in PC tap mode. Swipe (touch) mode keeps it
   faint even while tapping: a :hover/:focus rule on a touch device makes the browser spend the
   FIRST tap applying hover (dead tap) and only click on the second. */
@media (hover: hover) {
  .mode-tap .reader-page-jump:hover,
  .mode-tap .reader-page-jump:focus-within {
    opacity: 1;
  }
}
.reader-chrome-collapsed .reader-page-jump {
  display: none;
}
/* End card stays in normal flow below the image (a fixed one would sit on top of it). */
.mode-tap #tap-end-card-root:empty {
  display: none;
}
.mode-tap #tap-end-card-root .reader-end-card {
  margin: var(--space-4) auto var(--space-4);
}

/* ---- Mobile: horizontal / vertical swipe pager ---- */
html:has(.reader-root.mode-swipe),
body:has(.reader-root.mode-swipe) {
  overflow: hidden;
  height: 100%;
  overscroll-behavior: none;
}
.mode-swipe .reader-stage {
  overflow: visible;
}
/* The headers keep their stock sticky layout (white .viewer-header z40 above the dark
   .reader-header z35). The swipe surface starts below them at --reader-chrome (measured
   in JS, 0 once collapsed) and its z-index stays under the headers. */
.swipe-reader {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  top: var(--reader-chrome, 0px);
  z-index: 1;
  background: #0b0c0d;
}
.swipe-viewport {
  position: absolute;
  inset: 0;
  overflow: hidden;
  touch-action: none;            /* the pager owns all gestures on both axes */
  overscroll-behavior: contain;
  cursor: grab;
}
.swipe-viewport:active {
  cursor: grabbing;
}
.swipe-track {
  display: flex;
  width: 100%;
  height: 100%;
  will-change: transform;
}
.swipe-track.axis-x {
  flex-direction: row;
}
.swipe-track.axis-y {
  flex-direction: column;
}
.swipe-slide {
  flex: 0 0 100%;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Slides carry .reader-page (for the shared loading machinery); override its scroll-mode
   sizing so the image is contained and transformable inside the slide. */
.swipe-slide .swipe-img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  margin: 0;
  background: transparent;
  object-fit: contain;
  transform-origin: center center;
  will-change: transform;
  user-select: none;
  -webkit-user-drag: none;
}
/* PC-only right-edge size slider: fixed at the vertical centre, faint until hovered, never
   moves with the image, and stays visible even when the corner triangle collapses the chrome. */
.reader-size-rail {
  position: fixed;
  right: 6px;
  top: 50%;
  z-index: 46;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  /* With the browser's native range margin, this makes the rail about 15% narrower
     in Chromium (38px versus the former 44px). */
  padding: 10px 4px;
  background: rgba(21, 23, 25, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 999px;
  backdrop-filter: blur(8px);
  opacity: 0.20;
  transition: opacity 160ms ease;
}
.reader-size-rail:hover,
.reader-size-rail:focus-within {
  opacity: 1;
}

/* The compact header slider stays at its existing production size. Only the fixed
   right-side PC rail is shortened and narrowed. */
.reader-size-rail .scale-slider {
  width: 24px;
  height: 190px;
}

/* Passive reference tick at the real 100% position; it never intercepts dragging. */
.reader-size-notch {
  position: absolute;
  left: 3px;
  bottom: calc(10px + var(--reader-size-notch-offset));
  width: 7px;
  height: 2px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.78);
  pointer-events: none;
}

/* Swipe end-card slide: a final page holding the reaction card + series prev/next. It
   has no image, so it centres its content and scrolls internally if the content is tall.
   Overrides the scroll-mode end-card margins so it sits centred in the slide. */
.swipe-slide.swipe-end-slide {
  overflow-y: auto;
  /* The pager owns all gestures here. Without this, the browser treats a vertical drag as a
     scroll of THIS overflow container and steals it from the pager (why swipe-back failed on
     a tall end card with both series buttons). touch-action:none hands the drag back to us. */
  touch-action: none;
  padding: var(--space-4) var(--space-3);
}
.swipe-end-slide .reader-end-wrap {
  margin: auto 0;
  width: min(680px, calc(100vw - 32px));
}
.swipe-end-slide .reader-end-card {
  margin: var(--space-3) auto;
}
/* End-card links (Patreon/Fanbox, series prev/next) must swipe like an image slide:
   stop the browser's native link/image drag so press-and-drag pans the pager instead of
   being cancelled — a cancelled gesture snapped the track back (the "bounce") and ate the
   first tap. Image slides already get this from <img draggable="false">; links also need
   -webkit-user-drag off (iOS ignores the draggable attribute). Scoped to swipe mode so the
   scroll/tap end cards are untouched. */
.swipe-end-slide a,
.swipe-end-slide img {
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

/* "Add to Home Screen" tip button — sits on its own line ABOVE the Why link (flex-basis
   forces its own row inside .viewer-title-actions; the Why link stays pinned at bottom:0).
   Unobtrusive: muted, small, no border/background. */
.a2hs-tips-btn {
  flex-basis: 100%;
  text-align: right;
  padding: 0 14px;
  border: none;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 11px;
  cursor: pointer;
  white-space: nowrap;
}
.a2hs-tips-btn:hover {
  color: var(--accent);
}
.a2hs-benefit {
  margin: 0 0 14px;
  color: var(--text);
  line-height: 1.5;
}
.a2hs-steps {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.a2hs-step {
  display: flex;
  align-items: center;
  gap: 10px;
}
.a2hs-step-num {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 999px;
  background: var(--surface-soft);
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
}
.a2hs-step-text {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.a2hs-step-icon {
  vertical-align: middle;
  color: var(--text);
}
