:root { /* One palette for both themes. Every surface, text and status colour in the rules below resolves through these variables, so the dark theme only overrides values, never layout. These are the only literal colours in the file. */ --bg: #f7f5ee; --surface: #fffdf8; --surface-raised: #fffdf7; --surface-input: #fffefa; --text: #233d31; --text-strong: #264a35; --text-muted: #748073; --text-faint: #687568; --border: #e0e3d8; --border-strong: #d9decf; --border-input: #d6dccf; --accent: #315c43; --accent-soft: #eef2eb; --accent-marker: #bc803d; --accent-gold: #ba8345; --brand-panel: #284c38; --brand-panel-text: #f7f5ee; --brand-panel-muted: #d4dfd2; --brand-panel-faint: #c4d1c0; --brand-panel-line: #d6b980; --status-pending-bg: #f5f2e4; --status-pending-border: #e2dcc2; --status-pending-text: #7a6a35; --status-processing-bg: #eaf1f7; --status-processing-border: #c9dcea; --status-processing-text: #35566e; --status-ready-bg: #eef2eb; --status-ready-border: #cbd9c9; --status-ready-text: #315c43; --status-failed-bg: #fff0e7; --status-failed-border: #ebc3a8; --status-failed-text: #8b4324; --status-read-bg: #e7f0e9; --status-read-border: #b9d3bf; --status-read-text: #2c5b3d; --notice-bg: #fff0e7; --notice-border: #ebc3a8; --notice-text: #8b4324; --highlight: #eef2eb; --sense-bg: #fbf7ee; --sense-border: #d6b980; --sense-text: #6b5b3e; --sense-muted: #8b794e; --saved: #2f6b45; --word-learning-bg: #f6e7c9; --word-known-bg: #dee9d6; --word-ignored-text: #949c94; --word-selected-bg: #e3e9d9; --reader-font-scale: 1; font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif; color: var(--text); background: var(--bg); font-synthesis: none; --el-color-primary: var(--accent); --el-color-primary-light-3: #597f66; --el-color-primary-light-5: #8da493; --el-color-primary-light-7: #bdccc0; --el-color-primary-light-9: var(--accent-soft); --el-color-primary-dark-2: var(--text-strong); --el-border-radius-base: 8px; --el-font-size-base: 15px; } /* Dark palette. The Element Plus dark class is toggled by the display store, so components follow the same choice as the page palette. */ html[data-theme='dark'] { --bg: #171a17; --surface: #1f231f; --surface-raised: #1c201c; --surface-input: #242824; --text: #e7ece4; --text-strong: #cfe0d3; --text-muted: #9fae9c; --text-faint: #8e9c8c; --border: #333a33; --border-strong: #3b433b; --border-input: #3f483f; --accent: #7db28f; --accent-soft: #24312a; --accent-marker: #d7a45f; --accent-gold: #d7a45f; --brand-panel: #10140f; --brand-panel-text: #e7ece4; --brand-panel-muted: #b9c7b6; --brand-panel-faint: #97a695; --brand-panel-line: #d7a45f; --status-pending-bg: #33301f; --status-pending-border: #4a442b; --status-pending-text: #e0cf94; --status-processing-bg: #1d2a33; --status-processing-border: #2f4756; --status-processing-text: #a8cbe0; --status-ready-bg: #22301f; --status-ready-border: #35492f; --status-ready-text: #a9d0ac; --status-failed-bg: #33231b; --status-failed-border: #4d3527; --status-failed-text: #e9ad86; --status-read-bg: #1f3024; --status-read-border: #33493a; --status-read-text: #a5d3b1; --notice-bg: #33231b; --notice-border: #4d3527; --notice-text: #e9ad86; --highlight: #26332a; --sense-bg: #23261d; --sense-border: #4a4227; --sense-text: #d9cba6; --sense-muted: #c0ad7f; --saved: #8ec69f; --word-learning-bg: #3b3520; --word-known-bg: #253120; --word-ignored-text: #7d867d; --word-selected-bg: #2b382d; } /* Element Plus reads its own dark variables; the shared palette keeps both in step. */ html.dark { --el-bg-color: var(--surface); --el-bg-color-overlay: var(--surface-raised); --el-text-color-primary: var(--text); --el-text-color-regular: var(--text); --el-text-color-secondary: var(--text-muted); --el-border-color: var(--border); --el-border-color-light: var(--border); --el-fill-color-blank: var(--surface-input); --el-fill-color-light: var(--accent-soft); color-scheme: dark; } * { box-sizing: border-box; } body { margin: 0; min-width: 320px; } button, input { font: inherit; } a { color: inherit; } .brand { font-family: Georgia, serif; font-size: 30px; font-weight: 700; letter-spacing: -1px; text-decoration: none; } .brand-dot { color: var(--accent-gold); } .login-page { min-height: 100dvh; display: grid; grid-template-columns: minmax(340px, 1fr) minmax(400px, 1fr); } .welcome { background: var(--brand-panel); color: var(--brand-panel-text); padding: 46px 10%; display: flex; flex-direction: column; justify-content: space-between; } .welcome .brand { color: var(--brand-panel-text); } .welcome-copy { padding: 80px 0; max-width: 450px; } .welcome-copy h2 { font-family: Georgia, 'Microsoft YaHei', serif; font-weight: 400; font-size: clamp(32px, 3.3vw, 52px); line-height: 1.55; letter-spacing: 2px; margin: 26px 0; } .welcome-copy p { font-size: 16px; color: var(--brand-panel-muted); line-height: 1.9; } .book-mark { width: 60px; height: 60px; color: var(--brand-panel-line); } .welcome-foot { font-size: 13px; letter-spacing: 2px; color: var(--brand-panel-faint); } .login-main { display: grid; place-items: center; padding: 48px 28px; } .login-form { width: min(100%, 360px); } .eyebrow { color: var(--text-faint); font-size: 12px; letter-spacing: 3px; } h1 { font-size: 30px; font-weight: 600; margin: 14px 0; letter-spacing: 1px; } .subtle { color: var(--text-muted); font-size: 14px; line-height: 1.8; } .login-form form { margin-top: 36px; } .field { display: block; margin-bottom: 22px; } .field label { display: block; font-size: 14px; margin-bottom: 10px; } .el-input { --el-input-height: 46px; --el-input-bg-color: var(--surface-input); --el-input-border-color: var(--border-input); } .el-button { min-height: 42px; } .login-submit { width: 100%; margin-top: 8px; height: 48px; letter-spacing: 4px; } .notice { padding: 12px 15px; background: var(--notice-bg); border: 1px solid var(--notice-border); color: var(--notice-text); border-radius: 6px; font-size: 14px; line-height: 1.6; } .site-header { padding: 24px max(24px, calc((100vw - 1200px) / 2)); border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 32px; background: var(--surface-raised); } .site-header nav { flex: 1; } .active-nav { font-size: 15px; text-decoration: none; padding: 13px 0; border-bottom: 2px solid var(--accent); } .account { display: flex; align-items: center; gap: 18px; font-size: 14px; } .account-name { max-width: 200px; overflow: hidden; text-overflow: ellipsis; } .library { max-width: 1120px; margin: 60px auto; padding: 0 28px; } .library-title { display: flex; align-items: center; justify-content: space-between; gap: 20px; margin-bottom: 38px; } .language { border: 1px solid var(--border-strong); padding: 9px 17px; border-radius: 20px; font-size: 13px; background: var(--surface-raised); } .empty-library { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; min-height: 360px; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 44px 20px; text-align: center; } .empty-library .book-mark { color: var(--accent); width: 56px; height: 56px; } .empty-library h2 { font-weight: 500; font-size: 21px; margin: 26px 0 4px; } .loading { padding: 80px 24px; text-align: center; color: var(--text-muted); } /* Import / book / reader pages share one shell. */ .page { max-width: 1120px; margin: 60px auto; padding: 0 28px; } .page-title { display: flex; align-items: center; justify-content: space-between; gap: 18px; flex-wrap: wrap; margin-bottom: 28px; } .page-title h1 { margin: 14px 0 6px; overflow-wrap: anywhere; } .page-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; } .library-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; } .link-button { display: inline-flex; align-items: center; min-height: 42px; padding: 0 8px; color: var(--accent); } .breadcrumb { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 4px; font-size: 14px; color: var(--text-muted); } .breadcrumb a { color: var(--accent); } /* Library book cards. */ .book-grid { list-style: none; margin: 0; padding: 0; display: grid; gap: 18px; grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); } .book-card { display: flex; flex-direction: column; gap: 10px; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 22px; } .book-card .subtle { margin: 0; } .book-title { font-family: Georgia, 'Microsoft YaHei', serif; font-size: 21px; font-weight: 600; color: var(--text); text-decoration: none; overflow-wrap: anywhere; } .book-title:hover { color: var(--accent); text-decoration: underline; } .status-summary { align-self: flex-start; margin: 0; padding: 6px 13px; border-radius: 20px; background: var(--accent-soft); color: var(--accent); font-size: 13px; } /* Chapter and job status, identical vocabulary for both. */ .status-chip { display: inline-flex; align-items: center; white-space: nowrap; padding: 5px 13px; border: 1px solid transparent; border-radius: 20px; font-size: 13px; } .status-pending { background: var(--status-pending-bg); border-color: var(--status-pending-border); color: var(--status-pending-text); } .status-processing { background: var(--status-processing-bg); border-color: var(--status-processing-border); color: var(--status-processing-text); } .status-ready { background: var(--accent-soft); border-color: var(--status-ready-border); color: var(--accent); } .status-failed { background: var(--notice-bg); border-color: var(--notice-border); color: var(--notice-text); } .status-read { background: var(--status-read-bg); border-color: var(--status-read-border); color: var(--status-read-text); } /* Completing a chapter: one explicit action, with the reading rule spelled out. */ .chapter-complete { display: flex; align-items: center; justify-content: space-between; gap: 18px; flex-wrap: wrap; margin-top: 22px; padding: 18px 22px; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; } .chapter-complete .subtle { margin: 6px 0 0; } .read-state { margin: 0; font-weight: 600; color: var(--status-read-text); } /* Progress page. */ .progress-stats { display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); margin-top: 6px; } .stat-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 20px 22px; } .stat-card h2 { margin: 0 0 10px; font-size: 15px; font-weight: 600; color: var(--accent); letter-spacing: 0; } .stat-card .subtle { margin: 6px 0 0; } .stat-value { margin: 0; font-family: Georgia, 'Microsoft YaHei', serif; font-size: 30px; line-height: 1.2; color: var(--text); } .progress-books { margin-top: 34px; } .progress-books h2 { margin: 0 0 14px; font-size: 19px; } .progress-book-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; } .progress-book-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 16px 18px; } /* Import form. */ .import-form { max-width: 720px; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 28px; } .field-label { display: block; font-size: 14px; margin-bottom: 10px; } .fixed-value { margin: 0; padding: 12px 15px; border: 1px solid var(--border-input); border-radius: 8px; background: var(--surface-input); font-size: 15px; } .field-error { margin: 8px 0 0; color: var(--notice-text); font-size: 13px; line-height: 1.6; } .counter { margin: 8px 0 0; color: var(--text-muted); font-size: 13px; } .form-actions { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-top: 14px; } .import-form .el-textarea__inner { min-height: 220px; line-height: 1.9; } .book-select { width: 100%; } /* Book chapters. */ .chapter-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; } .chapter-row { display: grid; grid-template-columns: 40px minmax(0, 1fr) auto auto; align-items: center; gap: 14px; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 16px 18px; } .chapter-ordinal { font-family: Georgia, serif; font-size: 17px; color: var(--text-faint); text-align: center; } .chapter-info { min-width: 0; } .chapter-name { display: inline-block; font-size: 16px; font-weight: 500; color: var(--text); text-decoration: none; overflow-wrap: anywhere; } a.chapter-name:hover { color: var(--accent); text-decoration: underline; } .chapter-meta { margin: 4px 0 0; color: var(--text-muted); font-size: 13px; overflow-wrap: anywhere; } /* Reader keeps the pasted text exactly as it was, including line breaks and tabs. */ .reader-page { max-width: 820px; } .chapter-author { margin: -6px 0 0; color: var(--text-muted); font-size: 14px; } .reader-text { white-space: pre-wrap; overflow-wrap: break-word; margin: 26px 0 0; font-family: Georgia, 'Times New Roman', 'Microsoft YaHei', serif; font-size: calc(17px * var(--reader-font-scale)); line-height: 2; } .processing-hint { padding: 36px 0; color: var(--text-muted); } .reader-nav { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; margin-top: 36px; padding-top: 22px; border-top: 1px solid var(--border); } .reader-page.has-lookup { max-width: 1120px; } .reader-workspace { display: grid; grid-template-columns: minmax(0, 1fr); gap: 32px; align-items: start; } .has-lookup .reader-workspace { grid-template-columns: minmax(0, 1fr) 320px; } .reader-body { min-width: 0; } .reader-word { cursor: pointer; border-radius: 3px; } /* Saved words keep one style per status everywhere they appear. */ .reader-word.is-new { border-bottom: 2px dotted var(--accent-marker); } .reader-word.is-learning { background: var(--word-learning-bg); } .reader-word.is-known { background: var(--word-known-bg); } .reader-word.is-ignored { color: var(--word-ignored-text); } .reader-word:hover, .reader-word.is-selected { background: var(--word-selected-bg); color: var(--text-strong); } .reader-word:focus-visible { outline: 2px solid var(--accent-marker); outline-offset: 2px; background: var(--accent-soft); } .tokens-notice { color: var(--text-muted); font-size: 14px; margin-top: 24px; } .lookup-panel { position: sticky; top: 24px; margin-top: 26px; padding: 22px; border: 1px solid var(--border-strong); border-radius: 12px; background: var(--surface); max-height: calc(100dvh - 48px); overflow-y: auto; overflow-wrap: anywhere; } .lookup-heading { display: flex; align-items: start; justify-content: space-between; gap: 10px; border-bottom: 1px solid var(--border); padding-bottom: 12px; } .lookup-heading h2 { margin: 6px 0; font-family: Georgia, serif; font-size: 25px; } .lookup-content { font-size: calc(15px * var(--reader-font-scale)); line-height: 1.7; } .lookup-message { color: var(--notice-text); } .lookup-senses { padding-left: 22px; } .lookup-senses li { padding-left: 3px; margin-bottom: 20px; } .lookup-senses p { margin: 8px 0; } .sense-heading span { color: var(--text-muted); font-size: 13px; } .lookup-senses blockquote { border-left: 2px solid var(--status-ready-border); margin: 10px 0; padding-left: 12px; color: var(--text-faint); font-style: italic; } .lookup-range { border-bottom: 1px solid var(--border); padding-bottom: 14px; } .lookup-range p { margin: 0 0 10px; } .lookup-range-actions { display: flex; gap: 6px; flex-wrap: wrap; } /* A saved phrase is one unit: the run is underlined and keeps its status colour. */ .reader-word.is-phrase { text-decoration: underline; text-decoration-style: double; text-underline-offset: 2px; border-radius: 3px; } .reader-word.is-phrase-selected { outline: 2px solid var(--accent-marker); outline-offset: 1px; } .lookup-term { border-top: 1px solid var(--border); padding-top: 18px; margin-top: 20px; } .lookup-term label { display: flex; justify-content: space-between; gap: 10px; font-size: 14px; margin-top: 14px; } .lookup-term-title { display: flex; font-size: 14px; margin: 0; } .lookup-term label span { color: var(--sense-muted); font-size: 12px; } .lookup-term .el-radio-group { margin-top: 8px; flex-wrap: wrap; gap: 4px 12px; } .lookup-term .el-textarea { margin-top: 8px; } .lookup-term .el-textarea textarea { font: inherit; line-height: 1.6; } .lookup-saved { color: var(--saved); font-size: 14px; margin: 12px 0 0; } .lookup-actions { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; } .review-page { max-width: 680px; } .vocab-page { max-width: 760px; } .vocab-search { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 22px; } .vocab-search label { font-size: 14px; } .vocab-search .el-input { max-width: 260px; } .vocab-filter { width: 140px; } .vocab-list { list-style: none; padding: 0; margin: 18px 0 0; } .vocab-row { display: flex; align-items: center; gap: 14px; padding: 14px 16px; margin-bottom: 10px; border: 1px solid var(--border); border-radius: 10px; background: var(--surface); } .vocab-info { min-width: 0; flex: 1; } .vocab-term { margin: 0; font-family: Georgia, serif; font-size: 18px; overflow-wrap: anywhere; } .vocab-paging { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 18px; } .term-fields label { display: flex; justify-content: space-between; gap: 10px; font-size: 14px; margin-top: 14px; } .term-fields label span { color: var(--sense-muted); font-size: 12px; } .term-fields .el-textarea { margin-top: 8px; } .term-fields .el-radio-group { margin-top: 8px; flex-wrap: wrap; gap: 4px 12px; } .term-level { display: flex; align-items: center; gap: 10px; margin: 12px 0 0; font-size: 14px; } .term-level-select { width: 120px; } .chapter-notice { margin: 12px 0 0; padding: 10px 14px; border: 1px solid var(--border-strong); border-radius: 8px; background: var(--sense-bg); color: var(--sense-text); } .chapter-list .chapter-row { flex-wrap: wrap; } .review-notice { margin: 10px 0 0; padding: 10px 14px; border: 1px solid var(--border-strong); border-radius: 8px; background: var(--sense-bg); color: var(--sense-text); } .review-card, .review-summary { margin-top: 26px; padding: 28px; border: 1px solid var(--border-strong); border-radius: 14px; background: var(--surface); } .review-summary h2 { margin-top: 0; font-family: Georgia, serif; font-size: 24px; } .review-word { margin: 12px 0; font-family: Georgia, serif; font-size: calc(34px * var(--reader-font-scale)); } .review-example { margin: 12px 0; font-family: Georgia, serif; font-size: 19px; line-height: 1.8; } .review-answer { margin: 18px 0; padding-top: 18px; border-top: 1px solid var(--border); } .review-definition { margin: 0 0 12px; font-size: calc(18px * var(--reader-font-scale)); } .review-answer blockquote { border-left: 2px solid var(--status-ready-border); margin: 10px 0; padding-left: 12px; color: var(--text-faint); font-style: italic; } .review-grades { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 8px; } .review-actions { margin: 16px 0 0; } @media (max-width: 760px) { .login-page { grid-template-columns: 1fr; } .welcome { padding: 28px; } .welcome-copy, .welcome-foot { display: none; } .login-main { padding: 50px 28px 70px; align-items: start; } .site-header { padding: 20px; gap: 22px; flex-wrap: wrap; } .account { margin-left: auto; gap: 10px; } .account-name { max-width: 120px; } .site-header nav { order: 3; flex-basis: 100%; padding-top: 8px; } .library { margin-top: 32px; padding: 0 20px; } h1 { font-size: 26px; } /* Single column, tap-friendly controls and no horizontal overflow. */ .page { margin-top: 32px; padding: 0 20px; } .page-title { align-items: flex-start; } .library-title { align-items: flex-start; } .library-actions { width: 100%; justify-content: space-between; } .book-grid { grid-template-columns: 1fr; } .import-form { padding: 20px; } .chapter-row { grid-template-columns: 30px minmax(0, 1fr); align-items: start; row-gap: 10px; padding: 15px 16px; } .chapter-row .status-chip, .chapter-row .el-button { grid-column: 2; justify-self: start; } .chapter-complete { flex-direction: column; align-items: stretch; } .chapter-complete .el-button { width: 100%; } .reader-text { font-size: calc(16px * var(--reader-font-scale)); line-height: 1.95; } .has-lookup .reader-workspace { grid-template-columns: minmax(0, 1fr); gap: 20px; } .reader-page.has-lookup { padding-bottom: calc(45dvh + 28px); } .lookup-panel { position: fixed; inset: auto 0 0; z-index: 20; max-height: 45dvh; margin-top: 0; padding: 16px 20px max(20px, env(safe-area-inset-bottom)); border-radius: 16px 16px 0 0; box-shadow: 0 -5px 24px var(--text)14; } .lookup-heading { position: sticky; top: -16px; z-index: 1; background: var(--surface); } .reader-nav .el-button { flex: 1; } } /* One visible focus ring for every interactive element, including Element Plus controls. */ button:focus-visible, a:focus-visible, [role='button']:focus-visible, .el-button:focus-visible, .el-input__inner:focus-visible, .el-select__wrapper:focus-visible { outline: 3px solid var(--accent-marker); outline-offset: 3px; } /* Header display control: theme and reader font size. */ .display-trigger { display: inline-flex; align-items: center; gap: 8px; min-height: 38px; padding: 0 12px; border: 1px solid var(--border-strong); border-radius: 20px; background: var(--surface-raised); color: var(--text); font-size: 13px; cursor: pointer; } .display-trigger:hover { border-color: var(--accent); } /* Element Plus styles arrive after this file for lazy-loaded views, so the ring is stated with enough weight to win the tie while a control from its own library is focused. */ .display-trigger:focus-visible, .display-trigger.el-tooltip__trigger:focus-visible { outline: 3px solid var(--accent-marker); outline-offset: 3px; } .display-current { color: var(--text-muted); } .el-dropdown-menu__item.is-current { color: var(--accent); font-weight: 600; } /* Discoverable keyboard operations on the review page. */ .shortcut-hint { margin: 22px 0 0; color: var(--text-muted); font-size: 13px; } /* Chapter illustration: a small thumbnail that opens the original in a dialog. */ .illustration-thumb { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 6px; margin: 0 0 18px; padding: 8px; border: 1px solid var(--border); border-radius: 12px; background: var(--surface); cursor: zoom-in; } .illustration-thumb img { display: block; height: 120px; width: auto; max-width: 100%; border-radius: 8px; object-fit: cover; } .illustration-thumb:hover { border-color: var(--accent); } .illustration-thumb:focus-visible { outline: 3px solid var(--accent-marker); outline-offset: 3px; } .illustration-hint { font-size: 12px; color: var(--text-muted); } .illustration-full { display: block; max-width: min(88vw, 1200px); max-height: 78vh; width: auto; height: auto; object-fit: contain; } /* The book page keeps the cover control to one compact row so the chapter list gets the space. */ .cover-row { display: flex; align-items: center; gap: 14px; margin: 0 0 18px; } .cover-preview { flex: 0 0 120px; aspect-ratio: 16 / 9; border-radius: 10px; overflow: hidden; background: var(--accent-soft); } .cover-preview img { width: 100%; height: 100%; object-fit: cover; display: block; } .cover-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; } .cover-actions input[type='file'] { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; } .cover-actions label { position: relative; cursor: pointer; border: 1px solid var(--border-strong); border-radius: 20px; padding: 0 14px; background: var(--surface-raised); } .cover-actions label.is-busy { opacity: 0.6; cursor: progress; } /* The book title shows its author next to it. */ .title-line { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; } .title-line h1 { margin: 14px 0 6px; } .book-author { color: var(--text-muted); font-size: 15px; } /* Book covers: the card shows the stored image or a default block built from the title. */ .book-cover { display: block; aspect-ratio: 16 / 9; border-radius: 10px; overflow: hidden; background: var(--accent-soft); text-decoration: none; } .book-cover img { width: 100%; height: 100%; object-fit: cover; display: block; } .book-cover-default { display: grid; place-items: center; width: 100%; height: 100%; font-family: Georgia, serif; font-size: 40px; color: var(--accent); } /* Book attachments: metadata, preview and the upload controls. */ .attachments { margin-top: 26px; padding: 22px; border: 1px solid var(--border); border-radius: 12px; background: var(--surface); } .attachments h2 { margin: 0 0 16px; font-size: 19px; } .attachment-row { display: flex; gap: 18px; align-items: flex-start; padding: 14px 0; border-top: 1px solid var(--border); } .attachment-row:first-of-type { border-top: 0; padding-top: 0; } .attachment-preview { flex: 0 0 160px; aspect-ratio: 16 / 9; border-radius: 8px; overflow: hidden; background: var(--accent-soft); } .attachment-preview img { width: 100%; height: 100%; object-fit: cover; display: block; } .attachment-info { min-width: 0; flex: 1; } .attachment-label { margin: 0 0 4px; font-weight: 600; } .attachment-info .subtle { margin: 4px 0 0; } /* A label and its input share one line, so the dialog spends its height on the text being edited. */ .field-row { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; } .field-row label { flex: 0 0 76px; margin: 0; font-size: 14px; } .field-row .el-input { flex: 1; } /* One compact line per attachment: label, state, actions, then the size rule underneath. */ .attachment-block { display: flex; gap: 14px; align-items: flex-start; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); } .attachment-block .attachment-preview { flex: 0 0 96px; aspect-ratio: 16 / 9; border-radius: 8px; overflow: hidden; background: var(--accent-soft); } .attachment-block .attachment-info { min-width: 0; flex: 1; } .attachment-block .attachment-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0; } .attachment-block .attachment-label { font-weight: 600; } .attachment-block .subtle { margin: 2px 0 0; } .attachment-heading { margin: 22px 0 4px; font-size: 15px; font-weight: 600; color: var(--text-strong); } .attachment-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 10px 0 0; } .attachment-actions input[type='file'] { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; } .attachment-actions label { position: relative; cursor: pointer; border: 1px solid var(--border-strong); border-radius: 20px; padding: 0 14px; background: var(--surface-raised); } .attachment-actions label.is-busy { opacity: 0.6; cursor: progress; } /* Audio player: sticky so the controls stay reachable while the chapter is read. */ .audio-player { position: sticky; top: 0; z-index: 5; display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 18px; padding: 12px 16px; border: 1px solid var(--border); border-radius: 12px; background: var(--surface); box-shadow: 0 2px 10px #233d3114; } .audio-clock { font-size: 13px; color: var(--text-muted); font-variant-numeric: tabular-nums; } .audio-range { flex: 1 1 160px; min-width: 120px; accent-color: var(--accent); } .audio-rates { display: flex; gap: 4px; } .audio-rate { min-width: 44px; min-height: 32px; border: 1px solid var(--border-strong); border-radius: 16px; background: var(--surface-raised); color: var(--text); font-size: 12px; cursor: pointer; } .audio-rate.is-active { border-color: var(--accent); color: var(--accent); font-weight: 600; } .audio-error { display: inline-flex; align-items: center; gap: 6px; color: var(--notice-text); font-size: 13px; } .audio-player audio { display: none; }