/* ── [hidden] must always hide ──────────────────────────────────────────────── Any component that sets `display:flex/grid/block` on its base selector beats the UA's `[hidden]{display:none}` at equal specificity, making the element render as an empty box while "hidden". This bit .ab-char-bar, .wf-stepper, .reader-extract-banner, .ab-castpanel-inline and ~20 others individually — one root rule ends the whole bug class. */ [hidden] { display: none !important; } /* ── MDI icon integration ────────────────────────────────────────────────── */ .mdi { vertical-align: -0.125em; line-height: 1; display: inline-block; } button .mdi, a .mdi { font-size: 22px; } .nav-icon .mdi { font-size: 22px; line-height: 1; } .section-icon .mdi { font-size: 22px; line-height: 1; } .llm-card-icon .mdi { font-size: inherit; } .llm-local-icon.mdi { font-size: 22px; } .backend-metric-tag .mdi, .llm-metric-chip .mdi, .dc-metric-chip .mdi { font-size: 11px; vertical-align: -0.05em; } .link-icon { font-size: 11px; opacity: .7; margin-left: 1px; } /* ── Off-screen rendering skip (native virtualization) ─────────────────────── Long lists (300+ voices, 500+ online cards, big casts) only paint rows near the viewport. content-visibility:auto lets the browser skip layout/paint for the rest; contain-intrinsic-size reserves approximate height so the scrollbar stays stable. */ .vl-row { content-visibility: auto; contain-intrinsic-size: auto 64px; } .voice-source-card, .fa-card, .el-card { content-visibility: auto; contain-intrinsic-size: auto 320px; } .reh-cast-view-card .reh-cast-card { content-visibility: auto; contain-intrinsic-size: auto 420px; } .reh-cast-view-list .reh-cast-card { content-visibility: auto; contain-intrinsic-size: auto 72px; } /* ── Theme tokens (warm cream light theme) ──────────────────────────────── */ :root { --bg: #F5F3EE; --surface: #FFFFFF; --panel: #F3F4F6; --border: #E5E7EB; --text: #111827; --subtext: #5C6470; --placehld: #5C6470; --accent: #2563EB; --green: #16A34A; --red: #DC2626; --error: #DC2626; --yellow: #D97706; --mauve: #7C3AED; --teal: #0F766E; --on-accent:#FFFFFF; --radius: 8px; --font: 'Segoe UI', system-ui, sans-serif; --shadow: 0 2px 8px rgba(0,0,0,0.08); --sidebar-width: 220px; } [data-theme="dark"] { --bg: #111827; --surface: #1F2937; --panel: #374151; --border: #4B5563; --text: #F9FAFB; --subtext: #9CA3AF; --placehld: #6B7280; --accent: #3B82F6; --green: #22C55E; --red: #EF4444; --error: #EF4444; --yellow: #F59E0B; --mauve: #A855F7; --teal: #14B8A6; --on-accent:#FFFFFF; --shadow: 0 4px 12px rgba(0,0,0,0.5); } * { box-sizing: border-box; margin: 0; padding: 0; } html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; } /* stop iOS auto-inflating text */ body { background: var(--bg); color: var(--text); font-family: var(--font); font-size: 16px; height: 100vh; display: flex; flex-direction: column; overflow: hidden; -webkit-tap-highlight-color: transparent; } /* Dynamic viewport height — avoids the iOS Safari address-bar resize cutting off / locking the layout (a common cause of "stuck"/unscrollable mobile pages). dvh==vh on desktop. */ @supports (height: 100dvh) { body { height: 100dvh; } } /* Never allow sideways scroll on phones, and keep content clear of the notch */ @media (max-width: 768px) { body { overflow-x: hidden; } #app-shell { padding-left: env(safe-area-inset-left); padding-right: env(safe-area-inset-right); } } /* Language picker in the sidebar brand */ .app-lang-toggle { margin-left: auto; padding: 2px; border: 1px solid var(--border); border-radius: 5px; background: var(--panel); cursor: pointer; line-height: 0; display: inline-flex; align-items: center; transition: border-color .15s, transform .1s; } .app-lang-toggle:hover { border-color: var(--accent); } .app-lang-toggle:active { transform: scale(.94); } .app-lang-toggle:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(37,99,235,.18); } .app-lang-toggle .fi { width: 20px; height: 14px; border-radius: 2px; } /* ── App shell ───────────────────────────────────────────────────────────── */ #app-shell { display: flex; flex: 1; min-height: 0; overflow: hidden; } /* ── Sidebar ─────────────────────────────────────────────────────────────── */ #sidebar { width: var(--sidebar-width); flex-shrink: 0; background: var(--surface); border-right: 1px solid var(--border); display: flex; flex-direction: column; overflow-y: auto; overflow-x: hidden; box-shadow: 2px 0 12px rgba(0,0,0,0.06); } .sidebar-brand { padding: 18px 16px 14px; border-bottom: 1px solid var(--border); display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; } .sidebar-brand-text { min-width: 0; } .sidebar-collapse-btn, .sidebar-expand-btn { background: none; border: none; cursor: pointer; color: var(--subtext); padding: 4px; border-radius: 6px; font-size: 18px; line-height: 1; flex-shrink: 0; display: flex; align-items: center; justify-content: center; } .sidebar-collapse-btn:hover { background: var(--panel); color: var(--text); } #sidebar { transition: width .18s ease; } .sidebar-expand-btn { display: none; } /* ── Collapsed = slim 56 px icon rail; individual item labels appear as floating tooltips via JS (#nav-rail-tooltip) — layout never shifts ── */ @media (min-width: 768px) { body.sidebar-collapsed #sidebar { width: 56px; overflow-x: hidden; } /* Rail: hide all text/badges/chevrons, keep leading icons only */ body.sidebar-collapsed #sidebar .nav-label, body.sidebar-collapsed #sidebar .nav-badge, body.sidebar-collapsed #sidebar .ntc, body.sidebar-collapsed #sidebar .nav-chevron, body.sidebar-collapsed #sidebar .nav-group-label, body.sidebar-collapsed #sidebar .nav-group-head, body.sidebar-collapsed #sidebar .sidebar-brand-text { display: none; } body.sidebar-collapsed #sidebar .sidebar-brand { justify-content: center; padding-left: 0; padding-right: 0; } body.sidebar-collapsed #sidebar .nav-item, body.sidebar-collapsed #sidebar .nav-tree-item, body.sidebar-collapsed #sidebar .nav-tree-head { padding-left: 18px; padding-right: 0; gap: 0; white-space: nowrap; font-size: 0; } body.sidebar-collapsed #sidebar .nav-item .mdi, body.sidebar-collapsed #sidebar .nav-tree-item .mdi, body.sidebar-collapsed #sidebar .nav-tree-head .mdi { font-size: 15px; } body.sidebar-collapsed #sidebar .nav-tree .nav-tree-item, body.sidebar-collapsed #sidebar .nav-tree .nav-tree-head.nav-subhead, body.sidebar-collapsed #sidebar .nav-tree .nav-tree .nav-tree-item { padding-left: 18px; } } /* Floating label tooltip for icon-rail items */ #nav-rail-tooltip { position: fixed; z-index: 200; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 5px 11px; font-size: 13px; font-weight: 500; color: var(--text); white-space: nowrap; pointer-events: none; display: none; box-shadow: 2px 4px 14px rgba(0,0,0,.18); transform: translateY(-50%); } @media (max-width: 767px) { .sidebar-collapse-btn, .sidebar-expand-btn { display: none !important; } } .sidebar-brand h1 { font-size: 15px; font-weight: 800; color: var(--accent); letter-spacing: -0.01em; line-height: 1.2; display: flex; align-items: center; gap: 6px; } .sidebar-brand p { font-size: 14px; color: var(--subtext); margin-top: 3px; } .sidebar-nav { flex: 1; padding: 8px 0; } .nav-group-label { padding: 10px 16px 4px; font-size: 10px; font-weight: 800; color: var(--subtext); text-transform: uppercase; letter-spacing: 0.09em; } /* ── Sidebar tree (My Voices) ────────────────────────────────────────────── */ .nav-tree-head { position: relative; } .nav-label { flex: 1; } .nav-badge { font-size: 10.5px; font-weight: 700; color: var(--subtext); background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 1px 6px; min-width: 18px; text-align: center; } .nav-chevron { font-size: 14px; color: var(--subtext); transition: transform .2s; margin-left: 2px; } .nav-casting-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); flex-shrink: 0; margin-left: auto; animation: pulse 1s infinite; } .nav-chevron.open { transform: rotate(180deg); } .nav-tree { display: none; flex-direction: column; padding: 2px 0 6px; } .nav-tree.open { display: flex; } .nav-tree-item { display: flex; align-items: center; gap: 7px; padding: 5px 16px 5px 32px; font-size: 12.5px; color: var(--subtext); cursor: pointer; transition: background .1s, color .1s; } .nav-tree-item .mdi { font-size: 14px; flex-shrink: 0; } .nav-tree-item:hover { background: var(--panel); color: var(--text); } .nav-tree-item.is-active { color: var(--accent); font-weight: 700; } /* Only show sub-item highlight when the parent section head is the CURRENT page. When you're on a different section the remembered sub-item stays stored but should not look "active" — it just creates visual noise. */ .nav-item:not(.active) ~ .nav-tree .nav-tree-item.is-active, .nav-item:not(.active) ~ .nav-tree .nav-tree-head.nav-subhead.active { color: var(--subtext); font-weight: normal; } /* Suppress sub-item highlight when its section-head is not the current section. Covers Language Models, Books, Stage etc. that are hardcoded is-active in HTML. */ [data-nav-section]:not(.active) + .nav-tree .nav-tree-item.is-active { color: var(--subtext); background: transparent; font-weight: normal; } /* ── Background-process busy indicator ────────────────────────────────────── A small animated dot shown on a nav-item or nav-tree-head when a background task (synthesis, benchmark, cast, playback) is running in that section. Set via window.setNavBusy(sectionId, true|false) in nav.js. */ @keyframes nav-busy-pulse { 0%,100%{opacity:.5;transform:scale(.9)} 50%{opacity:1;transform:scale(1.15)} } .nav-busy-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); flex-shrink: 0; margin-left: auto; animation: nav-busy-pulse 1.2s ease-in-out infinite; } /* Current page/section within its parent group */ .nav-tree-item.active { background: rgba(37,99,235,0.08); color: var(--accent); font-weight: 700; border-right: 3px solid var(--accent); } /* Virtual tag subfolders under My Voices */ .nav-tree-tag-label { padding: 7px 16px 3px 32px; font-size: 9.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--subtext); pointer-events: none; } .nav-tree-item.nav-tree-tag { padding-left: 38px; } .nav-tree-item.nav-tree-tag .mdi { font-size: 13px; opacity: .8; } .ntc { font-size: 14px; color: var(--subtext); font-weight: 500; } /* ── Collapsible group headers (Voice Actions · Speak · Setup) ───────────── */ .nav-group-head { display: flex; align-items: center; gap: 8px; cursor: pointer; padding: 10px 16px 4px; margin-top: 2px; font-size: 10px; font-weight: 800; color: var(--subtext); text-transform: uppercase; letter-spacing: 0.09em; user-select: none; } .nav-group-head .nav-label { flex: 1; } .nav-group-head:hover { color: var(--text); } .nav-group-head .nav-chevron { font-size: 13px; } /* ── Sub-headers nested inside a tree (Tags · Script Rehearsal · Library · Engines · Integrations · Settings) — look like items, carry a chevron ── */ .nav-tree .nav-tree-head.nav-subhead { display: flex; align-items: center; gap: 7px; cursor: pointer; padding: 5px 16px 5px 32px; font-size: 12.5px; color: var(--subtext); transition: background .1s, color .1s; } .nav-tree .nav-tree-head.nav-subhead .nav-icon .mdi, .nav-tree .nav-tree-head.nav-subhead > .mdi { font-size: 14px; flex-shrink: 0; } .nav-tree .nav-tree-head.nav-subhead .nav-label { flex: 1; } .nav-tree .nav-tree-head.nav-subhead:hover { background: var(--panel); color: var(--text); } .nav-tree .nav-tree-head.nav-subhead.active { color: var(--accent); font-weight: 700; } /* Nested trees indent one extra level per depth */ .nav-tree .nav-tree .nav-tree-item, .nav-tree .nav-tree .nav-tree-head.nav-subhead { padding-left: 44px; } .nav-tree .nav-tree .nav-tree .nav-tree-item { padding-left: 56px; } .nav-tree-item.is-active .ntc { color: var(--accent); } .nav-item { display: flex; align-items: center; gap: 10px; padding: 9px 16px; cursor: pointer; color: var(--subtext); font-size: 13.5px; font-weight: 500; transition: background 0.12s, color 0.12s; border-right: 3px solid transparent; user-select: none; } .nav-item:hover { background: var(--panel); color: var(--text); } .nav-item.active { background: rgba(37,99,235,0.08); color: var(--accent); font-weight: 700; border-right-color: var(--accent); } .nav-icon { font-size: 15px; width: 18px; text-align: center; flex-shrink: 0; } .sidebar-footer { padding: 12px 16px; border-top: 1px solid var(--border); display: flex; gap: 8px; align-items: center; } .sidebar-footer button { background: var(--panel); border: 1px solid var(--border); color: var(--subtext); border-radius: var(--radius); padding: 6px 10px; font-size: 13px; cursor: pointer; flex: 1; transition: background 0.12s, color 0.12s; } .sidebar-footer button:hover { background: var(--border); color: var(--text); } /* ── Main content ────────────────────────────────────────────────────────── */ #main-content { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 0 28px 0; min-width: 0; } /* ── Page sections ───────────────────────────────────────────────────────── */ .page-section { padding-top: 7px; display: none; } .page-section.is-active { display: block; } .section-head { display: flex; align-items: center; gap: 14px; padding: 20px; border-bottom: 2px solid var(--border); margin-bottom: 20px; background: var(--surface); border-radius: var(--radius); } .section-icon { font-size: 26px; line-height: 1; } .section-title h2 { font-size: 20px; font-weight: 800; color: var(--text); letter-spacing: -0.02em; margin: 0; } .section-title p { font-size: 13px; color: var(--subtext); margin: 3px 0 0; line-height: 1.4; } /* Section head right-side actions (search + button) */ .section-head-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; flex-shrink: 0; } /* Previous/Next workflow shortcut, auto-injected top-right on every section header — same navigation as the stepper widget further down, without needing to scroll to it first. */ .wf-header-nav { display: flex; align-items: center; gap: 8px; margin-left: auto; flex-shrink: 0; } .wf-header-nav-btn { display: inline-flex; align-items: center; gap: 4px; padding: 7px 14px; border-radius: 8px; border: 1px solid var(--border); background: var(--panel); color: var(--text); font-size: 13px; font-weight: 600; cursor: pointer; } .wf-header-nav-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); background: rgba(37,99,235,.08); } .wf-header-nav-btn:disabled { opacity: .35; cursor: not-allowed; } .wf-header-nav-label { max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* Guidance bar under the section header: what to do on this step, and what either Previous/Next actually leads to — sits right where the user's eye already is after reading the Previous/Next buttons. */ .wf-step-hint { display: flex; align-items: baseline; gap: 10px; padding: 8px 20px; margin: -12px 0 20px; background: var(--panel); border: 1px solid var(--border); border-top: none; border-radius: 0 0 var(--radius) var(--radius); font-size: 12.5px; color: var(--subtext); line-height: 1.4; } /* Studio's own Previous/Next (not .wf-header-nav — see s-caststudio.html) */ .stu-phase-nav { display: flex; align-items: center; gap: 8px; margin-left: auto; flex-shrink: 0; } .stu-phase-nav-btn { display: inline-flex; align-items: center; gap: 4px; padding: 7px 14px; border-radius: 8px; border: 1px solid var(--border); background: var(--panel); color: var(--text); font-size: 13px; font-weight: 600; cursor: pointer; } .stu-phase-nav-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); background: rgba(37,99,235,.08); } .stu-phase-nav-btn:disabled { opacity: .35; cursor: not-allowed; } .wf-step-hint-badge { flex-shrink: 0; font-weight: 700; color: var(--accent); font-size: 11px; letter-spacing: .02em; text-transform: uppercase; } .wf-step-hint-text { flex: 1; min-width: 0; } .section-search { width: 220px; padding: 6px 12px; font-size: 13px; font-family: var(--font); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); transition: border-color .15s; } .section-search:focus, .section-search:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(37,99,235,.12); } .section-new-voice-btn { padding: 6px 14px; font-size: 13px; white-space: nowrap; } .section-search::placeholder { color: var(--placehld); } /* ── Force all tab-content visible ──────────────────────────────────────── */ .tab-content { display: flex !important; flex-direction: column; gap: 18px; padding: 0; } #tab-library { min-height: 0; } /* Hide noisy sub-panels we don't need in this layout */ .library-benchmark-panel { display: none !important; } #library-filter-panel { display: none; } #disabled-info { display: none; } /* ── Cards ──────────────────────────────────────────────────────────────── */ .card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; display: flex; flex-direction: column; gap: 14px; box-shadow: var(--shadow); min-width: 0; max-width: 100%; } .card h2 { font-size: 12px; font-weight: 700; color: var(--subtext); text-transform: uppercase; letter-spacing: .09em; } /* ── Collapsible cards ───────────────────────────────────────────────────── */ .card-collapse-h2 { cursor: pointer; user-select: none; display: flex; align-items: center; gap: 7px; } .card-collapse-h2:hover { opacity: .8; } .card-chev { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 18px; height: 18px; border-radius: 4px; background: var(--panel); border: 1px solid var(--border); color: var(--accent); font-size: 13px; line-height: 1; transition: transform .2s; } .card-col-closed .card-chev { transform: rotate(-90deg); } /* Sync
inside cards to the same chevron look */ .card > details > summary { list-style: none; cursor: pointer; user-select: none; display: flex; align-items: center; gap: 7px; } .card > details > summary::marker, .card > details > summary::-webkit-details-marker { display: none; } .card > details > summary::before { content: '▾'; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 16px; height: 16px; border-radius: 4px; background: var(--panel); border: 1px solid var(--border); color: var(--accent); font-size: 10px; transition: transform .2s; } .card > details:not([open]) > summary::before { transform: rotate(-90deg); } /* ── Drop zone ──────────────────────────────────────────────────────────── */ /* Clone source picker — segmented File / URL / Mic */ /* Integrated tab box: the tab strip and the panel below read as one unit */ .clone-tabbox { display: flex; flex-direction: column; } .clone-src-picker { display: flex; gap: 4px; margin: 0 0 -1px; align-items: flex-end; padding: 0 4px; border-bottom: 1px solid var(--border); position: relative; z-index: 1; } .clone-src-tab { display: inline-flex; align-items: center; justify-content: center; gap: 7px; padding: 11px 18px; border: 1px solid transparent; border-bottom: none; background: transparent; color: var(--subtext); font-size: 14px; font-weight: 600; border-radius: 10px 10px 0 0; cursor: pointer; transition: background .15s, color .15s, border-color .15s; font-family: var(--font); position: relative; } .clone-src-tab:hover { color: var(--text); background: color-mix(in srgb, var(--surface) 60%, transparent); } .clone-src-tab.active { background: var(--surface); color: var(--accent); border-color: var(--border); border-bottom-color: var(--surface); } .clone-src-tab.active::after { content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 2px; background: var(--accent); border-radius: 2px 2px 0 0; } .clone-src-tab .mdi { font-size: 18px; } .clone-srcpanel { background: var(--surface); border: 1px solid var(--border); border-radius: 0 var(--radius) var(--radius) var(--radius); padding: 20px; box-shadow: var(--shadow); } .clone-src-intro { color: var(--subtext); font-size: 13px; margin: 0 0 14px; display: flex; align-items: center; gap: 7px; } .clone-src-intro .mdi { font-size: 16px; color: var(--accent); } .clone-id-row { display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-end; } /* author `display` on .card overrides the UA [hidden] rule — restore it */ .clone-src-card[hidden] { display: none; } @media (max-width: 560px) { .clone-src-tab span:not(.mdi) { display: none; } } #drop-zone { border: 2px dashed var(--border); border-radius: var(--radius); padding: 44px 20px; text-align: center; cursor: pointer; color: var(--subtext); transition: border-color .2s, background .2s; font-size: 15px; } #drop-zone.drag-over { border-color: var(--accent); background: rgba(37,99,235,.05); color: var(--accent); } #drop-zone span { display: block; font-size: 13px; margin-top: 9px; } #drop-zone input[type=file] { display: none; } /* ── Buttons ────────────────────────────────────────────────────────────── */ button { cursor: pointer; border: none; border-radius: var(--radius); padding: 9px 18px; font-size: 14px; font-family: inherit; transition: opacity .15s, filter .15s; } button:disabled { opacity: .4; cursor: default; } button:not(:disabled):hover { filter: brightness(0.95); } .btn-primary { background: var(--accent); color: var(--on-accent); font-weight: 600; } .btn-secondary { background: var(--panel); color: var(--text); border: 1px solid var(--border); } .btn-danger { background: var(--error); color: #fff; font-weight: 600; } .btn-green { background: #15803D; color: var(--on-accent); font-weight: 600; } .btn-red { background: var(--red); color: var(--on-accent); font-weight: 600; } .btn-teal { background: var(--teal); color: var(--on-accent); font-weight: 600; } .btn-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; } /* ── Waveform ───────────────────────────────────────────────────────────── */ #waveform-wrap { background: var(--panel); border-radius: var(--radius); overflow: hidden; } #waveform { min-height: 90px; } .time-row { display: flex; gap: 18px; align-items: center; flex-wrap: wrap; } .time-row label { color: var(--subtext); font-size: 13px; display: flex; align-items: center; gap: 7px; } .time-row input[type=number] { background: var(--panel); border: 1px solid var(--border); color: var(--text); border-radius: 4px; padding: 6px 10px; width: 100px; font-size: 14px; } #trim-duration { font-size: 14px; padding: 6px 14px; border-radius: 4px; font-weight: 600; } .dur-ok { background: rgba(22,163,74,.12); color: var(--green); } .dur-warn { background: rgba(217,119,6,.12); color: var(--yellow); } .dur-bad { background: rgba(220,38,38,.12); color: var(--red); } /* ── URL row ────────────────────────────────────────────────────────────── */ .url-row { display: flex; gap: 8px; } .url-row input[type=text] { flex: 1; background: var(--panel); border: 1px solid var(--border); color: var(--text); border-radius: var(--radius); padding: 9px 14px; font-size: 14px; } input::placeholder, textarea::placeholder { color: var(--placehld); } #yt-progress { font-size: 13px; color: var(--subtext); min-height: 1.4em; font-family: monospace; } /* ── Recording ──────────────────────────────────────────────────────────── */ #rec-indicator { display: none; align-items: center; gap: 8px; color: var(--red); font-size: 14px; } #rec-indicator.active { display: flex; } #rec-dot { width: 10px; height: 10px; background: var(--red); border-radius: 50%; animation: pulse 1s infinite; } @keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .3; } } /* ── Voice ID field ─────────────────────────────────────────────────────── */ .voice-id-row input[type=text] { flex: 1; background: var(--panel); border: 1px solid var(--border); color: var(--text); border-radius: var(--radius); padding: 10px 14px; font-size: 15px; font-family: monospace; font-weight: 600; width: 100%; } .voice-id-row { display: flex; gap: 8px; } input.id-valid { border-color: var(--green) !important; } input.id-invalid { border-color: var(--red) !important; } /* ── Helper details ─────────────────────────────────────────────────────── */ details.helper { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); } details.helper summary { padding: 10px 14px; cursor: pointer; font-size: 13px; color: var(--subtext); user-select: none; list-style: none; } details.helper summary::before { content: '\25B6 '; font-size: 14px; } details.helper[open] summary::before { content: '\25BC '; } details.helper .helper-body { padding: 12px 14px 14px; display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; } .field { display: flex; flex-direction: column; gap: 5px; } .field label { font-size: 12px; color: var(--subtext); font-weight: 600; letter-spacing: .03em; text-transform: uppercase; } .field select, .field input[type=text] { background: var(--bg); border: 1px solid var(--border); color: var(--text); border-radius: var(--radius); padding: 8px 12px; font-size: 14px; } /* ── Textareas ──────────────────────────────────────────────────────────── */ #transcript-area, #preview-text-area, #design-instruct, #design-sample-text { width: 100%; background: var(--panel); border: 1px solid var(--border); color: var(--text); border-radius: var(--radius); padding: 10px 14px; font-size: 14px; font-family: inherit; resize: vertical; } #transcript-area { min-height: 90px; } #preview-text-area { min-height: 72px; } #design-instruct { min-height: 110px; } #design-sample-text { min-height: 56px; } audio { width: 100%; } @media (min-width: 900px) { #clone-sample-text, #lib-add-sample-text { min-height: 190px; font-size: 22px; line-height: 1.65; } } .tryout-voice-row { display: grid; grid-template-columns: minmax(150px,1fr) minmax(230px,1.7fr) minmax(150px,1fr); gap: 12px; align-items: end; } .tryout-voice-row .field select { width: 100%; min-width: 0; } .tryout-voice-pick { display: flex; gap: 6px; align-items: stretch; min-width: 0; } .tryout-voice-pick select { flex: 1 1 auto; min-width: 0; } .tryout-voice-pick .vp-root { flex: 1 1 auto; min-width: 0; } /* searchable widget replaces the select */ .conv-config-group .vp-root[data-vp-for="conv-tts-voice-select"] { min-width: min(360px, 100%); flex: 1 1 360px; } .conv-config-group .vp-root[data-vp-for="conv-tts-voice-select"] .vp-drop { z-index: 120; } @media (max-width: 900px) { .conv-config-group .vp-root[data-vp-for="conv-tts-voice-select"] { width: 100%; min-width: 0; } } .tryout-voice-pick .btn-sm { flex: 0 0 auto; padding: 0 11px; display: inline-flex; align-items: center; } @media (max-width: 720px) { .tryout-voice-row { grid-template-columns: 1fr; } } /* ── Settings pages ─────────────────────────────────────────────────────── */ .s-settings-page { display:none; flex-direction:column; gap:16px; } .s-settings-page.is-active { display:flex; } .s-engines-page { display:none; flex-direction:column; gap:16px; } .s-engines-page.is-active { display:flex; } /* Combined Library — tabs + panels */ .lib-tabs { display:flex; gap:6px; margin:4px 0 16px; flex-wrap:wrap; } .lib-tab { display:inline-flex; align-items:center; gap:6px; padding:8px 14px; border:1px solid var(--border); border-radius:8px; background:var(--surface); color:var(--subtext); font-size:13px; font-weight:500; cursor:pointer; transition:background .14s ease, color .14s ease, border-color .14s ease; } .lib-tab:hover { color:var(--text); border-color:var(--accent); } .lib-tab.is-active { background:var(--accent); border-color:var(--accent); color:#fff; } .lib-panel { display:none; } .lib-panel.is-active { display:block; } .lib-empty { display:flex; flex-direction:column; align-items:center; gap:8px; padding:40px 0; color:var(--subtext); text-align:center; } .lib-empty .mdi { font-size:42px; opacity:.4; } .lib-empty p { margin:0; font-size:13px; line-height:1.5; } .lib-empty-hint { opacity:.8; } .lib-chars-loading { padding:32px; text-align:center; color:var(--subtext); font-size:13px; } /* ── Characters / Cast view ─────────────────────────────────────────────────── */ .lib-chars-toolbar { display:flex; gap:8px; margin-bottom:16px; flex-wrap:wrap; align-items:center; } .lib-chars-toolbar #lib-chars-back-reader { margin-right:4px; } .lib-chars-visual-review { margin:0 0 12px; padding:10px 12px; border:1px solid color-mix(in srgb, var(--accent) 35%, var(--border)); border-radius:8px; color:var(--subtext); line-height:1.5; } .lib-chars-review-item { display:inline-block; margin-left:10px; } .lib-chars-sort { display:flex; align-items:center; gap:6px; font-size:12.5px; color:var(--subtext); font-weight:600; } .lib-chars-sort select { padding:5px 8px; border-radius:6px; border:1px solid var(--border); background:var(--surface); color:var(--text); font-size:12.5px; cursor:pointer; } .lib-chars-view-toggle { display:flex; gap:2px; margin-left:auto; } .lib-chars-view-toggle button.is-active { background:var(--accent); color:#fff; border-color:var(--accent); } /* Table view — dense alternative to the card grid for scanning a large cast */ .lib-chars-tbl-wrap { width:100%; max-width:100%; min-width:0; overflow-x:scroll; overflow-y:hidden; border:1px solid var(--border); border-radius:12px; background:var(--surface); position:relative; scrollbar-gutter:stable; scrollbar-width:auto; } .lib-chars-tbl-wrap::-webkit-scrollbar { height:12px; } .lib-chars-tbl-wrap::-webkit-scrollbar-track { background:var(--panel); } .lib-chars-tbl-wrap::-webkit-scrollbar-thumb { background:var(--subtext); border-radius:8px; border:3px solid var(--panel); } .lib-chars-column-chooser { position:relative; display:flex; justify-content:flex-end; margin:0 0 6px; } .lib-chars-column-button { display:inline-flex; align-items:center; gap:5px; } .lib-chars-column-menu { display:none; position:absolute; top:calc(100% + 4px); right:0; z-index:20; min-width:190px; padding:8px; border:1px solid var(--border); border-radius:8px; background:var(--surface); box-shadow:0 8px 24px rgba(0,0,0,.18); } .lib-chars-column-menu.is-open { display:block; } .lib-chars-column-menu label { display:flex; align-items:center; gap:5px; padding:3px 2px; color:var(--text); font-size:12px; white-space:nowrap; cursor:pointer; } .lib-chars-column-menu label:hover { background:var(--panel); border-radius:4px; } .lib-chars-column-menu input { accent-color:var(--accent); margin:0; } .lib-chars-tbl .lib-chars-col-hidden { display:none !important; } .lib-chars-tbl { width:100%; min-width:2470px; table-layout:fixed; border-collapse:collapse; font-size:12.9px; white-space:nowrap; } .lib-chars-tbl td { overflow: hidden; text-overflow: ellipsis; } .lib-chars-tbl thead th { text-align:left; padding:8px 8px; font-size:10.5px; font-weight:800; text-transform:uppercase; letter-spacing:.04em; color:var(--subtext); border-bottom:1px solid var(--border); background:var(--panel); position:sticky; top:0; z-index:1; } .lib-chars-tbl thead th.lib-chars-tbl-sortable { cursor: pointer; user-select: none; } .lib-chars-tbl thead th.lib-chars-tbl-sortable:hover { color: var(--accent); } .lib-chars-tbl thead th.lib-chars-tbl-sortable .mdi { font-size: 11px; vertical-align: -1px; } .lib-chars-tbl td { padding:8px 8px; border-bottom:1px solid var(--border); vertical-align:middle; } .lib-chars-tbl th:nth-child(1), .lib-chars-tbl td:nth-child(1), .lib-chars-tbl th:nth-child(2), .lib-chars-tbl td:nth-child(2), .lib-chars-tbl th:nth-child(3), .lib-chars-tbl td:nth-child(3) { position: sticky; background: var(--surface); } .lib-chars-tbl thead th:nth-child(1), .lib-chars-tbl thead th:nth-child(2), .lib-chars-tbl thead th:nth-child(3) { background: var(--panel); } .lib-chars-tbl th:nth-child(1), .lib-chars-tbl td:nth-child(1) { left: 0; z-index: 5; width: 28px; min-width: 28px; } .lib-chars-tbl th:nth-child(2), .lib-chars-tbl td:nth-child(2) { left: 28px; z-index: 4; width: 40px; min-width: 40px; } .lib-chars-tbl th:nth-child(3), .lib-chars-tbl td:nth-child(3) { left: 68px; z-index: 3; width: 260px; min-width: 260px; box-shadow: 10px 0 14px -14px rgba(0,0,0,.28); } .lib-chars-tbl-row:hover td:nth-child(1), .lib-chars-tbl-row:hover td:nth-child(2), .lib-chars-tbl-row:hover td:nth-child(3) { background: var(--panel); } /* Rows reuse .lib-char-card so the existing per-card click/voice/export wiring picks them up, but that class's display:flex;flex-direction:column (meant for the card grid) turned every into a flex column, stacking its s vertically instead of laying the table out in columns. Reset it back to a real table row, and undo the card-specific chrome that leaked in with it. */ tr.lib-char-card.lib-chars-tbl-row { display: table-row; cursor: pointer; background: none; border: 0; border-radius: 0; box-shadow: none; transition: none; /* Let Chromium skip layout/paint for distant rows while retaining the full DOM, so sorting, bulk selection, and row event wiring remain unchanged. */ content-visibility: auto; contain-intrinsic-size: auto 86px; } tr.lib-char-card.lib-chars-tbl-row:hover { transform: none; box-shadow: none; background: var(--panel); } .lib-chars-tbl-row:hover { background:var(--panel); } .lib-chars-tbl td.lib-chars-tbl-avatar-cell { overflow: visible; text-overflow: clip; padding-left: 6px; padding-right: 2px; } /* Same specificity as the base .lib-char-avatar rule (card-grid sizing) but declared earlier in the file, so without the extra qualifiers here the later, larger card-grid rule silently won the cascade — table rows ended up with ~90px avatars overlapping the name text instead of these 26px ones. Ancestor + two classes beats the single-class rule regardless of source order. */ .lib-chars-tbl-avatar-cell .lib-char-avatar.lib-chars-tbl-avatar { width: 48px; height: 48px; font-size: 18px; border: none; box-shadow: 0 1px 4px rgba(0,0,0,.10); border-radius: 50%; overflow: hidden; } .lib-chars-tbl-row .lib-char-export { position:static; opacity:1; width:auto; height:auto; border:0; background:none; color:var(--subtext); padding:2px; } .lib-chars-tbl-row .lib-char-export:hover { color:var(--accent); background:none; border:0; } .lib-chars-tbl-name { font-weight:600; white-space:normal; min-width:240px; } .lib-chars-tbl-name-main { display:flex; align-items:center; gap:8px; flex-wrap:wrap; line-height:1.15; } .lib-chars-tbl-sub { margin-top:3px; font-size:11.5px; color:var(--subtext); white-space:normal; line-height:1.35; } .lib-chars-tbl-book { margin-top:4px; font-size:11px; color:var(--accent); white-space:normal; line-height:1.35; font-weight:600; } /* Two stacked rows (voice name+play, then Auswahl/Auto) instead of one — a long voice id used to push the action buttons out of the visible column width entirely, confirmed live. */ .lib-chars-tbl-voice-wrap { display:flex; flex-direction:column; align-items:flex-start; gap:4px; } .lib-chars-tbl-voice-row { display:flex; align-items:center; gap:6px; flex-wrap:wrap; max-width:100%; } .lib-chars-tbl-voice button { padding:2px 7px; font-size:11px; } /* display:flex directly on a breaks its table-cell layout participation (Chrome renders it detached from the column grid — width/position stop matching its own header entirely). Flex only the inner wrapper instead. */ .lib-chars-tbl-tags { white-space:normal; max-width:260px; } .lib-chars-tbl-tags-wrap { display:flex; flex-wrap:wrap; gap:3px; } .lib-chars-tbl-profile { text-align:center; } .lib-chars-profile-count { display:inline-block; min-width:54px; padding:3px 6px; border-radius:999px; font-size:11.5px; font-weight:800; font-variant-numeric:tabular-nums; border:1px solid var(--border); background:var(--panel); color:var(--subtext); } .lib-chars-profile-count.is-sparse { color:#b42318; background:rgba(239,68,68,.10); border-color:rgba(239,68,68,.35); } .lib-chars-profile-count.is-partial { color:#a15c00; background:rgba(245,158,11,.12); border-color:rgba(245,158,11,.4); } .lib-chars-profile-count.is-strong { color:#137333; background:rgba(34,197,94,.10); border-color:rgba(34,197,94,.35); } .lib-chars-tbl-rating { display:inline-flex; gap:1px; } .lib-chars-tbl-rating-star { border:0; background:transparent; padding:1px; color:#d1d5db; cursor:pointer; line-height:1; } .lib-chars-tbl-rating-star:hover, .lib-chars-tbl-rating-star.is-on { color:#f59e0b; } /* Phones: the pinned checkbox/avatar/name block (328px) would cover the rating stars on a 320-414px screen, so it scrolls with the table there; the stars get 44px touch targets. */ @media (max-width: 640px) { .lib-chars-tbl th:nth-child(1), .lib-chars-tbl td:nth-child(1), .lib-chars-tbl th:nth-child(2), .lib-chars-tbl td:nth-child(2), .lib-chars-tbl th:nth-child(3), .lib-chars-tbl td:nth-child(3) { position: static; box-shadow: none; } .lib-chars-tbl thead th:nth-child(n+1) { position: sticky; top: 0; left: auto; } .lib-chars-tbl { min-width:2580px; } .lib-chars-tbl col:nth-child(11) { width:232px !important; } .lib-chars-tbl-rating-star { min-width:44px; min-height:44px; padding:0; display:inline-flex; align-items:center; justify-content:center; font-size:18px; } } .lib-chars-tbl-prompt { color:var(--subtext); font-size:11.5px; font-style:italic; white-space:normal; overflow-wrap:anywhere; word-break:normal; line-height:1.4; vertical-align:top; } .lib-chars-tbl-note-cell { padding:5px 8px !important; } .lib-chars-tbl-voice-note { display:block; width:100%; min-width:0; min-height:42px; resize:vertical; border:1px solid transparent; background:transparent; color:var(--text); padding:4px 5px; border-radius:5px; font:inherit; line-height:1.35; white-space:pre-wrap; overflow-wrap:anywhere; } .lib-chars-tbl-voice-note:hover, .lib-chars-tbl-voice-note:focus { border-color:var(--border); background:var(--panel); outline:none; } .lib-chars-tbl-voice-note:focus { min-height:64px; } .lib-chars-tbl-dash { opacity:.4; } .lib-chars-tbl-check { display:inline-flex; } .lib-chars-tbl-check.is-yes { color:#4caf50; } .lib-chars-tbl-check.is-no { color:var(--subtext); opacity:.35; } .lib-chars-tbl td .lib-char-align-bar { width:60px; flex:none; display:inline-block; } .lib-chars-production { margin-bottom:28px; border-radius: 12px; transition: box-shadow .3s, background .3s; } .lib-chars-production-highlight { box-shadow: 0 0 0 3px var(--accent); background: color-mix(in srgb, var(--accent) 6%, transparent); } /* Compact single-line list row instead of a bordered box per fact — the card body has very little room once the banner became a square (see .lib-char-card's playing-card aspect-ratio), so a table/list layout that only spends space on facts that actually have a value fits far more in. */ .lib-char-stat-row { display:flex; align-items:baseline; gap:7px; min-width:0; padding:4px 0; border-bottom:1px solid rgba(0,0,0,.06); font-size:12.5px; line-height:1.35; } .lib-char-stat-icon { flex-shrink:0; font-size:12px; color:var(--accent); align-self:center; } .lib-char-stat-label { flex-shrink:0; text-transform:uppercase; letter-spacing:.06em; font-size:10px; font-weight:800; color:var(--subtext); width:72px; } .lib-char-stat-value { min-width:0; font-weight:600; color:var(--text); overflow-wrap:anywhere; } .lib-chars-prod-head { display: flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:8px; padding:10px 14px; border-radius:8px 8px 0 0; gap:10px; background: linear-gradient(90deg, var(--pk1,#4f46e5), var(--pk2,#7c3aed)); color: #fff; cursor:pointer; } .lib-chars-prod-collapse-btn { flex-shrink:0; width:24px; height:24px; padding:0; border:0; border-radius:6px; background:rgba(255,255,255,.15); color:#fff; cursor:pointer; display:flex; align-items:center; justify-content:center; } .lib-chars-prod-collapse-btn:hover { background:rgba(255,255,255,.28); } .lib-chars-prod-collapse-btn .mdi { transition:transform .18s; } .lib-chars-production-collapsed .lib-chars-prod-collapse-btn .mdi { transform:rotate(-90deg); } .lib-chars-production-collapsed .lib-chars-prod-head { border-radius:8px; } .lib-chars-production-collapsed .lib-chars-prod-body { display:none; } .lib-chars-prod-title { font-weight:600; font-size:14px; display:flex; align-items:center; gap:8px; } .lib-chars-prod-title .mdi { opacity:.8; } .lib-chars-prod-actions { display:flex; gap:6px; flex-wrap:wrap; } .lib-chars-prod-actions .btn-secondary { background: rgba(255,255,255,.15); border-color: rgba(255,255,255,.3); color:#fff; font-size:11px; } .lib-chars-prod-actions .btn-secondary:hover { background: rgba(255,255,255,.28); } .lib-chars-prod-actions .lib-chars-bulk-delete-btn:not(:disabled) { border-color: rgba(255,120,120,.6); } .lib-chars-prod-actions .lib-chars-bulk-delete-btn:not(:disabled):hover { background: rgba(220,60,60,.35); } .lib-chars-prod-actions .lib-chars-image-provider { background: rgba(255,255,255,.15); border: 1px solid rgba(255,255,255,.3); color:#fff; font-size: 11px; border-radius: 5px; padding: 0 6px; height: 26px; font-family: inherit; } .lib-chars-prod-actions .lib-chars-image-provider option { color: #111; } .lib-chars-prod-actions .lib-chars-visual-style, .lcd-visual-style { background: var(--panel, #fff); border: 1px solid var(--border); color: var(--text, #222); font-size: 11px; border-radius: 5px; padding: 0 7px; height: 28px; font-family: inherit; } .lib-chars-prod-actions .lib-chars-visual-style option, .lcd-visual-style option { color: #111; } .lib-chars-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 18px; padding:18px; background: var(--panel); border: 1px solid var(--border); border-top: none; border-radius: 0 0 8px 8px; } /* Portrait-style character card */ .lib-char-card { display: flex; flex-direction:column; align-items:stretch; background: var(--surface); cursor:pointer; position:relative; border-radius:16px; overflow:hidden; border:1px solid var(--border); transition: transform .13s, box-shadow .13s; box-shadow: 0 10px 24px rgba(0,0,0,.12); /* Standard playing-card proportion (2.5:3.5) so every card in the grid reads as a uniform deck regardless of how much fact/text content each character has — the body below flexes to fill whatever's left under the square banner instead of driving the card's total height. */ aspect-ratio: 5 / 7; /* A production's full character grid (each card carrying a generated portrait image) stays fully laid out and painted at once with no virtualization, the same category of issue confirmed and fixed for the casting view's segment list — matches part of what was running when a later crash was reported (bulk profile/portrait/voice generation across dozens of characters). Same proven fix, same reasoning: off-screen cards stop costing layout/paint, nothing else about the page changes. */ content-visibility: auto; contain-intrinsic-size: auto 320px; } .lib-char-card:hover { transform:translateY(-4px); box-shadow:0 14px 34px rgba(0,0,0,.18); } .lib-char-select { position:absolute; top:8px; left:8px; z-index:3; cursor:pointer; width:20px; height:20px; display:flex; align-items:center; justify-content:center; background:rgba(0,0,0,.35); border-radius:5px; } .lib-char-select-cb { width:15px; height:15px; cursor:pointer; } /* Coloured banner at top of each card */ .lib-char-card-banner { aspect-ratio: 1 / 1; height:auto; flex-shrink:0; display:flex; align-items:flex-end; justify-content:flex-start; position:relative; gap:14px; padding:16px; overflow:hidden; background: linear-gradient(135deg, var(--ch1,#4f46e5) 0%, var(--ch2,#7c3aed) 100%); } .lib-char-avatar { width:96px; height:96px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:36px; font-weight:700; color:#fff; letter-spacing:0; border:4px solid rgba(255,255,255,.32); box-shadow:0 10px 22px rgba(0,0,0,.34); flex-shrink:0; } /* Once the banner itself is the portrait, the avatar circle collapses to a small "change photo" corner button instead of duplicating the same image at icon size on top of itself. */ .lib-char-card-banner.has-photo { align-items:flex-end; } .lib-char-avatar.is-photo-edit { position:absolute; top:10px; right:10px; width:30px; height:30px; font-size:15px; border-width:2px; box-shadow:0 3px 8px rgba(0,0,0,.4); background:rgba(0,0,0,.38); opacity:.75; transition:opacity .15s; } .lib-char-avatar.is-photo-edit:hover { opacity:1; } .lib-char-banner-copy { flex:1; min-width:0; min-height:0; display:flex; flex-direction:column; justify-content:center; gap:6px; color:#fff; padding:4px 34px 4px 0; overflow:hidden; } .lib-char-name { font-weight:800; font-size:20px; text-align:left; display:flex; align-items:center; gap:8px; flex-wrap:wrap; line-height:1.1; } .lib-char-tier { font-size:9px; font-weight:700; letter-spacing:.5px; text-transform:uppercase; padding:2px 6px; border-radius:10px; } .lib-char-tier.main { background: rgba(245,158,11,.2); color: #d97706; } .lib-char-tier.support { background: rgba(99,102,241,.15); color: var(--accent); } /* Whether a character has a voice — and which one — used to only be readable as text at the very bottom of a long card, so scanning a whole grid for "who still needs a voice" meant reading every card in full. A clickable pill right at the top of the banner makes it a glance instead of a read, and doubles as the fastest way to fix it: opens the same pick/design popup used everywhere else. */ .lib-char-voice-top-row { /* left offset clears the select checkbox, which sits at the same corner (top:8px/left:8px, 20px wide) one layer above the banner. Holds the "hear a sample" play button directly in front of the voice pill instead of down at the bottom of the card, now that the pill itself lives here. */ position:absolute; top:10px; left:34px; z-index:2; display:flex; align-items:center; gap:6px; max-width:calc(100% - 44px); } .lib-char-card-banner.has-photo .lib-char-voice-top-row { max-width:calc(100% - 80px); } .lib-char-voice-top-row .lib-char-voice-play { flex-shrink:0; width:22px; height:22px; padding:0; border-radius:50%; border:0; background:var(--green,#22c55e); color:#fff; } .lib-char-voice-top-row .lib-char-voice-play:hover { background:#1ca750; } .lib-char-voice-top-row .lib-char-voice-play.playing { background:var(--accent); } .lib-char-voice-pill { display:flex; align-items:center; gap:5px; min-width:0; flex:1; height:22px; padding:0 9px; border-radius:11px; border:0; cursor:pointer; background:rgba(0,0,0,.4); color:rgba(255,255,255,.7); font-size:11px; font-weight:700; transition:background .12s, color .12s; } .lib-char-voice-pill:hover { background:rgba(0,0,0,.6); color:#fff; } .lib-char-voice-pill .mdi { flex-shrink:0; font-size:13px; } .lib-char-voice-pill-text { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } .lib-char-voice-pill.has-voice { background:var(--green,#22c55e); color:#fff; } .lib-char-voice-pill.has-voice:hover { background:#1ca750; } .lib-char-voice-pill.voice-missing { background:var(--red,#ef4444); color:#fff; } .lib-char-voice-pill.voice-missing:hover { background:#dc2626; } .lib-chars-tbl-voice-missing, .lcd-voice-missing { color:var(--red,#ef4444); font-weight:600; } .lib-chars-tbl-voice-missing .mdi, .lcd-voice-missing .mdi { font-size:13px; } .lib-char-roleline { font-size:14px; color:rgba(255,255,255,.92); font-weight:700; line-height:1.25; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; } .lib-char-subline { font-size:12.5px; color:rgba(255,255,255,.75); line-height:1.35; font-style:italic; overflow-wrap:anywhere; display:-webkit-box; -webkit-line-clamp:1; -webkit-box-orient:vertical; overflow:hidden; } .lib-char-meta-chips { display:flex; flex-wrap:wrap; gap:6px; margin-top:2px; } .lib-char-meta-chip { display:inline-flex; align-items:center; gap:5px; padding:4px 8px; border-radius:999px; background: rgba(255,255,255,.15); border:1px solid rgba(255,255,255,.18); color:#fff; font-size:11px; font-weight:700; } .lib-char-meta-chip .mdi { font-size:11px; } .lib-char-body { /* Fills whatever's left under the square banner within the card's own aspect-ratio, instead of a hardcoded height — the card's total size is now driven by aspect-ratio + width, not by summing fixed child sizes. */ flex:1; min-height:0; padding:14px 16px 16px; display:flex; flex-direction:column; gap:6px; overflow-y:auto; } .lib-char-facts { display:flex; flex-direction:column; } .lib-char-archetype { font-size:13px; color:var(--subtext); text-align:left; margin:0; font-style:italic; } .lib-char-snippet { font-size:13.5px; line-height:1.55; color:var(--subtext); flex:1; margin:0; display:-webkit-box; -webkit-line-clamp:5; -webkit-box-orient:vertical; overflow:hidden; min-height: 88px; } /* Table-view voice actions (Auswahl/Auto) — base look + per-action hover tint. */ .lib-char-pick-voice, .lib-char-auto-voice, .lib-char-online-voice, .lib-char-gen-voice { font-size:10px; padding:2px 8px; border-radius:4px; cursor:pointer; border:1px solid var(--border); background:var(--panel); color:var(--text); transition: background .12s, border-color .12s, color .12s; } .lib-char-pick-voice:hover { border-color:var(--accent); color:var(--accent); } .lib-char-auto-voice:hover { border-color: var(--green,#22c55e); color: var(--green,#22c55e); } .lib-char-online-voice:hover { border-color:#0ea5e9; color:#0ea5e9; } .lib-char-gen-voice:hover { border-color:#a855f7; color:#a855f7; } /* Quick "hear a sample line" play button — sits next to the voice ID in both the card and table views, wherever a character already has a voice assigned. Toggles to a stop icon while its own preview is playing. */ .lib-char-voice-play { flex-shrink:0; min-width:22px; height:22px; padding:0 10px; border-radius:11px; display:inline-flex; align-items:center; justify-content:center; gap:5px; border:1px solid var(--border); background:var(--panel); color:var(--accent); cursor:pointer; font-size:12px; transition:background .12s, border-color .12s, color .12s; } /* Icon-only (table view) stays a plain circle — no label text to pad for. */ .lib-chars-tbl-voice-wrap .lib-char-voice-play { padding:0; width:22px; border-radius:50%; } .lib-char-voice-play:hover { border-color:var(--accent); background:rgba(var(--accent-rgb,124,106,247),.12); } .lib-char-voice-play.loading .mdi { animation: spin .8s linear infinite; } .lib-char-voice-play.playing { border-color:var(--accent); background:var(--accent); color:#fff; } .lib-chars-tbl-voice-wrap .lib-char-voice-play { margin-left:2px; } /* Icon-only "design a new voice" action — for when the current voice just doesn't fit, next to (not replacing) Auswahl/Auto so there's an explicit one-click way to get something genuinely different. */ .lib-char-redesign-voice { flex-shrink:0; width:22px; height:22px; padding:0; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; border:1px solid var(--border); background:var(--panel); color:var(--accent); cursor:pointer; font-size:13px; transition:background .12s, border-color .12s, color .12s; } .lib-char-redesign-voice:hover:not(:disabled) { border-color:var(--accent); background:rgba(var(--accent-rgb,124,106,247),.12); } .lib-char-redesign-voice:disabled { opacity:.5; cursor:not-allowed; } /* Big hover preview of a character's profile picture — a table/card thumbnail alone (often 32-40px) gives no real sense of the actual portrait. Fixed-position, follows whichever avatar is hovered, never the click target itself (pointer-events:none) so it can't interfere with the avatar's own click-to-upload behavior. */ .lib-avatar-hover-preview { position:fixed; z-index:2000; width:512px; height:512px; border-radius:12px; overflow:hidden; pointer-events:none; box-shadow:0 12px 40px rgba(0,0,0,.35); border:1px solid var(--border); background:var(--panel); } .lib-avatar-hover-preview img { width:100%; height:100%; object-fit:cover; display:block; } /* Alignment bar */ .lib-char-align { display:flex; align-items:center; gap:5px; margin:6px 0 4px; } .lib-char-align-evil { font-size:9px; color:#555; flex-shrink:0; line-height:1; } .lib-char-align-good { font-size:9px; color:#ddd; flex-shrink:0; line-height:1; } .lib-char-align-bar { flex:1; height:7px; border-radius:4px; position:relative; cursor:default; background: linear-gradient(to right, #111 0%, #444 30%, #888 50%, #ccc 70%, #f0f0f0 100%); box-shadow: inset 0 1px 2px rgba(0,0,0,.4); } .lib-char-align-dot { position:absolute; top:50%; transform:translate(-50%,-50%); width:11px; height:11px; border-radius:50%; background: #fff; border:2px solid rgba(0,0,0,.6); box-shadow: 0 0 0 1px rgba(255,255,255,.4), 0 1px 3px rgba(0,0,0,.5); transition: left .3s ease; } .lib-char-align-arrow { font-size:13px; flex-shrink:0; line-height:1; font-style:normal; } /* Most-interacted character dots */ .lib-char-rels { display:flex; align-items:center; gap:5px; margin:5px 0 7px; flex-wrap:wrap; } .lib-char-rel-dot { width:24px; height:24px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; font-size:10px; font-weight:700; color:#fff; flex-shrink:0; cursor:default; border:1.5px solid rgba(255,255,255,.15); box-shadow: 0 1px 3px rgba(0,0,0,.3); } @media (max-width: 1100px) { .lib-chars-grid { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); } } @media (max-width: 700px) { .lib-chars-grid { grid-template-columns: 1fr; } /* A single full-width column would stretch a 5:7 card absurdly tall — drop the aspect-ratio and let content size the card again. */ .lib-char-card { aspect-ratio: auto; height: auto; } .lib-char-card-banner { aspect-ratio: auto; height: auto; min-height: 120px; } .lib-char-avatar { width:82px; height:82px; font-size:30px; } .lib-char-name { font-size:18px; } .lib-chars-tbl { min-width: 2470px; } } /* ── Character detail modal (D&D / LARP card) ───────────────────────────────── */ .lib-char-detail-ov { position:fixed; inset:0; z-index:1100; background: rgba(0,0,0,.78); backdrop-filter:blur(6px); display:flex; align-items:flex-start; justify-content:center; padding:32px 16px 48px; overflow-y:auto; } .lib-char-detail-box { background: var(--surface); border-radius:16px; width:100%; max-width:860px; box-shadow: 0 32px 80px rgba(0,0,0,.6); overflow:hidden; flex-shrink:0; } .lcd-header { display:flex; gap:20px; align-items:stretch; padding:28px 28px 24px; background: linear-gradient(135deg, var(--lc1,#4f46e5) 0%, var(--lc2,#7c3aed) 100%); color:#fff; position:relative; } /* A character with an already-saved profile picture used to show it as a small ~80px square tucked in the corner like the plain letter fallback — once there's a real photo, it deserves to fill the whole header instead of being squeezed down to icon size. The letter-only placeholder stays at its normal compact size (align-items:stretch above only affects children that don't set their own height, like this one via align-self below). */ .lcd-header .cs-avatar.cs-avatar-img { width:110px; height:auto; align-self:stretch; border-radius:8px; } .lcd-avatar { flex-shrink:0; width:90px; height:90px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:38px; font-weight:700; color:#fff; letter-spacing:0; border:3px solid rgba(255,255,255,.4); box-shadow: 0 6px 18px rgba(0,0,0,.35); overflow:hidden; cursor:pointer; } .lcd-avatar:hover::after { content:'\F04F4'; font-family:'Material Design Icons'; font-size:28px; position:absolute; inset:0; border-radius:50%; display:flex; align-items:center; justify-content:center; background:rgba(0,0,0,.45); color:#fff; } .lcd-avatar { position:relative; } .lcd-avatar img { width:100%; height:100%; object-fit:cover; } .lcd-avatar-wrap { position: relative; flex-shrink: 0; } .lcd-avatar-actions { position: absolute; left: 50%; bottom: -8px; transform: translateX(-50%); z-index: 2; display: flex; gap: 4px; } .lcd-avatar-actions button { width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: var(--accent); color: #fff; border: 2px solid var(--surface); cursor: pointer; font-size: 12px; box-shadow: 0 2px 6px rgba(0,0,0,.25); } .lcd-avatar-actions button:hover { filter: brightness(1.1); } .lcd-avatar-actions button:disabled { opacity: .6; cursor: default; } .lcd-avatar-online-btn { background: var(--teal, #0F766E) !important; } .lcd-avatar-upload-btn { background: var(--mauve, #7C3AED) !important; } .lcd-header-body { flex:1; min-width:0; } .lcd-form-switcher { display:inline-flex; align-items:center; gap:8px; margin-top:10px; font-size:12px; font-weight:600; color:rgba(255,255,255,.9); } .lcd-form-switcher select { max-width:190px; padding:5px 8px; border:1px solid rgba(255,255,255,.35); border-radius:6px; background:rgba(0,0,0,.22); color:#fff; } .lcd-form-switcher option { background:var(--surface,#20202a); color:var(--text,#fff); } .lcd-name { font-size:26px; font-weight:700; line-height:1.15; margin-bottom:5px; } .lcd-aliases { font-size:14px; opacity:.72; margin-bottom:7px; font-style:italic; } .lcd-archetype { font-size:17px; opacity:.88; margin-bottom:12px; font-weight:500; } .lcd-tier-gender { display:flex; align-items:center; gap:10px; flex-wrap:wrap; } .lcd-tier { font-size:12px; font-weight:700; letter-spacing:.6px; text-transform:uppercase; padding:4px 12px; border-radius:14px; background:rgba(255,255,255,.22); } .lcd-linecount { display:inline-flex; align-items:center; justify-content:center; font-size:12px; font-weight:700; letter-spacing:.02em; padding:4px 10px; border-radius:14px; border:1px solid rgba(255,255,255,.28); background:rgba(255,255,255,.14); color:#fff; } .lcd-gender { font-size:14px; opacity:.82; } /* The auto-generated concept-art sheet — a full-width banner just below the header (its own .lcd-section-full row), sized to actually be readable instead of squeezed to a 110px header thumbnail (confirmed live: too small to make out any detail in a multi-pose design sheet). */ .lcd-conceptart-headrow { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; } .lcd-conceptart-img { width: 100%; max-height: 70vh; border-radius: 10px; overflow: hidden; cursor: pointer; border: 1px solid var(--border); display: flex; justify-content: center; background: color-mix(in srgb, var(--panel) 60%, transparent); } .lcd-conceptart-img img { max-width: 100%; max-height: 70vh; object-fit: contain; display: block; } .lcd-conceptart-empty { width: 100%; min-height: 160px; border-radius: 10px; border: 2px dashed var(--border); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; font-size: 13px; text-align: center; color: var(--subtext); padding: 20px; } .lcd-conceptart-empty .mdi { font-size: 30px; } .lcd-conceptart-empty.is-noprompt { opacity: .6; } .lcd-conceptart-empty { cursor:pointer; } .lcd-conceptart-empty:hover { border-color:var(--accent); } .conceptart-lightbox-box { width: min(980px, 96vw); max-height: 94vh; overflow-y: auto; } .conceptart-lightbox-img { width: 100%; height: auto; border-radius: 10px; display: block; } .conceptart-editor-preview { display:flex; justify-content:center; min-height:120px; margin-top:18px; } .conceptart-editor-preview .conceptart-lightbox-img { max-width:min(900px, 90vw); max-height:62vh; object-fit:contain; width:auto; } .conceptart-editor-empty { width:min(700px, 88vw); min-height:240px; border:2px dashed var(--border); border-radius:10px; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:8px; color:var(--subtext); } .conceptart-editor-empty .mdi { font-size:38px; } .conceptart-quick-actions { display:flex; justify-content:center; flex-wrap:wrap; gap:10px; margin:18px 0; } .conceptart-editor-tools { display:flex; flex-direction:column; gap:9px; padding:0 10px; } .conceptart-editor-tools textarea { width:100%; resize:vertical; padding:10px; font:inherit; min-height:120px; } .lcd-header-btns { display:flex; gap:8px; flex-shrink:0; align-self:flex-start; } .lcd-header-btns button { background:rgba(255,255,255,.18); border:1px solid rgba(255,255,255,.32); color:#fff; border-radius:8px; padding:7px 16px; font-size:14px; cursor:pointer; transition:background .12s; display:flex; align-items:center; gap:6px; } .lcd-header-btns button:hover { background:rgba(255,255,255,.32); } /* Body with alternating section backgrounds */ .lcd-body { padding:0; } .lcd-align-section { padding:20px 28px 18px; border-bottom:1px solid var(--border); background: var(--panel); } .lcd-align-bar-wrap { display:flex; align-items:center; gap:12px; margin:10px 0 6px; } .lcd-align-label { font-size:14px; color:var(--subtext); flex-shrink:0; font-weight:600; } .lcd-align-bar { flex:1; height:12px; border-radius:6px; position:relative; background: linear-gradient(to right, #1a1a1a 0%, #555 30%, #888 50%, #bbb 70%, #efefef 100%); box-shadow: inset 0 2px 4px rgba(0,0,0,.5); } .lcd-align-dot { position:absolute; top:50%; transform:translate(-50%,-50%); width:20px; height:20px; border-radius:50%; background:#fff; border:3px solid rgba(0,0,0,.5); box-shadow: 0 0 0 2px rgba(255,255,255,.6), 0 3px 8px rgba(0,0,0,.55); } .lcd-align-arc { font-size:15px; font-weight:500; margin-top:7px; } .lcd-arc-note { font-size:14px; color:var(--subtext); font-style:italic; margin-top:5px; line-height:1.6; } /* Two-column section grid */ .lcd-sections { display:grid; grid-template-columns:1fr 1fr; } @media (max-width:620px) { .lcd-sections { grid-template-columns:1fr; } } .lcd-section { padding:22px 28px; border-bottom:1px solid var(--border); background: var(--surface); } .lcd-section:nth-child(odd) { border-right:1px solid var(--border); } /* Alternating panel rows */ .lcd-section:nth-child(4n+1), .lcd-section:nth-child(4n+2) { background: var(--panel); } .lcd-section-full { padding:22px 28px; border-bottom:1px solid var(--border); background: var(--surface); } .lcd-section-full:nth-child(even) { background: var(--panel); } .lcd-empty-note { display: flex; align-items: center; gap: 10px; color: var(--subtext); font-size: 14px; font-style: italic; } .lcd-empty-note .mdi { font-size: 20px; opacity: .7; } .lcd-section-label { font-size:11px; font-weight:700; letter-spacing:.9px; text-transform:uppercase; color:var(--accent); margin-bottom:14px; display:flex; align-items:center; gap:7px; } /* Generation Prompts section: four fold-out "ready to copy" boxes */ .lcd-prompts-section .lcd-section-label { justify-content: space-between; } .lcd-prompts-section .lcd-gen-prompts { margin-left: auto; text-transform: none; letter-spacing: 0; } .lcd-prompt-box { border: 1px solid var(--border); border-radius: 8px; background: var(--surface); margin-bottom: 8px; overflow: hidden; } .lcd-prompt-box summary { cursor: pointer; padding: 9px 12px; font-size: 12.5px; font-weight: 700; color: var(--text); list-style: none; display: flex; align-items: center; gap: 6px; user-select: none; } .lcd-prompt-box summary::before { content: '›'; font-size: 15px; color: var(--subtext); transition: transform .15s; } .lcd-prompt-box[open] summary::before { transform: rotate(90deg); } .lcd-prompt-box summary:hover { background: var(--panel); } .lcd-prompt-hint { margin-left: auto; font-size: 11px; font-weight: 400; color: var(--subtext); } .lcd-prompt-empty { font-weight: 400; font-size: 11px; color: var(--subtext); font-style: italic; } .lcd-prompt-body { padding: 0 12px 10px; display: flex; flex-direction: column; gap: 8px; } .cs-concept-art-preview { border-radius: 8px; overflow: hidden; border: 1px solid var(--border); } .cs-concept-art-preview img { display: block; width: 100%; height: auto; } .lcd-prompt-text { white-space: pre-wrap; font-size: 12.5px; line-height: 1.55; color: var(--text); background: var(--panel); border: 1px solid var(--border); border-radius: 6px; padding: 8px 10px; min-height: 40px; outline: none; } .lcd-prompt-text:focus { border-color: var(--accent); } .lcd-prompt-actions { display: flex; gap: 6px; justify-content: flex-end; } .lcd-prompt-actions .lcd-save-prompt { margin-right: auto; } .lcd-generated-voice-file .lcd-regenerate-voice { margin-left: auto; flex: 0 0 auto; } .lcd-voice-prompt-box { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); box-shadow: 0 2px 8px rgba(37,99,235,.08); } .lcd-voice-prompt-box .lcd-prompt-actions { padding-top: 2px; } .lcd-voice-actions { display: flex; justify-content: flex-end; margin: -1px 0 10px; } @media (max-width: 560px) { .lcd-prompt-hint { display: none; } .lcd-generated-voice-file { flex-wrap: wrap; } .lcd-generated-voice-file .lcd-regenerate-voice { margin-left: auto; } .lcd-voice-actions { justify-content: stretch; } .lcd-voice-actions button { width: 100%; } } .lcd-generated-voice-file { display: flex; align-items: center; gap: 8px; margin: -2px 0 8px; padding: 7px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel); color: var(--subtext); font-size: 11.5px; } .lcd-generated-voice-file-label { min-width: 0; overflow-wrap: anywhere; } .lcd-generated-voice-file-label strong { color: var(--text); font-weight: 600; } .lcd-voice-replace-confirm { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: -2px 0 10px; padding: 10px 12px; border: 1px solid #f59e0b; border-radius: 8px; background: color-mix(in srgb, #f59e0b 10%, var(--surface)); color: var(--text); font-size: 12px; line-height: 1.4; } .lcd-voice-replace-confirm[hidden] { display: none; } .lcd-voice-replace-confirm > span:first-child { display: flex; align-items: flex-start; gap: 7px; } .lcd-voice-replace-confirm .mdi-alert-outline { color: #d97706; font-size: 17px; } .lcd-voice-replace-actions { display: flex; gap: 7px; flex: 0 0 auto; } @media (max-width: 640px) { .lcd-voice-replace-confirm { align-items: stretch; flex-direction: column; } .lcd-voice-replace-actions { justify-content: flex-end; } } .lcd-generated-voice-play { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; width: 28px; height: 28px; padding: 0; border: 1px solid var(--accent); border-radius: 50%; background: var(--accent); color: #fff; cursor: pointer; } .lcd-generated-voice-play:hover { filter: brightness(.92); } .lcd-generated-voice-play.playing { background: #1d4ed8; } .lcd-generated-voice-play.loading { opacity: .65; cursor: wait; } .lcd-field { margin-bottom:16px; } .lcd-field:last-child { margin-bottom:0; } /* Compact list row — label left, value right, thin separator — used inside the grouped 2-column sections for a scannable list feel instead of the more spacious stacked .lcd-field blocks. */ .lcd-row { display: grid; grid-template-columns: minmax(88px, auto) 1fr; gap: 10px; padding: 7px 0; border-bottom: 1px solid color-mix(in srgb, var(--border) 55%, transparent); font-size: 13.5px; } .lcd-row:last-child { border-bottom: none; } .lcd-row.lcd-row-full { grid-template-columns: 1fr; } .lcd-row dt { font-weight: 800; color: var(--subtext); text-transform: uppercase; font-size: 10px; letter-spacing: .05em; line-height: 1.4; } .lcd-row dd { margin: 0; color: var(--text); line-height: 1.5; } .lcd-row dd .cs-source-mark { margin-left: 6px; vertical-align: middle; } /* Character sheets are read as a profile, not a compact comparison table: each field has a bold heading followed by normal prose in one column. Scope this to the sheet so the denser casting/library editor remains intact. */ .cs-profile-layout .lcd-sections { grid-template-columns: 1fr; } .cs-profile-layout .lcd-section:nth-child(odd) { border-right: none; } .cs-profile-layout .lcd-section:nth-child(odd) { background: var(--surface); } .cs-profile-layout .lcd-section:nth-child(even) { background: var(--panel); } .cs-profile-layout .lcd-row { display: block; padding: 10px 0; } .cs-profile-layout .lcd-row dt { color: var(--text); font-size: 12px; font-weight: 800; letter-spacing: .02em; text-transform: none; } .cs-profile-layout .lcd-row dd { margin-top: 4px; font-size: 14px; line-height: 1.55; } .lcd-field-label { font-size:11px; font-weight:700; color:var(--subtext); text-transform:uppercase; letter-spacing:.5px; margin-bottom:5px; } .lcd-field-value { font-size:15px; line-height:1.65; color:var(--text); white-space:pre-wrap; } .lcd-rels-dots { display:flex; flex-wrap:wrap; gap:8px; margin-top:10px; } .lcd-rel-dot { width:36px; height:36px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; font-size:14px; font-weight:700; color:#fff; border:2px solid rgba(255,255,255,.18); box-shadow:0 3px 7px rgba(0,0,0,.35); cursor:default; } .lcd-voice-section { padding:22px 28px 28px; background:var(--panel); } .lcd-voice-row { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-top:10px; } .lcd-voice-label { font-size:15px; flex:1; min-width:140px; } .lcd-voice-row button { font-size:13px; padding:6px 16px; border-radius:7px; cursor:pointer; border:1px solid var(--border); background:var(--surface); color:var(--text); transition:background .12s, border-color .12s, color .12s; } .lcd-pick-voice:hover { border-color:var(--accent); color:var(--accent); } .lcd-auto-voice:hover { border-color:var(--green,#22c55e); color:var(--green,#22c55e); } .lcd-online-voice:hover { border-color:#0ea5e9; color:#0ea5e9; } .lcd-gen-voice:hover { border-color:#a855f7; color:#a855f7; } .lcd-analysis { background:var(--panel); border-top:1px solid var(--border); padding:20px 28px; font-size:14px; line-height:1.7; color:var(--subtext); white-space:pre-wrap; } /* Voice section at top of modal body */ .lcd-voice-top { padding:20px 28px 18px; border-bottom:1px solid var(--border); background:var(--surface); display:flex; align-items:center; gap:12px; flex-wrap:wrap; } .lcd-voice-top .lcd-section-label { margin-bottom:0; flex-shrink:0; } .lcd-voice-top .lcd-voice-label { font-size:14px; flex:0 1 auto; min-width:0; } .lcd-voice-top button { font-size:13px; padding:6px 16px; border-radius:7px; cursor:pointer; border:1px solid var(--border); background:var(--panel); color:var(--text); transition:background .12s, border-color .12s, color .12s; } .lcd-voice-top .lcd-pick-voice:hover { border-color:var(--accent); color:var(--accent); } .lcd-voice-top .lcd-auto-voice:hover { border-color:var(--green,#22c55e); color:var(--green,#22c55e); } .lcd-voice-top .lcd-online-voice:hover { border-color:#0ea5e9; color:#0ea5e9; } .lcd-voice-top .lcd-gen-voice:hover { border-color:#a855f7; color:#a855f7; } .lcd-voice-top .lcd-preview-voice { display:inline-flex; align-items:center; justify-content:center; width:30px; height:30px; padding:0; border-radius:50%; } .lcd-voice-top .lcd-preview-voice { background:#2563eb; border-color:#2563eb; color:#fff; } .lcd-voice-top .lcd-preview-voice:hover { background:#1d4ed8; border-color:#1d4ed8; color:#fff; } .lcd-voice-top .lcd-preview-voice.playing { background:#1d4ed8; border-color:#1d4ed8; color:#fff; } .lcd-voice-top .lcd-preview-voice.loading { opacity:.65; cursor:wait; } .lcd-voice-top .lcd-design-voice-icon { display:inline-flex; align-items:center; justify-content:center; width:30px; height:30px; padding:0; border-radius:50%; color:#7c3aed; } .lcd-voice-top .lcd-design-voice-icon:hover { background:#f3e8ff; border-color:#a855f7; color:#7c3aed; } /* Sources section */ .lcd-sources { padding:20px 28px; background:var(--panel); border-top:1px solid var(--border); } .lcd-source-list { display:flex; flex-direction:column; gap:8px; margin-top:10px; } .lcd-source-item { background:var(--surface); border:1px solid var(--border); border-radius:8px; padding:10px 14px; } .lcd-source-page { font-size:11px; font-weight:700; color:var(--accent); margin-bottom:4px; text-transform:uppercase; letter-spacing:.4px; } .lcd-source-quote { font-size:13px; line-height:1.55; color:var(--text); font-style:italic; } .lcd-source-hint { font-size:11px; color:var(--subtext); margin-top:3px; } .lcd-sources-hint { font-size:10px; opacity:.45; font-weight:400; letter-spacing:0; } .lcd-source-clickable { cursor:pointer; transition:background .12s, border-color .12s; } .lcd-source-clickable:hover { background:rgba(var(--accent-rgb,124,106,247),.12); border-color:var(--accent); } /* ── Character detail page (full-page layout, no modal) ─────────────────── */ /* Fixed height so each panel scrolls independently. The outer content wrapper already has overflow-y:auto (line ~284), so sticky doesn't work inside it. Instead we give .lib-char-page a real viewport height and let each column overflow internally. */ .lib-char-page { display:flex; flex-direction:row; height:calc(100dvh - 148px); /* viewport minus app header + section tabs */ min-height:400px; overflow:hidden; } .lib-cpg-main { flex:1; overflow-y:auto; min-width:0; padding-bottom:60px; } .lib-cpg-back { display:inline-flex; align-items:center; gap:6px; background:none; border:none; cursor:pointer; color:var(--accent); font-size:13px; padding:14px 20px 8px; opacity:.8; } .lib-cpg-back:hover { opacity:1; } /* Remove the outer box from the modal — header fills edge-to-edge */ .lib-char-page .lcd-header { border-radius:0; } .lib-char-page .lcd-body { padding:0; } /* Inline-editable fields inside the detail page */ .lcd-field-editable { outline:none; border-radius:4px; cursor:text; padding:2px 5px; margin:-2px -5px; transition:background .15s, box-shadow .15s; min-height:1.2em; } .lcd-field-editable:hover { background:rgba(255,255,255,.05); } .lcd-field-editable:focus { background:rgba(255,255,255,.09); box-shadow:inset 0 -2px 0 var(--accent); } /* Same treatment for the live casting card's own pencil-toggle editing — only actionable once the card is in edit mode (contenteditable="true"). */ .cs-field-editable[contenteditable="true"] { outline:none; border-radius:4px; cursor:text; padding:2px 5px; margin:-2px -5px; display:inline-block; transition:background .15s, box-shadow .15s; } .cs-field-editable[contenteditable="true"]:hover { background:rgba(var(--accent-rgb,124,106,247),.08); } .cs-field-editable[contenteditable="true"]:focus { background:rgba(var(--accent-rgb,124,106,247),.14); box-shadow:inset 0 -2px 0 var(--accent); } /* Header name/aliases/archetype editable */ .lcd-header [contenteditable] { outline:none; cursor:text; } .lcd-header [contenteditable]:hover { text-decoration:underline; text-underline-offset:3px; text-decoration-style:dotted; } .lcd-header [contenteditable]:focus { text-decoration:none; background:rgba(0,0,0,.2); border-radius:4px; padding:0 4px; } /* Alignment slider */ .lcd-align-slider { flex:1; height:6px; accent-color:var(--accent,#7c6af7); cursor:pointer; } .lcd-align-slider-val { font-size:13px; font-weight:600; opacity:.7; flex-shrink:0; min-width:48px; text-align:right; } /* Right sidebar — independent scroll, no sticky needed (fixed-height parent) */ .lib-cpg-sidebar { width:240px; flex-shrink:0; border-left:3px solid var(--border); box-shadow:-4px 0 12px rgba(0,0,0,.18); overflow-y:auto; background:var(--panel); } .lib-cpg-sidebar-title { padding:12px 14px 8px; font-size:10px; font-weight:700; letter-spacing:.07em; text-transform:uppercase; opacity:.45; border-bottom:1px solid var(--border); } .lib-cpg-sidebar-tools { display:flex; gap:5px; padding:8px; border-bottom:1px solid var(--border); } .lib-cpg-sidebar-search, .lib-cpg-sidebar-sort { min-width:0; padding:5px 6px; border:1px solid var(--border); border-radius:5px; background:var(--surface); color:var(--text); font:inherit; font-size:11px; outline:none; } .lib-cpg-sidebar-search { flex:1; } .lib-cpg-sidebar-sort { width:76px; cursor:pointer; } .lib-cpg-sidebar-search:focus, .lib-cpg-sidebar-sort:focus { border-color:var(--accent); } .lib-cpg-sidebar-item { display:flex; align-items:center; gap:9px; padding:8px 12px; cursor:pointer; transition:background .12s; border-bottom:1px solid rgba(255,255,255,.03); } .lib-cpg-sidebar-item:hover, .lib-cpg-sidebar-item.is-active { background:var(--surface); } .lib-cpg-sidebar-item.is-active { border-left:2px solid var(--accent); padding-left:10px; } .lib-cpg-sidebar-dot { width:26px; height:26px; border-radius:50%; flex-shrink:0; display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:700; color:#fff; } .lib-cpg-sidebar-name { flex:1; font-size:12px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } .lib-cpg-sidebar-count { font-size:11px; opacity:.4; flex-shrink:0; } /* Voice picker popup */ .lib-voice-picker-popup { /* position/left/top/bottom/width set inline in JS (fixed, anchored via getBoundingClientRect) — see _openVoicePicker */ z-index:500; background: var(--surface); border:1px solid var(--border); border-radius:8px; box-shadow: 0 8px 24px rgba(0,0,0,.25); overflow:hidden; max-height:280px; display:flex; flex-direction:column; } .lib-vp-search { padding:8px; border-bottom:1px solid var(--border); } .lib-vp-quick { padding:0 8px 8px; border-bottom:1px solid var(--border); } .lib-vp-design-btn { width:100%; display:flex; align-items:center; justify-content:center; gap:6px; padding:7px; border-radius:6px; border:1px dashed var(--accent); background:none; color:var(--accent); font-size:12px; font-weight:600; cursor:pointer; transition:background .12s; } .lib-vp-design-btn:hover { background:rgba(var(--accent-rgb,124,106,247),.1); } .lib-vp-input { width:100%; box-sizing:border-box; background:var(--panel); border:1px solid var(--border); border-radius:6px; padding:5px 9px; font-size:12px; color:var(--text); outline:none; } .lib-vp-input:focus { border-color:var(--accent); } .lib-vp-list { overflow-y:auto; flex:1; } .lib-vp-item { padding:7px 12px; font-size:12px; cursor:pointer; transition:background .1s; } .lib-vp-item:hover { background:var(--panel); } .lib-vp-item.selected { color:var(--accent); font-weight:600; } /* Character production tags */ .cl-card-tags { display:flex; flex-wrap:wrap; gap:4px; margin-top:8px; } .cl-tag-chip { display:inline-flex; align-items:center; gap:3px; padding:2px 8px; border-radius:10px; background:var(--chip-bg, rgba(127,127,127,.16)); color:var(--subtext); font-size:11px; font-weight:500; } .cl-tag-chip .mdi { font-size:12px; opacity:.7; } .lib-char-tags { display:flex; flex-wrap:wrap; gap:4px; margin:4px 0; } /* Page card head */ .s-page-head { border-bottom:1px solid var(--border); padding-bottom:14px; } .s-page-head h3 { font-size:16px; font-weight:700; margin:0 0 4px; color:var(--text); } .s-page-head p { font-size:13px; color:var(--subtext); margin:0; line-height:1.5; } .s-page-head-row { display:flex; justify-content:space-between; align-items:flex-start; gap:16px; } .s-stack-badge { display:inline-flex; align-items:center; gap:5px; border:1px solid color-mix(in srgb,var(--green) 40%,transparent); background:rgba(22,163,74,.07); color:#166534; border-radius:999px; padding:5px 11px; font-size:12px; font-weight:600; white-space:nowrap; flex-shrink:0; } /* Groups */ .s-group { display:flex; flex-direction:column; gap:14px; } .s-group + .s-group { border-top:1px solid var(--border); padding-top:18px; } .s-group-head { display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; } .s-group-head strong { font-size:14px; font-weight:600; color:var(--text); } .s-group-head span { font-size:12px; color:var(--subtext); } /* Field grids */ .settings-grid { display:grid; grid-template-columns:repeat(auto-fit, minmax(280px,1fr)); gap:16px; } .settings-grid.three { grid-template-columns:repeat(auto-fit, minmax(240px,1fr)); } .settings-grid.compact { grid-template-columns:repeat(2, minmax(220px,1fr)); gap:14px; } .settings-behavior-grid { grid-template-columns:minmax(220px,.6fr) minmax(220px,.6fr); gap:16px; align-items:start; } .settings-param-grid textarea { min-height:58px; } /* Fields */ .s-field { display:flex; flex-direction:column; gap:6px; } .s-field label { font-size:13px; color:var(--text); font-weight:500; } .s-label-note { font-weight:400; color:var(--subtext); } .s-field input, .s-field select, .s-field textarea { background:var(--panel); border:1px solid var(--border); color:var(--text); border-radius:var(--radius); padding:9px 12px; font-size:14px; transition:border-color .15s,box-shadow .15s; width:100%; box-sizing:border-box; } .s-field input:focus, .s-field select:focus, .s-field textarea:focus { border-color:var(--accent); outline:none; box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 12%,transparent); } .s-field textarea { min-height:76px; resize:vertical; font-family:monospace; } .s-hint { font-size:12px; color:var(--subtext); line-height:1.5; } .s-key-row { display:flex; gap:6px; } .s-key-row input { flex:1; } .s-eye-btn { background:var(--panel); border:1px solid var(--border); color:var(--subtext); border-radius:var(--radius); padding:0 13px; font-size:15px; cursor:pointer; flex-shrink:0; transition:color .15s; } .s-eye-btn:hover { color:var(--text); } .settings-guide { display: grid; grid-template-columns: 34px minmax(0,1fr); gap: 12px; align-items: start; margin: 14px 0 4px; padding: 12px 14px; border: 1px solid color-mix(in srgb, var(--accent) 24%, var(--border)); border-radius: 8px; background: color-mix(in srgb, var(--accent) 6%, var(--surface)); } .settings-guide-icon { width: 34px; height: 34px; border-radius: 8px; display: inline-flex; align-items: center; justify-content: center; color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); font-size: 18px; } .settings-guide-body { min-width: 0; } .settings-guide-body strong { display: block; font-size: 13px; color: var(--text); margin-bottom: 3px; } .settings-guide-body p { margin: 0; color: var(--subtext); font-size: 12px; line-height: 1.5; } .settings-guide-body ul { margin: 7px 0 0; padding-left: 18px; color: var(--subtext); font-size: 12px; line-height: 1.55; } .s-field label { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; } .s-help-tip { position: relative; display: inline-flex; align-items: center; } .s-help-tip button { width: 18px; height: 18px; border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border)); border-radius: 50%; background: color-mix(in srgb, var(--accent) 7%, var(--panel)); color: var(--accent); display: inline-flex; align-items: center; justify-content: center; padding: 0; cursor: help; font-size: 12px; line-height: 1; } .s-help-tip button:hover, .s-help-tip button:focus-visible { background: var(--accent); color: #fff; } .s-tooltip { position: absolute; left: 50%; bottom: calc(100% + 8px); transform: translateX(-50%) translateY(3px); width: min(300px, calc(100vw - 40px)); padding: 9px 10px; border-radius: 7px; background: var(--text); color: var(--bg); font-size: 12px; line-height: 1.45; font-weight: 500; box-shadow: var(--shadow); opacity: 0; pointer-events: none; z-index: 250; transition: opacity .14s ease, transform .14s ease; } .s-tooltip::after { content: ''; position: absolute; left: 50%; top: 100%; transform: translateX(-50%); border: 6px solid transparent; border-top-color: var(--text); } .s-help-tip:hover .s-tooltip, .s-help-tip:focus-within .s-tooltip { opacity: 1; transform: translateX(-50%) translateY(0); } .s-field-tip { display: block; border-left: 3px solid color-mix(in srgb, var(--accent) 32%, var(--border)); padding: 6px 9px; border-radius: 0 6px 6px 0; background: color-mix(in srgb, var(--accent) 4%, var(--panel)); color: var(--subtext); font-size: 12px; line-height: 1.45; } .s-field:focus-within .s-field-tip { color: var(--text); background: color-mix(in srgb, var(--accent) 7%, var(--panel)); border-left-color: var(--accent); } .s-page-actions { position: sticky; bottom: 0; background: color-mix(in srgb, var(--surface) 94%, transparent); backdrop-filter: blur(8px); margin: 0 -2px -2px; padding: 14px 2px 2px; z-index: 5; } @media (max-width: 700px) { .settings-guide { grid-template-columns: 1fr; } .settings-guide-icon { display: none; } .s-tooltip { display: none; } } /* STT layout */ .stt-settings-grid { grid-template-columns:minmax(320px,.65fr) minmax(240px,1fr); } .settings-mini-actions { margin-top:4px; gap:8px; flex-wrap:wrap; } .settings-mini-actions button { min-height:32px; padding:6px 11px; font-size:12px; } /* Captures toggle grid */ .s-toggle-grid { display:grid; grid-template-columns:repeat(auto-fit, minmax(240px,1fr)); gap:10px; } .s-toggle-row { display:flex; align-items:flex-start; gap:12px; cursor:pointer; padding:10px 12px; border:1px solid var(--border); border-radius:var(--radius); background:var(--panel); transition:border-color .15s; } .s-toggle-row:hover { border-color:var(--accent); } .s-toggle-row input[type="checkbox"] { margin-top:2px; flex-shrink:0; accent-color:var(--accent); width:16px; height:16px; cursor:pointer; } .s-toggle-title { display:block; font-size:13px; font-weight:600; color:var(--text); margin-bottom:2px; } .s-toggle-row .s-hint { display:block; margin:0; } /* Page save/reload actions */ .s-page-actions { border-top:1px solid var(--border); padding-top:14px; display:flex; gap:10px; } .s-page-actions button { min-width:120px; } /* Log viewer */ .s-log-toolbar { display:flex; align-items:center; gap:8px; flex-wrap:wrap; } .s-log-live-label { display:inline-flex; align-items:center; gap:6px; font-size:13px; color:var(--subtext); cursor:pointer; padding:0 4px; } .s-log-live-label input { accent-color:var(--accent); cursor:pointer; } .s-log-count { font-size:12px; color:var(--subtext); margin-left:auto; } .s-log-filters { display:flex; gap:6px; flex-wrap:wrap; } .s-log-filter { border:1px solid var(--border); background:transparent; color:var(--subtext); border-radius:999px; padding:3px 10px; font-size:12px; cursor:pointer; transition:all .15s; } .s-log-filter:hover { border-color:var(--accent); color:var(--accent); } .s-log-filter.is-active { background:var(--accent); border-color:var(--accent); color:#fff; } .s-log-viewer { background:var(--panel); border:1px solid var(--border); border-radius:var(--radius); padding:12px 14px; font-family:monospace; font-size:12px; line-height:1.6; max-height:75vh; overflow-y:auto; display:flex; flex-direction:column; gap:1px; } .s-log-empty { color:var(--subtext); font-family:inherit; text-align:center; padding:24px 0; } .s-log-row { display:grid; grid-template-columns:max-content max-content 1fr; gap:12px; align-items:baseline; padding:2px 0; border-bottom:1px solid color-mix(in srgb,var(--border) 50%,transparent); } .s-log-row:last-child { border-bottom:none; } .s-log-ts { color:var(--subtext); font-size:11px; white-space:nowrap; } .s-log-level { font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.04em; } .s-log-level.ERROR, .s-log-level.CRITICAL { color:var(--red); } .s-log-level.WARNING { color:#f59e0b; } .s-log-level.INFO { color:var(--accent); } .s-log-level.DEBUG { color:var(--subtext); } .s-log-msg { color:var(--text); word-break:break-all; } /* About */ .s-about-block { display:flex; flex-direction:column; gap:14px; max-width:640px; } .s-about-name { font-size:20px; font-weight:700; color:var(--text); display:flex; align-items:center; gap:8px; } .s-about-name .mdi { font-size:22px; color:var(--accent); } .s-about-desc { font-size:14px; color:var(--subtext); line-height:1.65; margin:0; } .s-about-stack { display:flex; flex-wrap:wrap; gap:8px; } .s-about-chip { display:inline-flex; align-items:center; gap:5px; background:var(--panel); border:1px solid var(--border); border-radius:var(--radius); padding:4px 10px; font-size:12px; color:var(--subtext); } .s-about-backends { display:flex; flex-wrap:wrap; gap:8px; margin-top:4px; } .s-about-backend { display:inline-flex; align-items:center; gap:6px; border:1px solid var(--border); border-radius:var(--radius); padding:5px 10px; font-size:12px; color:var(--subtext); } .s-about-backend.online { border-color:color-mix(in srgb,var(--green) 50%,transparent); color:var(--green); } .s-about-backend.offline { opacity:.55; } .s-about-backend .mdi { font-size:11px; } /* Responsive */ @media (max-width:1100px) { .settings-grid.compact { grid-template-columns:1fr; } .settings-behavior-grid { grid-template-columns:1fr; } } @media (max-width:900px) { .stt-settings-grid { grid-template-columns:1fr; } } /* Accessibility helpers */ .sr-only { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; } .skip-link { position: fixed; left: 12px; top: 12px; z-index: 6000; transform: translateY(-160%); background: var(--accent); color: #fff; padding: 9px 13px; border-radius: 7px; font-weight: 800; box-shadow: var(--shadow); } .skip-link:focus { transform: translateY(0); outline: 3px solid #fff; outline-offset: 2px; } button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible, .nav-item:focus-visible, .nav-tree-item:focus-visible { outline: 3px solid color-mix(in srgb, var(--accent) 78%, white); outline-offset: 2px; } /* ── Toast ──────────────────────────────────────────────────────────────── */ #toast { position: fixed; top: 50%; left: 50%; background: var(--surface); border: 2px solid var(--accent); border-left-width: 8px; color: var(--text); padding: 16px 20px; border-radius: 8px; font-size: 15px; font-weight: 700; opacity: 0; transform: translate(-50%, calc(-50% + 12px)) scale(.98); transition: opacity .18s, transform .18s; pointer-events: none; z-index: 5000; max-width: min(520px, calc(100vw - 40px)); min-width: min(360px, calc(100vw - 40px)); box-shadow: 0 20px 70px rgba(0,0,0,.35); text-align: center; } #toast.show { opacity: 1; transform: translate(-50%, -50%) scale(1); } #toast.success { border-color: var(--green); color: var(--green); } #toast.error { border-color: var(--red); color: var(--red); } #toast.info { border-color: var(--accent); color: var(--accent); } /* ── Status bar ─────────────────────────────────────────────────────────── */ #status-bar { background: var(--surface); border-top: 1px solid var(--border); padding: 4px 18px; font-size: 12px; color: var(--subtext); min-height: 24px; flex-shrink: 0; display: flex; align-items: center; gap: 16px; min-width: 0; } #status-message { flex: 0 1 auto; min-width: 120px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } #status-engines { margin-left: auto; min-width: 0; display: flex; align-items: center; justify-content: flex-end; gap: 16px; overflow: hidden; } .status-engine { min-width: 0; display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; color: var(--subtext); background: none; border: none; padding: 2px 4px; margin: 0; border-radius: 4px; font: inherit; cursor: pointer; } .status-engine:hover { background: var(--panel); color: var(--text); } .status-engine-caret { font-size: 13px; opacity: .55; } .status-dot { width: 11px; height: 11px; border-radius: 50%; flex: 0 0 auto; background: var(--red); box-shadow: 0 0 0 2px color-mix(in srgb, var(--red) 14%, transparent); } .status-engine.ok .status-dot { background: var(--green); box-shadow: 0 0 0 2px color-mix(in srgb, var(--green) 16%, transparent); } .status-engine-label { font-weight: 800; color: var(--text); } .status-engine-value { min-width: 0; overflow: hidden; text-overflow: ellipsis; max-width: min(34vw, 420px); } .status-engine.bad .status-engine-value { color: var(--red); } /* Fly-up menu opened by clicking a footer engine chip — quick-switch the active LLM model / STT backend / TTS backend without opening Settings. */ .status-flyup { position: fixed; z-index: 2000; min-width: 200px; max-width: 320px; max-height: 320px; display: flex; flex-direction: column; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: 0 -8px 24px rgba(0,0,0,.18); padding: 4px; } .status-flyup-search { display: flex; align-items: center; gap: 6px; padding: 4px 8px 6px; border-bottom: 1px solid var(--border); margin-bottom: 4px; flex-shrink: 0; } .status-flyup-search .mdi { font-size: 14px; color: var(--subtext); flex-shrink: 0; } .status-flyup-search input { flex: 1; min-width: 0; border: none; background: none; outline: none; font-size: 12.5px; color: var(--text); font-family: inherit; } .status-flyup-list { overflow-y: auto; } .status-flyup-item { display: flex; align-items: center; gap: 6px; width: 100%; text-align: left; padding: 7px 10px; border: none; background: none; border-radius: 6px; font-size: 12.5px; color: var(--text); cursor: pointer; } .status-flyup-item:hover { background: var(--panel); } .status-flyup-item.is-active { color: var(--accent); font-weight: 700; } .status-flyup-item .mdi { font-size: 14px; flex-shrink: 0; } .status-flyup-empty { padding: 10px 12px; font-size: 12px; color: var(--subtext); font-style: italic; } @media (max-width: 820px) { #status-bar { align-items: flex-start; flex-direction: column; gap: 4px; } #status-engines { margin-left: 0; width: 100%; justify-content: flex-start; flex-wrap: wrap; gap: 8px 14px; } .status-engine-value { max-width: 72vw; } } .info-box { background: var(--panel); border-radius: var(--radius); padding: 12px 16px; font-family: monospace; font-size: 13px; color: var(--accent); word-break: break-all; } hr { border: none; border-top: 1px solid var(--border); } .note { font-size: 13px; color: var(--subtext); line-height: 1.5; } code { background: var(--panel); border-radius: 4px; padding: 1px 5px; font-family: monospace; font-size: 12px; } /* ── Library ────────────────────────────────────────────────────────────── */ .lib-toolbar { display: flex; align-items: center; gap: 12px; min-height: 34px; margin-bottom: 6px; } .lib-toolbar h2 { flex: 0 0 auto; } .lib-toolbar .spacer { flex: 1 1 auto; } .show-disabled-control { font-size: 13px; color: var(--subtext); display:flex; align-items:center; gap:7px; cursor:pointer; white-space:nowrap; } .loading-panel { min-height: 170px; display: flex; align-items: center; justify-content: center; border: 1px solid var(--border); border-radius: 6px; background: var(--panel); color: var(--subtext); padding: 18px; } .loading-box { width: min(680px,100%); display: flex; flex-direction: column; gap: 13px; } .loading-head { display: flex; align-items: center; gap: 10px; color: var(--text); font-weight: 700; font-size: 14px; } .spinner { width: 18px; height: 18px; border-radius: 50%; border: 2px solid var(--border); border-top-color: var(--accent); animation: spin .8s linear infinite; flex: 0 0 auto; } @keyframes spin { to { transform: rotate(360deg); } } .loading-sub { font-size: 13px; line-height: 1.4; } .skeleton-stack { display: flex; flex-direction: column; gap: 8px; } .skeleton-row { height: 38px; border-radius: 6px; border: 1px solid var(--border); background: linear-gradient(90deg, var(--panel), rgba(37,99,235,.06), var(--panel)); background-size: 220% 100%; animation: skeletonSweep 1.15s ease-in-out infinite; } @keyframes skeletonSweep { 0% { background-position: 120% 0; } 100% { background-position: -120% 0; } } .library-benchmark-panel { display: none !important; } .library-benchmark-panel .field { min-width: 0; } #benchmark-sample-text { min-height: 62px; resize: vertical; background: var(--panel); border: 1px solid var(--border); color: var(--text); border-radius: var(--radius); padding: 10px 14px; font-size: 14px; font-family: inherit; width: 100%; } .benchmark-actions { display: flex; gap: 8px; flex-wrap: wrap; } .volume-actions { align-items: flex-end; } .target-db-field { display: flex; flex-direction: column; gap: 4px; flex: 0 0 112px; color: var(--subtext); font-size: 14px; text-transform: uppercase; letter-spacing: .06em; } .target-db-field input { width: 112px; background: var(--panel); border: 1px solid var(--border); color: var(--text); border-radius: var(--radius); padding: 8px 10px; font-size: 14px; } .play-mode-field select { width: 100%; } .benchmark-progress { margin-top: 10px; padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--panel); display: flex; flex-direction: column; gap: 7px; } .benchmark-progress[hidden] { display: none; } .benchmark-progress-head { display: flex; justify-content: space-between; gap: 12px; color: var(--subtext); font-size: 13px; } #benchmark-progress-label { color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .benchmark-progress-track { height: 8px; background: var(--bg); border-radius: 999px; overflow: hidden; border: 1px solid var(--border); } #benchmark-progress-bar { height: 100%; width: 0%; background: var(--accent); transition: width .2s ease; } .benchmark-live-stats { display: grid; grid-template-columns: repeat(6, minmax(72px,1fr)); gap: 6px; font-size: 12px; color: var(--subtext); } .benchmark-live-stats span { border: 1px solid var(--border); border-radius: 5px; padding: 5px 7px; background: var(--bg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .benchmark-live-last { min-height: 1.3em; font-size: 12px; color: var(--subtext); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .library-insights { display: grid; grid-template-columns: repeat(6, minmax(100px,1fr)); gap: 6px; margin-top: 5px; } .insight { border: 1px solid var(--border); border-radius: 6px; background: var(--panel); min-height: 30px; padding: 5px 9px; display:flex; align-items:baseline; gap:8px; } .insight[data-filter] { cursor: pointer; } .insight[data-filter]:hover, .insight.active { border-color: var(--accent); background: rgba(37,99,235,.08); } .insight strong { display: block; color: var(--text); font-size: 14px; line-height: 1.1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width:0; } .insight span { display: block; color: var(--subtext); font-size: 10px; text-transform: uppercase; letter-spacing: .06em; margin-top: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width:0; line-height: 1.1; } .library-filter-note { border: 1px solid var(--border); border-radius: 6px; background: rgba(37,99,235,.05); color: var(--subtext); padding: 8px 10px; font-size: 13px; display: flex; justify-content: space-between; align-items: center; gap: 10px; margin: 8px 0; } .library-filter-note strong { color: var(--text); } .library-filter-note button { flex: 0 0 auto; } .library-control-panel { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; align-items: end; padding: 12px; border: 1px solid var(--border); border-radius: 7px; background: rgba(37,99,235,.035); } .library-control-panel .field { min-width: 0; } .library-control-panel input, .library-control-panel select { width: 100%; background: var(--panel); border: 1px solid var(--border); color: var(--text); border-radius: var(--radius); padding: 8px 10px; } .library-list-title { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 8px 2px 8px; color: var(--subtext); font-size: 12px; text-transform: uppercase; letter-spacing: .08em; font-weight: 800; } .library-list-title strong { color: var(--text); font-size: 13px; } .library-action-row { display: grid; grid-template-columns: auto 1fr auto auto; gap: 8px; align-items: center; margin: 8px 0 12px; } .voice-action-group { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; } .voice-action-group button { min-height: 34px; padding: 7px 12px; white-space: nowrap; } .action-add { grid-column: 1; } .action-refresh { grid-column: 2; justify-content: flex-start; } .action-refresh button { flex: 0 0 auto; } .action-db { display: none; } .action-benchmark { display: none; } .action-copy { display: none; } .action-show-disabled { grid-column: 4; justify-self: end; align-self: center; min-height: 34px; padding: 0 4px; margin: 0; } .benchmark-confirm { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 10px 0 0 132px; padding: 10px 12px; max-width: 760px; border: 1px solid rgba(217,119,6,.45); border-radius: 7px; background: rgba(217,119,6,.07); color: var(--text); box-shadow: var(--shadow); } .benchmark-confirm[hidden] { display: none; } .benchmark-confirm strong { color: var(--yellow); font-size: 13px; } .benchmark-confirm span { color: var(--subtext); font-size: 13px; line-height: 1.35; flex: 1 1 260px; } .benchmark-confirm button { white-space: nowrap; } .benchmark-confirm .benchmark-confirm-start { background: var(--accent); color: var(--on-accent); border-color: var(--accent); } .disabled-info { background: rgba(37,99,235,.06); border: 1px solid var(--border); border-radius: var(--radius); padding: 11px 15px; font-size: 13px; color: var(--subtext); line-height: 1.6; } .disabled-info strong { color: var(--text); } /* Voice list rows - Card grid layout */ .vl-grid { display: grid; grid-template-columns: repeat(2, minmax(320px, 1fr)); gap: 16px; padding: 12px 0; } .vl-header { display: none; } .vl-row { position: relative; padding: 16px; border: 1px solid var(--border); border-radius: 12px; background: var(--surface); transition: all .2s; display: flex; flex-direction: column; gap: 12px; } .vl-row:hover { border-color: var(--accent); background: rgba(37,99,235,.02); box-shadow: 0 8px 24px rgba(37,99,235,.15); transform: translateY(-2px); } .vl-row.vr-disabled { opacity: .42; } .vl-row.edit-open { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(37,99,235,.15); } .vr-main-row { display: flex; flex-direction: column; gap: 12px; width: 100%; } .vr-photo { width: 100%; height: 200px; border-radius: 12px; overflow: hidden; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); position: relative; display: flex; align-items: center; justify-content: center; } .vr-photo img { width: 100%; height: 100%; object-fit: cover; } .vr-photo .ph-icon { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; font-size: 64px; color: white; font-weight: 700; } .vr-photo:hover::after { content: ''; } .vr-photo.drag-over { opacity: 0.8; outline: 3px dashed var(--accent); outline-offset: -4px; transition: all 0.2s; } .vr-identity { display: flex; gap: 10px; align-items: center; min-width: 0; width: 100%; } .vr-flag { display: none; } .vr-flag .flag-emoji { font-size: 22px; line-height: 1; transition: transform .15s; } .vr-flag .flag-code { font-size: 10px; font-family: monospace; font-weight: 700; color: var(--subtext); letter-spacing: .04em; } .vr-flag:hover .flag-emoji { transform: scale(1.18); } .flag-picker { display: none; } .vr-gender { display: none; } .gender-badge { display: inline-flex; align-items: center; gap: 5px; border-radius: 6px; padding: 4px 10px; background: rgba(37,99,235,.08); border: 1px solid rgba(37,99,235,.18); cursor: pointer; transition: border-color .15s, background .15s; font-size: 13px; font-weight: 600; } .gender-badge:hover { background: rgba(37,99,235,.14); border-color: var(--accent); } .gender-sym { font-size: 14px; line-height: 1; } .gender-txt { font-size: 12px; font-weight: 600; letter-spacing: .01em; } .g-f { color: #e91e8c; } .g-m { color: #0ea5e9; } .g-n { color: #8b5cf6; } .vr-name { display: flex; align-items: center; gap: 4px; min-width: 0; flex: 1; } .vr-name-text { font-weight: 800; color: var(--text); font-family: inherit; font-size: 15px; overflow: hidden; text-overflow: ellipsis; flex: 1; cursor: text; white-space: nowrap; } .vr-name-input { flex: 1; background: var(--bg); border: 1px solid var(--accent); color: var(--text); border-radius: 4px; padding: 4px 8px; font-size: 13px; font-family: monospace; min-width: 0; } .icon-btn { background: none; border: none; padding: 3px 5px; font-size: 14px; color: var(--subtext); border-radius: 4px; cursor: pointer; flex-shrink: 0; } .icon-btn:hover { background: var(--border); color: var(--text); } .rename-confirm { display: none; gap: 3px; } .rename-confirm.show { display: flex; } .vr-type { display: none; } .vr-length { display: none; } .vr-db { display: none; } .vr-bench { display: none; } .vr-edit { display: flex; justify-content: flex-end; } .edit-audio-btn { width: 38px; height: 34px; border-radius: 7px; padding: 0; background: rgba(37,99,235,.10); border: 1px solid var(--border); color: var(--accent); font-size: 22px; line-height: 1; display: flex; align-items: center; justify-content: center; } .edit-audio-btn:hover { border-color: var(--accent); background: rgba(37,99,235,.18); filter: none; } .benchmark-one-btn { width: 26px; height: 26px; border-radius: 6px; padding: 0; font-size: 12px; } .vr-rating { display: flex; gap: 2px; font-size: 18px; justify-content: flex-start; align-items: center; min-height: 31px; } .star { cursor: pointer; color: var(--border); transition: color .1s; } .star.on { color: var(--yellow); } .vr-rating:hover .star { color: var(--yellow); } .vr-rating:hover .star ~ .star { color: var(--border); } .vr-play { display: flex; align-items: center; justify-content: flex-start; } .vr-play-group { display: flex; gap: 8px; align-items: center; justify-content: flex-start; } .vr-play button { width: 40px; height: 40px; border-radius: 50%; background: var(--accent); color: var(--on-accent); font-size: 13px; display: flex; align-items: center; justify-content: center; padding: 0; } .vr-play-synth button { background: var(--teal); } .vr-toggle { display: flex; justify-content: center; } .toggle { position: relative; display: inline-block; width: 42px; height: 24px; } .toggle input { opacity: 0; width: 0; height: 0; } .t-slider { position: absolute; cursor: pointer; inset: 0; background: var(--border); border-radius: 24px; transition: .2s; } .t-slider::before { position: absolute; content: ''; height: 18px; width: 18px; left: 3px; bottom: 3px; background: var(--bg); border-radius: 50%; transition: .2s; } .toggle input:checked + .t-slider { background: var(--green); } .toggle input:checked + .t-slider::before { transform: translateX(18px); } .vr-delete { display: flex; justify-content: flex-end; } .delete-btn { width: 30px; height: 30px; border-radius: 6px; padding: 0; background: none; border: 1px solid transparent; font-size: 15px; color: var(--subtext); display: flex; align-items: center; justify-content: center; transition: background .15s, border-color .15s, color .15s; } .delete-btn:hover { background: rgba(220,38,38,.12); border-color: var(--red); color: var(--red); filter: none; } .delete-confirm { position: absolute; right: 38px; top: 16px; z-index: 20; display: none; align-items: center; gap: 8px; min-width: 270px; max-width: min(380px, calc(100vw - 80px)); padding: 8px 9px; border: 1px solid rgba(220,38,38,.45); border-radius: 7px; background: var(--surface); color: var(--text); box-shadow: var(--shadow); } .delete-confirm strong { font-size: 12px; color: var(--red); white-space: nowrap; } .delete-confirm span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; color: var(--subtext); } .delete-confirm button { height: 28px; padding: 0 10px; border-radius: 6px; font-size: 12px; white-space: nowrap; } .delete-confirm .delete-confirm-go { background: rgba(220,38,38,.12); border-color: rgba(220,38,38,.45); color: var(--red); } .vl-row.delete-pending { border-color: rgba(220,38,38,.65); } .vl-row.delete-pending .delete-confirm { display: flex; } .vl-row.delete-pending .delete-btn { background: rgba(220,38,38,.12); border-color: var(--red); color: var(--red); } .vr-detail-row { display: none; } .vr-detail-active { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; min-height: 31px; } .vr-detail-active .vr-active-tools { justify-content: flex-end; width: 100%; } .vr-detail-delete { display: flex; align-items: start; justify-content: flex-end; min-height: 31px; padding-top: 18px; } .vr-active-tools { display: flex; gap: 10px; align-items: center; } .vr-ref, .vr-note, .vr-source { display: flex; flex-direction: column; align-items: stretch; gap: 2px; } .vr-ref .vr-inline, .vr-note .vr-inline, .vr-source .vr-inline { display: flex; gap: 6px; align-items: center; min-width: 0; } .vr-ref input { width: 100%; min-width: 0; background: transparent; border: none; border-bottom: 1px solid transparent; color: var(--subtext); font-size: 13px; padding: 3px 5px; font-family: inherit; outline: none; } .vr-ref input:focus { border-bottom-color: var(--accent); color: var(--text); } .vr-ref input::placeholder { color: var(--placehld); font-style: italic; } .ref-transcribe-btn { width: 28px; height: 28px; padding: 0; border-radius: 6px; background: none; border: 1px solid transparent; color: var(--subtext); flex-shrink: 0; font-size: 14px; } .ref-transcribe-btn:hover { background: var(--border); color: var(--text); filter: none; } .vr-note input, .vr-source input { width: 100%; background: transparent; border: none; border-bottom: 1px solid var(--border); color: var(--text); font-size: 13px; padding: 3px 5px; font-family: inherit; outline: none; } .vr-note input:focus, .vr-source input:focus { border-bottom-color: var(--accent); } .vr-note input::placeholder, .vr-source input::placeholder { color: var(--placehld); } .vr-row-subtle { color: var(--subtext); font-size: 14px; font-family: monospace; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .vr-optimizer { grid-column: auto; display: none; margin: 6px 0 0; width: 100%; box-sizing: border-box; } .vl-row.edit-open .vr-optimizer { display: block; } .optimizer-grid { display: grid; grid-template-columns: minmax(280px,1.2fr) minmax(280px,1fr); gap: 12px; align-items: start; } .opt-group { box-shadow: var(--shadow); margin-top: 20px; margin-right: 10px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); padding: 10px; min-width: 0; } .opt-group-title { font-size: 14px; font-weight: 800; color: var(--accent); text-transform: uppercase; letter-spacing: .08em;} .opt-group-note { font-size: 12px; color: var(--subtext); line-height: 1.4; margin-top: 7px; } .opt-compare-panel { grid-column: 1 / -1; } .opt-compare-grid { display: grid; grid-template-columns: repeat(2, minmax(240px,1fr)); gap: 10px; } .opt-compare-card { display: flex; flex-direction: column; gap: 7px; min-width: 0; } .opt-compare-card strong { font-size: 12px; color: var(--text); } .opt-compare-card audio { width: 100%; } .opt-style-panel { grid-column: 1 / -1; display: grid; grid-template-columns: minmax(280px,1fr) minmax(260px,.8fr); gap: 10px; } .opt-style-preview-box { display: flex; flex-direction: column; gap: 8px; min-width: 0; } .opt-style-audio { width: 100%; display: none; } .backend-help { border: 1px solid var(--border); border-radius: 6px; background: rgba(37,99,235,.05); padding: 10px 12px; color: var(--subtext); font-size: 13px; line-height: 1.45; display: grid; gap: 7px; min-width: min(100%, 620px); } .backend-help strong { color: var(--text); } .backend-help-tags { display: flex; gap: 6px; flex-wrap: wrap; } .backend-tag { border: 1px solid var(--border); border-radius: 4px; padding: 2px 6px; background: var(--panel); color: var(--subtext); font-size: 12px; } .backend-tag.good { color: #166534; border-color: rgba(22,163,74,.35); } /* AA on light */ .backend-tag.warn { color: #92400E; border-color: rgba(217,119,6,.35); } /* AA on light */ .backend-metrics-row { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 2px; } .backend-metric-tag { border: 1px solid rgba(99,102,241,.3); border-radius: 4px; padding: 1px 6px; background: rgba(99,102,241,.07); color: #1D4ED8; font-size: 11px; white-space: nowrap; } .style-backend-warn { margin-top: 6px; padding: 7px 10px; border-radius: 6px; background: rgba(220,38,38,.07); border: 1px solid rgba(220,38,38,.25); color: var(--red); font-size: 12px; line-height: 1.45; } .opt-wave { cursor: crosshair; touch-action: none; width: 100%; height: 86px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; display: block; } .opt-controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: end; margin-top: 8px; } .crop-duration-hint { align-self: center; color: var(--subtext); font-size: 12px; padding: 7px 9px; border: 1px solid var(--border); border-radius: 6px; background: var(--panel); } .crop-duration-hint.ok { color: var(--green); border-color: rgba(22,163,74,.35); } .crop-duration-hint.warn { color: var(--yellow); border-color: rgba(217,119,6,.35); } .optimizer-workflow { grid-column: 1 / -1; } .opt-maintenance { grid-column: 1 / -1; } .opt-field { display: flex; flex-direction: column; gap: 4px; } .opt-field label { font-size: 10px; color: var(--subtext); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; } .opt-field input { width: 74px; background: var(--bg); border: 1px solid var(--border); color: var(--text); border-radius: 5px; padding: 6px 8px; font-size: 12px; font-family: monospace; } .opt-field select { background: var(--bg); border: 1px solid var(--border); color: var(--text); border-radius: 5px; padding: 6px 8px; font-size: 12px; font-family: monospace; } .opt-field.wide { flex: 1 1 220px; } .opt-field.wide input, .opt-field.wide select { width: 100%; min-width: 180px; } .opt-transcript { width: 100%; min-height: 86px; background: var(--bg); border: 1px solid var(--border); color: var(--text); border-radius: 6px; padding: 8px 10px; font-size: 13px; resize: vertical; } .opt-status { font-size: 12px; color: var(--subtext); min-height: 16px; margin-top: 6px; } .opt-restart-note { font-size: 12px; color: var(--yellow); align-self: center; } .opt-restart-note[hidden] { display: none; } .opt-restart-needed .opt-restart-tts { border-color: var(--yellow); color: var(--yellow); } /* ── Audio bar ──────────────────────────────────────────────────────────── */ #lib-audio-bar { background: var(--panel); border: 1px solid var(--accent); border-radius: var(--radius); padding: 10px 14px; } #lib-audio-bar audio { width: 100%; } #lib-audio-bar .lib-audio-label { font-size: 13px; color: var(--accent); font-family: monospace; margin-bottom: 5px; font-weight: 700; } /* ── Library add panel ──────────────────────────────────────────────────── */ .lib-add-panel { display: none; border: 1px solid rgba(37,99,235,.3); border-radius: 8px; background: rgba(37,99,235,.04); padding: 16px; margin: 6px 0 12px; } .lib-add-panel.open { display: block; } .lib-add-stack { display: flex; flex-direction: column; gap: 16px; } .lib-add-section { border: 1px solid rgba(37,99,235,.2); border-radius: 8px; background: var(--surface); padding: 16px; } .lib-add-section > h2 { color: var(--accent); margin-bottom: 14px !important; letter-spacing: .08em; } .lib-add-input-grid { display: grid; grid-template-columns: minmax(300px,.9fr) minmax(420px,1.1fr); gap: 12px; align-items: stretch; } .lib-add-import-box { border: 1px solid rgba(37,99,235,.2); border-radius: 8px; background: var(--panel); padding: 14px; display: flex; flex-direction: column; gap: 14px; } .lib-add-record-box { border: 1px solid rgba(37,99,235,.2); border-radius: 8px; background: var(--panel); padding: 14px; display: flex; flex-direction: column; gap: 10px; } .lib-add-drop { border: 2px dashed rgba(37,99,235,.4); border-radius: 8px; min-height: 150px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; cursor: pointer; color: var(--subtext); text-align: center; padding: 16px; transition: border-color .15s, background .15s, color .15s; background: rgba(37,99,235,.03); } .lib-add-drop.drag-over { border-color: var(--accent); background: rgba(37,99,235,.08); color: var(--accent); } .lib-add-drop strong { color: var(--text); font-size: 16px; } .lib-add-drop span { font-size: 12px; color: var(--subtext); } .lib-add-source-box { display: flex; flex-direction: column; gap: 10px; } .lib-add-source-box input[type=text] { width: 100%; background: var(--surface); border: 1px solid rgba(37,99,235,.22); color: var(--text); border-radius: var(--radius); padding: 9px 12px; font-size: 14px; } .lib-add-source-preview { display: none; align-items: center; gap: 10px; border: 1px solid rgba(37,99,235,.25); border-radius: 8px; background: rgba(37,99,235,.06); padding: 8px 10px; min-width: 0; } .lib-add-source-preview.open { display: flex; } .lib-add-source-preview img { width: 44px; height: 44px; border-radius: 6px; object-fit: cover; border: 1px solid var(--border); background: var(--bg); } .lib-add-source-preview strong { display: block; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .lib-add-source-preview span { display: block; color: var(--subtext); font-size: 12px; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .record-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; } .record-head .note { font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--accent); } .record-head-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; } .record-head-actions button { padding: 6px 10px; font-size: 12px; } .lib-add-recorder-tools { display: flex; flex-direction: column; gap: 10px; } .lib-add-mic-row { display: grid; grid-template-columns: minmax(240px,1fr) minmax(260px,.9fr); gap: 10px; align-items: stretch; } .sample-read-box, .mic-monitor-box { border: 1px solid rgba(37,99,235,.18); border-radius: 6px; background: var(--surface); padding: 12px; } .mic-monitor-box { padding: 10px; display: flex; flex-direction: column; gap: 8px; } .sample-head, .mic-monitor-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 6px; } .sample-head label, .mic-monitor-head span:first-child { font-size: 14px; font-weight: 700; color: var(--accent); text-transform: uppercase; letter-spacing: .06em; } .sample-head select { background: var(--bg); border: 1px solid var(--border); color: var(--text); border-radius: 6px; padding: 6px 8px; font-size: 13px; min-width: 150px; } .sample-sentence { width: 100%; min-height: 120px; font-size: 16px; line-height: 1.55; color: var(--text); background: var(--surface); border: 1px solid var(--border); border-left: 4px solid var(--accent); border-radius: 6px; padding: 10px 12px; resize: vertical; font-family: inherit; } .sample-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; } .sample-actions button { padding: 6px 10px; font-size: 12px; } .recording-tips { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; } .recording-tip { font-size: 14px; color: var(--subtext); border: 1px solid rgba(37,99,235,.25); border-radius: 999px; padding: 3px 8px; background: rgba(37,99,235,.07); } .meter-readout { font-family: monospace; color: var(--subtext); font-size: 12px; white-space: nowrap; } .mic-meter { display: grid; grid-template-columns: repeat(18, 1fr); gap: 3px; height: 24px; align-items: end; margin-bottom: 8px; } .mic-meter .bar { height: 7px; border-radius: 3px; background: var(--border); transition: height .08s linear, background .08s linear, opacity .08s linear; opacity: .55; } .mic-meter .bar.on { opacity: 1; background: var(--green); } .mic-meter .bar.hot { background: var(--yellow); } .mic-meter .bar.clip { background: var(--red); } .mic-live-wave { width: 100%; height: 52px; border-radius: 4px; display: block; background: rgba(0,0,0,.06); margin-bottom: 4px; } [data-theme="dark"] .mic-live-wave { background: rgba(17,17,27,.6); } .mic-buttons { display: grid; grid-template-columns: repeat(auto-fit, minmax(96px,1fr)); gap: 8px; align-content: start; min-width: 0; } .mic-buttons button { padding-left: 10px; padding-right: 10px; min-height: 44px; } .mic-timer { font-family: monospace; font-size: 13px; color: var(--subtext); } .mic-gain-row { display: grid; grid-template-columns: auto minmax(120px,1fr) 42px; gap: 8px; align-items: center; } .mic-gain-row label { font-size: 12px; color: var(--accent); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; } .mic-gain-row input[type=range] { width: 100%; accent-color: var(--accent); } .mic-gain-value { font-family: monospace; color: var(--text); font-size: 12px; text-align: right; } .mic-help-panel { display: none; border: 1px solid var(--border); border-radius: 6px; background: rgba(37,99,235,.06); padding: 10px; color: var(--subtext); font-size: 12px; line-height: 1.45; } .mic-help-panel.open { display: block; } .mic-help-panel strong { color: var(--text); } .mic-help-panel ul { margin: 7px 0 0 18px; } .mic-help-panel li { margin: 4px 0; } .lib-add-audio-row { display: grid; grid-template-columns: minmax(240px,1fr) auto; gap: 10px; align-items: center; margin-top: 14px; } .lib-add-panel audio { width: 100%; } .lib-add-panel .opt-wave { cursor: crosshair; touch-action: none; } .lib-add-panel textarea { width: 100%; min-height: 80px; background: var(--surface); border: 1px solid rgba(37,99,235,.22); color: var(--text); border-radius: 6px; padding: 8px 10px; font-size: 13px; resize: vertical; } .lib-add-panel textarea.sample-sentence { min-height: 128px; font-size: 16px; line-height: 1.55; border-left: 3px solid var(--accent); padding: 10px 12px; } .lib-add-name-grid { display: grid; grid-template-columns: 100px 100px minmax(240px,1fr); gap: 8px; align-items: end; } .lib-add-name-grid .opt-field input, .lib-add-name-grid .opt-field select { width: 100%; } #lib-add-status { border-radius: 6px; background: rgba(37,99,235,.07); padding: 8px 10px; color: var(--subtext); font-size: 12px; } /* ── Design preset library ──────────────────────────────────────────────── */ .design-preset-library { display: flex; flex-direction: column; gap: 6px; } .design-preset-row { display: grid; grid-template-columns: minmax(120px,.8fr) 70px 90px minmax(180px,1.5fr) minmax(180px,1.4fr) 220px; gap: 8px; align-items: center; border: 1px solid var(--border); border-radius: 6px; background: var(--panel); padding: 8px 10px; font-size: 13px; } .design-preset-row .preset-desc, .design-preset-row .preset-transcript { color: var(--subtext); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .design-preset-row .preset-actions { display: flex; gap: 6px; justify-content: flex-end; } /* ── Get voices ─────────────────────────────────────────────────────────── */ .getvoices-source-editor { display: grid; grid-template-columns: 1fr auto; gap: 10px; align-items: stretch; } .getvoices-source-editor textarea { width: 100%; min-height: 96px; resize: vertical; background: var(--panel); border: 1px solid var(--border); color: var(--text); border-radius: var(--radius); padding: 10px 12px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; } .getvoices-source-buttons { display: flex; flex-direction: column; gap: 8px; min-width: 132px; } .getvoices-toolbar { display: grid; grid-template-columns: minmax(160px,1fr) 170px 150px 130px 120px auto; gap: 8px; align-items: center; margin-top: 10px; } .getvoices-toolbar input, .getvoices-toolbar select { width: 100%; background: var(--panel); border: 1px solid var(--border); color: var(--text); border-radius: var(--radius); padding: 9px 12px; } .getvoices-summary { display: grid; grid-template-columns: repeat(4, minmax(130px,1fr)); gap: 8px; } .getvoices-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px,1fr)); gap: 12px; } .voice-source-card { border: 1px solid var(--border); border-radius: 7px; background: var(--surface); padding: 12px; display: flex; flex-direction: column; gap: 10px; min-width: 0; } .voice-source-head { display: flex; gap: 12px; align-items: flex-start; min-width: 0; } .voice-source-thumb { width: 72px; height: 72px; border-radius: 7px; object-fit: cover; background: var(--bg); border: 1px solid var(--border); flex: 0 0 auto; } .voice-source-title { min-width: 0; flex: 1; } .voice-source-title strong { display: block; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .voice-source-title span { display: block; color: var(--subtext); font-size: 12px; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .voice-source-desc { color: var(--subtext); font-size: 13px; line-height: 1.4; min-height: 2.8em; } .voice-source-card audio { width: 100%; min-height: 32px; } .voice-source-actions { display: flex; gap: 7px; flex-wrap: wrap; align-items: center; } .voice-source-actions a, .voice-source-actions button { font-size: 12px; padding: 7px 9px; min-height: 32px; } .voice-source-pill { border: 1px solid var(--border); color: var(--subtext); border-radius: 999px; padding: 3px 7px; font-size: 12px; } .vs-import-footer { display: flex; justify-content: flex-end; padding-top: 4px; margin-top: -4px; border-top: 1px solid var(--border); } .vs-import-footer .btn-primary { font-size: 12px; padding: 7px 12px; } /* ── Integrations ───────────────────────────────────────────────────────── */ .integration-toolbar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; } .integration-grid { display: flex; flex-direction: column; gap: 12px; } .integration-card { border: 1px solid var(--border); border-radius: 6px; background: var(--surface); padding: 13px; display: flex; flex-direction: column; gap: 10px; width: 100%; } .integration-card h3 { font-size: 15px; color: var(--text); margin: 0; display: flex; align-items: center; gap: 7px; cursor: pointer; user-select: none; } .integration-card h3:hover { opacity: .8; } .icard-logo { width: 16px; height: 16px; flex-shrink: 0; border-radius: 2px; object-fit: contain; } .icard-icon { font-size: 16px; flex-shrink: 0; color: var(--accent); line-height: 1; } .icard-chev { display: inline-flex; align-items: center; justify-content: center; margin-left: auto; flex-shrink: 0; width: 16px; height: 16px; border-radius: 4px; background: var(--panel); border: 1px solid var(--border); color: var(--accent); font-size: 10px; line-height: 1; transition: transform .2s; } .icard-chev::before { content: '▾'; } .integration-card.icard-closed .icard-chev { transform: rotate(-90deg); } .icard-body { display: flex; flex-direction: column; gap: 10px; } .integration-card p { font-size: 13px; color: var(--subtext); line-height: 1.5; margin: 0; } .integration-card pre { background: var(--panel); border: 1px solid var(--border); border-radius: 6px; color: var(--text); padding: 10px 12px; font-size: 12px; line-height: 1.45; overflow: auto; min-height: 118px; max-height: 360px; white-space: pre-wrap; } .integration-card code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; background: none; padding: 0; border-radius: 0; } .integration-card-wide { border-left: 3px solid var(--accent); } .integration-card-wide pre { max-height: 480px; } .btn-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; } /* ── Routing ────────────────────────────────────────────────────────────── */ .routing-toolbar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; } .routing-grid { display: grid; grid-template-columns: 42px minmax(104px,.7fr) minmax(74px,.38fr) minmax(118px,.5fr) minmax(112px,.6fr) minmax(140px,1fr) 96px minmax(120px,.65fr) minmax(120px,.65fr) 42px; gap: 8px; align-items: center; } /* Input voice holds short tokens (default, DE_M) so it can be narrow; the language cell now carries a flag plus a code and needs the room instead. */ .route-speed-cell { display: flex; align-items: center; gap: 4px; } .route-speed-cell input { width: 100%; min-width: 0; text-align: center; } .route-speed-preview { flex-shrink: 0; width: 30px; height: 30px; padding: 0; display: inline-flex; align-items: center; justify-content: center; } .routing-header { padding: 8px 10px 10px; border-bottom: 1px solid var(--border); } .routing-header div { font-size: 14px; font-weight: 700; color: var(--subtext); text-transform: uppercase; letter-spacing: .07em; } .routing-row { border: 1px solid var(--border); border-radius: 6px; background: var(--surface); padding: 8px 10px; } .routing-row input, .routing-row select { width: 100%; min-width: 0; background: var(--bg); border: 1px solid var(--border); color: var(--text); border-radius: 6px; padding: 7px 8px; font-size: 13px; } .route-sound-cell { display: grid; grid-template-columns: minmax(0,1fr) auto auto; gap: 5px; align-items: center; } .route-sound-cell .route-sound-upload, .route-sound-cell .route-sound-pick { min-height: 32px; padding: 6px 8px; font-size: 12px; } .routing-row .toggle { justify-self: center; } .routing-delete { width: 34px; height: 32px; padding: 0; } .routing-help { border: 1px solid var(--border); border-radius: 6px; background: rgba(37,99,235,.05); padding: 12px; color: var(--subtext); font-size: 13px; line-height: 1.55; } .routing-url-warning { display: none; margin-top: 7px; color: var(--yellow); } .routing-url-warning.show { display: block; } .routing-test-row { margin-top: 12px; display: grid; grid-template-columns: minmax(120px,.35fr) minmax(110px,.3fr) minmax(220px,1fr) auto; gap: 8px; align-items: stretch; } .routing-test-row input, .routing-test-row textarea { width: 100%; min-width: 0; background: var(--panel); border: 1px solid var(--border); color: var(--text); border-radius: 6px; padding: 8px 10px; font-size: 13px; font-family: inherit; } .routing-test-row textarea { min-height: 38px; resize: vertical; } .routing-test-result { margin-top: 8px; font-size: 13px; color: var(--subtext); line-height: 1.45; } .routing-test-result.warn { color: var(--yellow); } .routing-test-result.ok { color: var(--green); } .routing-log-head { display:flex; justify-content:space-between; align-items:flex-start; gap:12px; flex-wrap:wrap; } .routing-log-actions { display:flex; gap:8px; align-items:center; flex-wrap:wrap; } .routing-log-filter { min-height:36px; border:1px solid var(--border); border-radius:6px; background:var(--panel); color:var(--text); padding:0 10px; font:inherit; min-width:170px; } .routing-log-list { display:flex; flex-direction:column; gap:7px; max-height:340px; overflow:auto; padding-right:4px; } .routing-log-empty { color:var(--subtext); font-size:13px; padding:10px; border:1px dashed var(--border); border-radius:6px; background:var(--panel); } .routing-log-entry { display:grid; grid-template-columns: 78px 92px minmax(120px,.75fr) minmax(180px,1.1fr) minmax(150px,.8fr) minmax(220px,1.2fr); gap:8px; align-items:center; border:1px solid var(--border); border-radius:6px; background:var(--surface); padding:8px 10px; font-size:12px; min-width: 860px; } .routing-log-entry .log-time { color:var(--subtext); font-family:monospace; } .routing-log-entry .log-app, .routing-log-entry .log-voice { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } .routing-log-entry .log-meta, .routing-log-entry .log-text { color:var(--subtext); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } .routing-log-badge { justify-self:start; border:1px solid var(--border); border-radius:999px; padding:3px 8px; font-size:11px; font-weight:800; text-transform:uppercase; letter-spacing:.06em; color:var(--subtext); background:rgba(37,99,235,.06); } .routing-log-entry.status-ok .routing-log-badge, .routing-log-entry.status-matched .routing-log-badge, .routing-log-entry.status-streaming .routing-log-badge { color:var(--green); } .routing-log-entry.status-no_match .routing-log-badge { color:var(--yellow); } .routing-log-entry.status-error .routing-log-badge { color:var(--red); } .routing-log-scroll { overflow-x:auto; } .routing-sound-browser-head { display:flex; align-items:flex-start; justify-content:space-between; gap:12px; flex-wrap:wrap; } .routing-sound-browser-actions { display:flex; gap:8px; flex-wrap:wrap; } .routing-sound-list { display:flex; flex-direction:column; gap:6px; max-height:320px; overflow:auto; padding-right:4px; } .routing-sound-search { width:100%; background:var(--panel); border:1px solid var(--border); color:var(--text); border-radius:6px; padding:8px 10px; } .routing-sound-item { display:grid; grid-template-columns:44px minmax(220px,1fr) 90px 96px; gap:8px; align-items:center; border:1px solid var(--border); background:var(--surface); border-radius:6px; padding:8px; } .routing-sound-item .sound-path { overflow:hidden; white-space:nowrap; text-overflow:ellipsis; font-family:monospace; font-size:12px; } .routing-sound-item .sound-meta { color:var(--subtext); font-size:12px; } .routing-sound-item button { min-height:32px; padding:6px 9px; font-size:12px; } .routing-sound-preview { width:100%; margin-top:8px; } /* ── Section dividers between subsections ───────────────────────────────── */ .subsection-head { display: flex; align-items: center; gap: 8px; padding: 16px 0 8px; border-bottom: 1px solid var(--border); margin-bottom: 12px; } .subsection-head h3 { font-size: 14px; font-weight: 700; color: var(--subtext); text-transform: uppercase; letter-spacing: .06em; margin: 0; } /* ── Responsive ─────────────────────────────────────────────────────────── */ @media (max-width: 900px) { .vl-grid { grid-template-columns: 1fr; } .getvoices-source-editor, .getvoices-toolbar, .getvoices-summary { grid-template-columns: 1fr; } .getvoices-source-buttons { flex-direction: row; min-width: 0; } /* .design-sample-grid's real mobile override lives in voice-design.css -- that file loads after this one and re-declares the same selector unscoped, so a copy here would always lose the cascade. */ .qwen-sample-actions { justify-content: flex-start; } .design-preset-row, .routing-grid { grid-template-columns: 1fr; } /* minmax(120px,...) minmax(110px,...) minmax(220px,...) auto needs 450px+ just for its minimums -- "Test route" and the textarea ran off-screen. */ .routing-test-row { grid-template-columns: 1fr; } } /* Mobile top bar — hidden on desktop */ #mobile-topbar { display: none; } /* Sidebar backdrop (mobile drawer overlay) */ #sidebar-backdrop { display: none; position: fixed; inset: 0; z-index: 199; background: rgba(0,0,0,0.45); backdrop-filter: blur(2px); } #sidebar-backdrop.open { display: block; } /* ── Mobile (≤ 767px) ─────────────────────────────────────────────────────── */ @media (max-width: 767px) { /* Top bar */ #mobile-topbar { display: flex; align-items: center; gap: 10px; height: 52px; padding: 0 14px; background: var(--surface); border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 100; flex-shrink: 0; } body { display: flex; flex-direction: column; } #app-shell { flex: 1; overflow: hidden; } #sidebar-toggle { background: none; border: 1px solid var(--border); border-radius: 7px; width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--text); font-size: 18px; flex-shrink: 0; } #sidebar-toggle:active { background: var(--panel); } .mobile-brand { font-size: 15px; font-weight: 800; color: var(--accent); } /* Sidebar: slide-in drawer */ #sidebar { position: fixed; top: 0; left: -240px; bottom: 0; width: 240px !important; z-index: 200; transition: left 0.22s ease; box-shadow: 4px 0 24px rgba(0,0,0,0.18); overflow-y: auto; } #sidebar.open { left: 0; } /* Main content full width, top-padded by topbar height */ #main-content { padding: 0 12px 60px; } /* Section headings -- every section actually uses .section-head, not .section-header (the selector this rule shipped with, which matched nothing); its icon/title/action row never wrapped on a narrow screen as a result, pushing "+ New voice" and similar buttons off-screen with no way to reach them. */ .section-head { flex-wrap: wrap; row-gap: 8px; } .section-head-actions { margin-left: 0; } .section-search { width: 100% !important; max-width: 100%; box-sizing: border-box; } /* Toast: full width on small screens */ #toast { left: 50%; right: auto; max-width: calc(100vw - 24px); min-width: calc(100vw - 24px); } .toast-item { max-width: 100%; } /* ── Voice workbench ──────────────────────────────────────────────────── */ .voices-workbench { flex-direction: column; height: auto !important; min-height: 0 !important; } .voices-list-pane { width: 100% !important; flex-shrink: 0; } .vl-card-list { max-height: 55vh; } .voices-inspector-pane { flex: none; min-height: 60vh; } /* When a voice is selected on mobile, hide the list and show inspector full-height */ .voices-workbench.mobile-inspector-open .voices-list-pane { display: none; } .voices-workbench.mobile-inspector-open .voices-inspector-pane { min-height: calc(100dvh - 52px); } /* Inspector back button (injected by JS) */ .insp-mobile-back { display: flex; align-items: center; gap: 6px; padding: 10px 14px; font-size: 13px; font-weight: 600; color: var(--accent); cursor: pointer; border-bottom: 1px solid var(--border); background: var(--surface); } .insp-mobile-back .mdi { font-size: 18px; } /* ── Conversation ─────────────────────────────────────────────────────── */ .conv-config-row { flex-direction: column; gap: 10px; } .conv-config-group--actions { margin-left: 0; } .conv-url-inp { width: 100% !important; box-sizing: border-box; } .conv-main { flex-direction: column; } .conv-stats-panel { width: 100% !important; flex-shrink: 0; } /* The text field, send/mic buttons, noise-gate row and three toggle labels were all one non-wrapping flex row -- the toggles ran off the right edge with no way to reach them. Text input gets its own full-width line; everything else wraps onto the ones below it. */ .conv-text-row { flex-wrap: wrap; } .conv-text-inp { flex: 1 1 100%; min-width: 0; } /* ── Routing log ──────────────────────────────────────────────────────── */ .routing-log-entry { min-width: 0 !important; grid-template-columns: auto 1fr !important; grid-template-rows: auto auto auto; } .routing-log-entry .log-duration, .routing-log-entry .log-meta, .routing-log-entry .log-text { display: none; } /* ── Try It Out ───────────────────────────────────────────────────────── */ .preview-match-panel { grid-template-columns: 1fr !important; } /* .fx-sliders shrinks (flex-shrink:1) to fit next to its checkbox toggle on the same line before .fx-row's own flex-wrap ever triggers -- its nowrap labels then overflow the squeezed container instead of the row ever dropping them to their own line. Forcing a full-width basis is what actually makes that wrap happen. */ .fx-sliders { flex-basis: 100% !important; } .fx-sliders input[type=range] { width: 84px !important; } /* ── Clone / Design section toolbar ──────────────────────────────────── */ .vl-toolbar { gap: 5px; } /* ── Voice inspector: synth-mode segmented control ────────────────────── "Preview text" / "Reference transcript" side by side never fit next to the "Synth uses" label at this width -- the row simply ran off-screen with no way to reach the second button (body's overflow-x: hidden clips it, so it wasn't merely scrollable, it was unreachable). Wrap the label onto its own line and let the segmented control take the full width below it, each button sharing it evenly. */ .vl-synth-mode-row { flex-wrap: wrap; } .vl-synth-seg { flex: 1 1 100%; } .vl-synth-seg-btn { flex: 1; text-align: center; } /* ── Settings ─────────────────────────────────────────────────────────── */ .settings-grid { grid-template-columns: 1fr !important; } .settings-cluster-grid { grid-template-columns: 1fr !important; } /* ── Language Models / Engines ───────────────────────────────────────── */ /* minmax(440px/380px, 1fr) cannot fit even one column under ~400-460px -- every card in these grids ran off-screen rather than stacking. */ /* A 1fr track still won't shrink below a grid item's own content-based minimum (grid items default to min-width:auto) -- long unbroken card content (an API-key row, a stats line) was the real remaining floor. */ .llm-service-grid, .llm-local-grid, .dc-grid { grid-template-columns: 1fr !important; } .llm-service-grid > *, .llm-local-grid > *, .dc-grid > * { min-width: 0 !important; } /* ── Generic helpers ──────────────────────────────────────────────────── */ .vl-grid { grid-template-columns: 1fr; } .section-toolbar { flex-wrap: wrap; gap: 6px; } select, input[type="text"], input[type="url"], input[type="number"], textarea { font-size: 16px; /* prevent iOS auto-zoom */ } } @media (max-width: 1100px) { .library-control-panel { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } .library-insights { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } .settings-cluster-grid { grid-template-columns: 1fr; } .lib-add-input-grid, .lib-add-mic-row { grid-template-columns: 1fr; } } /* ── Voices empty state ──────────────────────────────────────────────────── */ .voices-empty-state { padding: 32px 8px; } .voices-empty-cases { display: flex; align-items: stretch; gap: 0; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); overflow: hidden; } .voices-empty-case { flex: 1; padding: 28px 24px; display: flex; flex-direction: column; gap: 10px; } .voices-empty-case:first-child { border-right: 1px solid var(--border); } .voices-empty-case-icon { font-size: 32px; line-height: 1; } .voices-empty-case h4 { font-size: 15px; font-weight: 700; color: var(--text); margin: 0; } .voices-empty-case p { font-size: 13px; line-height: 1.6; color: var(--subtext); margin: 0; } .voices-empty-case code { background: var(--panel); border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px; font-size: 12px; } .voices-empty-divider { display: flex; align-items: center; justify-content: center; width: 40px; flex-shrink: 0; font-size: 14px; font-weight: 700; color: var(--subtext); text-transform: uppercase; letter-spacing: .08em; background: var(--panel); border-left: 1px solid var(--border); border-right: 1px solid var(--border); } .voices-folder-row { display: flex; gap: 8px; margin-top: 4px; } .voices-folder-input { flex: 1; min-width: 0; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 8px 10px; font-size: 13px; color: var(--text); font-family: monospace; } .voices-folder-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(37,99,235,.12); } .voices-empty-hint { font-size: 14.5px; color: var(--subtext); opacity: .75; line-height: 1.7; margin-top: 2px; } /* Directory browser */ .vef-dir-browser { margin-top: 8px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg); overflow: hidden; } .vef-breadcrumb { display: flex; align-items: center; flex-wrap: wrap; gap: 0; padding: 6px 10px; background: var(--panel); border-bottom: 1px solid var(--border); font-size: 12px; } .vef-crumb-btn { background: none; border: none; color: var(--accent); cursor: pointer; font-size: 12px; padding: 1px 3px; font-family: monospace; } .vef-crumb-btn:hover { text-decoration: underline; } .vef-crumb-cur { font-family: monospace; font-size: 12px; font-weight: 700; color: var(--text); padding: 1px 3px; } .vef-sep { color: var(--subtext); font-size: 12px; padding: 0 1px; } .vef-dir-list { max-height: 160px; overflow-y: auto; padding: 4px 0; } .vef-dir-item { display: flex; align-items: center; gap: 6px; width: 100%; text-align: left; padding: 5px 12px; font-size: 12px; font-family: monospace; background: none; border: none; color: var(--text); cursor: pointer; transition: background .1s; } .vef-dir-item:hover { background: var(--panel); color: var(--accent); } .vef-loading, .vef-empty, .vef-error { display: block; padding: 12px 12px; font-size: 12px; color: var(--subtext); } .vef-error { color: #e53e3e; } .vef-browser-actions { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-top: 1px solid var(--border); background: var(--panel); } .vef-selected-path { flex: 1; font-family: monospace; font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--accent); } .vef-browser-actions .btn-primary { font-size: 12px; padding: 5px 12px; flex-shrink: 0; } .voices-empty-actions { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; } .voices-empty-action-btn { width: 100%; } /* ── Cross-workflow pipeline stepper (Reader/Casting/Characters/Rehearser) ── PDF → Text → Cast Audiobook → Cast Characters (optional) → Script Rehearser → Generate MP3s → Audiobook. Every reachable stop is clickable in either direction; state lives wherever it was created (readerState/_audiobook/ rehState), so jumping around never destroys it. */ .wf-stepper { display: flex; flex-direction: column; gap: 10px; padding: 14px 20px; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 14px; } .wf-stepper-row1 { display: flex; align-items: center; gap: 0; } .wf-stepper-row2 { display: flex; align-items: flex-start; gap: 0; overflow-x: auto; padding-top: 10px; border-top: 1px solid var(--border); } .wf-nav-btn { align-self: center; display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: 50%; flex-shrink: 0; border: 1px solid var(--border); background: var(--surface); color: var(--text); cursor: pointer; font-size: 16px; margin: 0 6px; } .wf-nav-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); background: rgba(37,99,235,.08); } .wf-nav-btn:disabled { opacity: .35; cursor: not-allowed; } .wf-stepper-row2 .wf-nav-prev { margin: 0 10px 0 0; } .wf-stepper-row2 .wf-nav-next { margin-left: auto; } .wf-stepper-title { align-self: center; font-weight: 700; color: var(--text); font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .wf-step { display: flex; flex-direction: column; align-items: center; gap: 5px; background: none; border: none; padding: 2px 16px; border-radius: 10px; cursor: pointer; white-space: nowrap; color: var(--subtext); font-size: 11.5px; font-weight: 600; transition: background .15s, color .15s; flex-shrink: 0; } .wf-step:hover:not(:disabled) { background: rgba(37,99,235,.08); color: var(--accent); } .wf-step:disabled { opacity: .4; cursor: not-allowed; } .wf-step-num { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: 50%; background: var(--surface); border: 2px solid var(--border); color: var(--subtext); font-size: 13px; font-weight: 800; flex-shrink: 0; transition: background .15s, border-color .15s, color .15s, transform .15s; } .wf-step.is-done { color: var(--text); } .wf-step.is-done .wf-step-num { background: rgba(37,99,235,.12); border-color: var(--accent); color: var(--accent); } .wf-step.is-current { color: var(--accent); } .wf-step.is-current .wf-step-num { background: var(--accent); border-color: var(--accent); color: #fff; transform: scale(1.15); box-shadow: 0 0 0 4px rgba(37,99,235,.16); } .wf-step.is-optional .wf-step-num { border-style: dashed; } .wf-step small { display: block; font-weight: 500; font-size: 9.5px; opacity: .7; margin-top: -2px; } /* Connecting line between steps, like a wizard progress rail. */ .wf-step-arrow { color: var(--border); font-size: 20px; flex-shrink: 0; margin: 0 -3px; align-self: flex-start; padding-top: 4px; } .wf-step-arrow.is-done { color: var(--accent); opacity: .5; } /* ── Voices workbench ────────────────────────────────────────────────────── */ .voices-workbench { display: flex; height: calc(100vh - 148px); min-height: 420px; gap: 10px; background: transparent; overflow: visible; } /* List pane — flex column wrapper; visual card appearance is on .vl-card children */ .voices-list-pane { width: 380px; flex-shrink: 0; display: flex; flex-direction: column; gap: 8px; overflow: visible; } /* The two cards inside the list pane */ .vl-card { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); overflow: hidden; box-shadow: var(--shadow); } .vl-card-list { display: flex; flex-direction: column; flex: 1; min-height: 0; overflow: hidden; } .vl-toolbar { display: flex; flex-wrap: wrap; gap: 4px; padding: 8px 10px 7px; border-bottom: 1px solid var(--border); } .vl-tb-btn { padding: 3px 8px !important; font-size: 12px !important; flex: 0 0 auto; } .vl-filters { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; padding: 7px 10px; border-bottom: 1px solid var(--border); } .vl-search { flex: 1; min-width: 60px; padding: 5px 8px; font-size: 12.5px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg); color: var(--text); } .vl-search input::placeholder { color: var(--placehld); } .vl-search:focus { outline: none; border-color: var(--accent); } .vl-filters select { padding: 4px 5px; font-size: 12px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg); color: var(--text); max-width: 80px; } .vl-disabled-label { font-size: 14.5px; color: var(--subtext); white-space: nowrap; cursor: pointer; gap: 3px; display: flex; align-items: center; } /* Sort bar */ .vl-sort-bar { display: flex; align-items: center; gap: 5px; padding: 5px 10px; border-bottom: 1px solid var(--border); background: var(--panel); } /* Search fills the left; the Sort controls sit on the right */ .vl-search-main { flex: 1; min-width: 80px; } .vl-sort-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--subtext); flex-shrink: 0; margin-right: 1px; margin-left: auto; } #voice-sort-field { flex: 0 0 auto; padding: 3px 6px; font-size: 12px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text); cursor: pointer; min-width: 0; } #voice-sort-field:focus { outline: none; border-color: var(--accent); } .vl-sort-dir-btn { flex-shrink: 0; width: 26px; height: 26px; padding: 0; display: flex; align-items: center; justify-content: center; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--subtext); cursor: pointer; font-size: 14px; transition: background .1s, color .1s; } .vl-sort-dir-btn:hover { background: var(--accent); color: #fff; border-color: var(--accent); } .vl-sort-dir-btn.active { background: var(--accent); color: #fff; border-color: var(--accent); } #voice-list { flex: 1; overflow-y: auto; min-height: 0; } /* Virtual tag folders in the voice list */ .vl-tag-group { border-bottom: 1px solid var(--border); } .vl-tag-head { width: 100%; display: flex; align-items: center; gap: 7px; padding: 8px 12px; background: var(--panel); border: none; border-top: 1px solid var(--border); color: var(--text); font-size: 13px; font-weight: 600; cursor: pointer; position: sticky; top: 0; z-index: 2; font-family: inherit; text-align: left; } .vl-tag-head:hover { background: var(--border); } .vl-tag-head .mdi { font-size: 16px; color: var(--accent); flex-shrink: 0; } .vl-tag-caret { color: var(--subtext) !important; font-size: 15px !important; } .vl-tag-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .vl-tag-count { font-size: 11px; font-weight: 700; color: var(--subtext); background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 1px 8px; } .vl-tag-body { display: flex; flex-direction: column; } /* ── Voice groups bar ──────────────────────────────────────────────────── */ /* ── Bulk-edit toolbar ──────────────────────────────────────────────────── */ .vl-bulk-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 7px 10px; border-bottom: 2px solid rgba(37,99,235,.35); background: rgba(37,99,235,.06); position: sticky; top: 0; z-index: 10; } .vl-bulk-bar[hidden] { display: none; } .vl-bulk-count { font-size: 12px; font-weight: 700; color: var(--accent); white-space: nowrap; min-width: 64px; } .vl-bulk-btn { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; padding: 3px 9px; border-radius: 6px; border: 1px solid var(--border); background: var(--card-bg, var(--panel)); color: var(--text); cursor: pointer; white-space: nowrap; transition: background .12s; } .vl-bulk-btn:hover { background: var(--panel); border-color: var(--accent); } .vl-bulk-danger { color: var(--red, #ef4444); border-color: rgba(239,68,68,.3); } .vl-bulk-danger:hover { background: rgba(239,68,68,.1); border-color: var(--red, #ef4444); } .vl-bulk-sep { width: 1px; height: 18px; background: var(--border); margin: 0 2px; flex-shrink: 0; } .vl-bulk-tag-inp, .vl-bulk-rating-sel { height: 26px; padding: 0 8px; border: 1px solid var(--accent); border-radius: 6px; background: var(--input-bg, var(--panel)); color: var(--text); font-size: 12px; outline: none; } /* Bulk delete confirmation — centered modal */ .vl-bdc-overlay { position: fixed; inset: 0; z-index: 2000; background: rgba(0,0,0,.45); display: flex; align-items: center; justify-content: center; padding: 20px; animation: reh-cdp-in .12s ease-out; } .vl-bdc-modal { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; box-shadow: 0 18px 50px rgba(0,0,0,.3); padding: 20px; width: 440px; max-width: 100%; max-height: 82vh; display: flex; flex-direction: column; gap: 12px; } .vl-bdc-title { font-size: 17px; font-weight: 800; color: var(--text); display: flex; align-items: center; gap: 9px; } .vl-bdc-title .mdi { color: var(--red, #ef4444); font-size: 22px; } .vl-bdc-sub { font-size: 13px; color: var(--subtext); margin: 0; line-height: 1.45; } .vl-bdc-list { display: flex; flex-wrap: wrap; gap: 5px; overflow-y: auto; max-height: 220px; padding: 2px; } .vl-bdc-id { font-size: 12px; font-family: monospace; padding: 2px 8px; border-radius: 5px; background: rgba(239,68,68,.1); color: var(--red,#ef4444); border: 1px solid rgba(239,68,68,.25); white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis; } .vl-bdc-more { font-size: 12px; color: var(--subtext); align-self: center; padding: 0 4px; } .vl-bdc-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; flex-wrap: wrap; } .vl-bdc-actions button { padding: 9px 16px; border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer; font-family: var(--font); border: 1px solid var(--border); background: var(--panel); color: var(--text); } .vl-bdc-cancel:hover { border-color: var(--accent); } .vl-bdc-go { background: var(--red, #ef4444) !important; color: #fff !important; border-color: var(--red, #ef4444) !important; display: inline-flex; align-items: center; gap: 6px; } .vl-bdc-go:hover { filter: brightness(.95); } .vl-bdc-go:disabled { opacity: .75; cursor: default; } @media (max-width: 600px) { .vl-bdc-actions button { flex: 1; } } /* Bulk checkbox on each row */ .vl-bulk-cb { width: 15px; height: 15px; cursor: pointer; flex-shrink: 0; margin-right: 4px; accent-color: var(--accent); align-self: center; } .vl-row.vl-bulk-selected { background: rgba(37,99,235,.06); } /* ── Benchmark statuses ──────────────────────────────────────────────────── */ .vl-row.benchmarking-pending { background: rgba(245, 158, 11, 0.08); border-color: rgba(245, 158, 11, 0.4); transition: background 0.3s; } .vl-row.benchmarking-active { background: rgba(37, 99, 235, 0.12); border-color: var(--accent); box-shadow: 0 0 0 2px rgba(37,99,235,0.2); transition: background 0.3s; } .vl-row.benchmarking-done { background: rgba(34, 197, 94, 0.08); border-color: rgba(34, 197, 94, 0.4); transition: background 0.8s; } [data-theme="dark"] .vl-row.benchmarking-pending { background: rgba(245, 158, 11, 0.15); border-color: rgba(245, 158, 11, 0.5); } [data-theme="dark"] .vl-row.benchmarking-active { background: rgba(37, 99, 235, 0.25); box-shadow: 0 0 0 2px rgba(37,99,235,0.4); } [data-theme="dark"] .vl-row.benchmarking-done { background: rgba(34, 197, 94, 0.15); border-color: rgba(34, 197, 94, 0.5); } .voice-groups-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 7px 10px; border-bottom: 1px solid var(--border); background: rgba(245,158,11,.05); } .vg-label { font-size: 11px; font-weight: 700; color: var(--subtext); display: inline-flex; align-items: center; gap: 4px; margin-right: 4px; } .vg-chip { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; padding: 3px 6px 3px 10px; border-radius: 12px; background: var(--card-bg, var(--panel)); border: 1px solid var(--border); color: var(--text); cursor: pointer; transition: border-color .15s, background .15s; } .vg-chip:hover { border-color: #f59e0b; } .vg-chip.active { border-color: #f59e0b; background: rgba(245,158,11,.12); font-weight: 600; } .vg-count { font-size: 10px; background: rgba(0,0,0,.1); border-radius: 8px; padding: 0 5px; font-weight: 700; } .vg-del { border: none; background: none; cursor: pointer; color: var(--subtext); font-size: 15px; line-height: 1; padding: 0 2px; border-radius: 4px; } .vg-del:hover { color: var(--red, #ef4444); background: rgba(239,68,68,.12); } .vg-clear { border: none; background: none; cursor: pointer; color: var(--accent); font-size: 12px; font-weight: 600; padding: 2px 8px; } .vl-footer { padding: 7px 10px; border-top: 1px solid var(--border); font-size: 14.5px; color: var(--subtext); display: flex; align-items: center; justify-content: space-between; gap: 6px; } .vl-add-btn { padding: 4px 9px !important; font-size: 11.5px !important; } /* Compact voice row — override original card-style .vl-row */ .voices-list-pane .vl-row { display: flex !important; flex-direction: row !important; align-items: center !important; gap: 9px !important; padding: 7px 10px !important; cursor: pointer; border: none !important; border-left: 3px solid transparent !important; border-bottom: 1px solid var(--border) !important; border-radius: 0 !important; box-shadow: none !important; transform: none !important; transition: background .1s; user-select: none; background: var(--surface) !important; } .voices-list-pane .vl-row:last-child { border-bottom: none !important; } .voices-list-pane .vl-row:hover { background: var(--panel) !important; } .voices-list-pane .vl-row.vr-selected { background: rgba(37,99,235,.07) !important; border-left-color: var(--accent) !important; } .voices-list-pane .vl-row.benchmarking-pending { background: rgba(245, 158, 11, 0.08) !important; border-left-color: rgba(245, 158, 11, 0.8) !important; } .voices-list-pane .vl-row.benchmarking-active { background: rgba(37, 99, 235, 0.15) !important; border-left-color: var(--accent) !important; } .voices-list-pane .vl-row.benchmarking-done { background: rgba(34, 197, 94, 0.08) !important; border-left-color: rgba(34, 197, 94, 0.8) !important; } [data-theme="dark"] .voices-list-pane .vl-row.benchmarking-pending { background: rgba(245, 158, 11, 0.15) !important; } [data-theme="dark"] .voices-list-pane .vl-row.benchmarking-active { background: rgba(37, 99, 235, 0.25) !important; } [data-theme="dark"] .voices-list-pane .vl-row.benchmarking-done { background: rgba(34, 197, 94, 0.15) !important; } .voices-list-pane .vl-row.vr-disabled { opacity: .5; } /* Hide everything except the compact div */ .voices-list-pane .vl-row .vr-main-row, .voices-list-pane .vl-row .vr-detail-row, .voices-list-pane .vl-row .vr-optimizer { display: none !important; } /* Compact content block */ .vl-compact { display: flex; align-items: center; gap: 9px; width: 100%; min-width: 0; } .vl-compact .vr-play-group { display: flex; gap: 4px; flex-shrink: 0; } .vl-compact .vr-play-group button { width: 26px; height: 26px; border-radius: 50%; border: none; font-size: 9px; display: flex; align-items: center; justify-content: center; padding: 0; cursor: pointer; transition: filter .15s; } .vl-compact .play-btn-orig { background: var(--accent); color: #fff; } .vl-compact .play-btn-synth { background: var(--teal); color: #fff; } .vl-compact .vr-play-group button:hover { filter: brightness(1.12); } /* Avatar: rounded square, supports photo / flag-icon / initial */ .vl-avatar { width: 42px; height: 42px; border-radius: var(--radius); flex-shrink: 0; display: flex; align-items: center; justify-content: center; overflow: hidden; } .vl-avatar-photo { padding: 0; } .vl-avatar-img { width: 100%; height: 100%; object-fit: cover; border-radius: var(--radius); } .vl-avatar-flag { background: transparent; } .vl-avatar-flag .fi { display: block; width: 100%; height: 100%; background-size: cover; background-position: center; border-radius: var(--radius); } .vl-avatar-initial { background: transparent; } .vl-avatar-letter { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; font-size: 17px; font-weight: 800; border-radius: var(--radius); } /* Voice info block (name + type badge + gender chip) */ .vl-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; } .vl-name { font-size: 12.5px; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .vl-row.vr-selected .vl-name { color: var(--accent); } .vl-meta { display: flex; align-items: center; gap: 6px; } /* Type label — no background, just colored text */ .vl-type-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; } .vl-type-clone { color: var(--accent); } .vl-type-design { color: var(--mauve); } /* Separator dot between type and gender */ .vl-type-label + .vl-gender-label::before { content: '·'; color: var(--border); margin-right: 6px; font-weight: 400; } /* Gender label — no background, just colored symbol + text */ .vl-gender-letter { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; border-radius: 4px; font-weight: 800; font-size: 11px; } .vl-gender-letter.g-f { background: rgba(236, 72, 153, 0.15); color: #ec4899; } .vl-gender-letter.g-m { background: rgba(59, 130, 246, 0.15); color: #3b82f6; } .vl-gender-letter.g-n { background: rgba(156, 163, 175, 0.15); color: #9ca3af; } .vl-flag { font-size: 14px; color: var(--subtext); } .vl-meta .vr-rating { font-size: 13px; min-height: unset; gap: 0; } /* Benchmark speed chip — compact list row + inspector header */ .vl-meta .vl-bench-chip { margin-left: 2px; } .vl-bench-chip { display: inline-flex; align-items: center; gap: 2px; font-size: 10px; font-weight: 600; font-variant-numeric: tabular-nums; padding: 1px 5px; border-radius: 4px; background: rgba(20,184,166,.12); color: var(--subtext); } .vl-bench-chip .mdi { font-size: 10px; } .insp-bench-chip { display: inline-flex; align-items: center; gap: 3px; font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--subtext); } .insp-bench-chip .mdi { font-size: 11px; } .bench-ok { color: var(--green, #22c55e) !important; } .bench-warn{ color: var(--yellow, #eab308) !important; } .bench-bad { color: var(--red, #ef4444) !important; } .vl-bench-chip.bench-ok { background: rgba(34,197,94,.1); } .vl-bench-chip.bench-warn{ background: rgba(234,179,8,.1); } .vl-bench-chip.bench-bad { background: rgba(239,68,68,.1); } .vl-dbfs { display: none; } /* Table View mode */ .vl-tbl-lang, .vl-tbl-gender, .vl-tbl-type, .vl-tbl-bench, .vl-tbl-dbfs, .vl-tbl-len, .vl-tbl-dur, .vl-tbl-factor, .vl-tbl-time, .vl-tbl-wpm, .vl-tbl-rating, .vl-tbl-source, .vl-tbl-seed, .vl-tbl-note, .vl-tbl-tag, .vl-tbl-active { display: none; } /* len, dur, time hidden in table view — full detail lives in Setup → Benchmark */ .vl-table-header { display: none; } .voices-workbench.table-view .voices-list-pane { width: 100% !important; max-width: 100%; } .voices-workbench.table-view .voices-inspector-pane { display: none !important; } .voices-workbench.table-view .vl-table-header { display: grid; grid-template-columns: 24px 32px 60px minmax(140px, 1.2fr) 55px 65px 55px 55px 50px 65px 55px 85px 65px minmax(80px, 1fr) minmax(120px, 1fr) 50px; gap: 8px; align-items: center; padding: 8px 10px; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--subtext); border-bottom: 1px solid var(--border); background: var(--surface); position: sticky; top: 0; z-index: 5; } .vl-th-sortable { cursor: pointer; display: flex; align-items: center; gap: 3px; transition: color .15s; } .vl-th-sortable:hover { color: var(--text); } .vl-th-sortable .mdi { font-size: 12px; opacity: .6; } .vl-th-sortable:hover .mdi { opacity: 1; color: var(--accent); } .vl-th-info { font-size: 11px !important; opacity: .5; cursor: help; margin-left: 1px; } .vl-th-info:hover { opacity: 1; color: var(--accent); } .voices-workbench.table-view .vl-compact { display: grid; grid-template-columns: 24px 32px 60px minmax(140px, 1.2fr) 55px 65px 55px 55px 50px 65px 55px 85px 65px minmax(80px, 1fr) minmax(120px, 1fr) 50px; gap: 8px; align-items: center; } .voices-workbench.table-view .vl-bulk-cb { order: 0; margin: 0; justify-self: center; } .voices-workbench.table-view .vl-info { display: contents; } .voices-workbench.table-view .vl-meta { display: none; } .voices-workbench.table-view .vl-avatar { order: 1; width: 32px; height: 32px; } .voices-workbench.table-view .vr-play-group { order: 2; flex-shrink: 0; } .voices-workbench.table-view .vl-name, .voices-workbench.table-view .vl-name-input { order: 3; display: block; font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text); } .voices-workbench.table-view .vl-tbl-lang { display: block; order: 4; font-size: 12px; } .voices-workbench.table-view .vl-tbl-gender { display: block; order: 5; font-size: 12px; } .voices-workbench.table-view .vl-tbl-factor { display: block; order: 6; font-size: 12px; font-family: monospace; font-weight: 600; } .voices-workbench.table-view .vl-tbl-wpm { display: block; order: 7; font-size: 12px; font-family: monospace; } .voices-workbench.table-view .vl-tbl-seed { display: block; order: 8; font-size: 12px; font-family: monospace; } .voices-workbench.table-view .vl-tbl-dbfs { display: block; order: 9; font-size: 12px; font-family: monospace; } .voices-workbench.table-view .vl-tbl-type { display: block; order: 10; font-size: 12px; } .voices-workbench.table-view .vl-tbl-source { display: block; order: 11; font-size: 12px; overflow: hidden; text-overflow: ellipsis; } .voices-workbench.table-view .vl-tbl-rating { display: flex; order: 12; font-size: 14px; color: var(--subtext); } .voices-workbench.table-view .vl-tbl-rating .star.on { color: #f59e0b; } .voices-workbench.table-view .vl-tbl-tag { display: block; order: 13; font-size: 12px; color: var(--accent); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; } .voices-workbench.table-view .vl-tbl-note { display: block; order: 14; font-size: 12px; color: var(--subtext); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-style: italic; } .voices-workbench.table-view .vl-tbl-active { display: block; order: 15; font-size: 16px; text-align: center; } .voices-workbench.table-view .vl-tbl-factor.bench-ok { color: var(--success, #16a34a); } .voices-workbench.table-view .vl-tbl-factor.bench-warn { color: #d97706; } .voices-workbench.table-view .vl-tbl-factor.bench-bad { color: var(--error, #dc2626); } .voices-workbench.table-edit-mode .vl-compact { background: rgba(37,99,235,0.03); border-bottom: 1px solid var(--border); } .vl-inline-edit { background: var(--surface-light); border: 1px solid var(--border); color: var(--text); font-family: inherit; font-size: 12px; padding: 2px 4px; border-radius: 4px; outline: none; transition: border-color 0.15s; } .vl-inline-edit:focus { border-color: var(--accent); } .vl-inline-edit.vl-name-input { font-size: 13px; font-weight: 600; width: 100%; } /* Synth mode panel (segmented control + preview input) */ .vl-synth-panel { background: var(--surface); } .vl-synth-mode-row { display: flex; align-items: center; gap: 7px; padding: 5px 8px 5px; } .vl-synth-icon { font-size: 9px; color: var(--teal); flex-shrink: 0; } .vl-synth-label { font-size: 14px; font-weight: 700; color: var(--subtext); white-space: nowrap; flex-shrink: 0; } .vl-synth-seg { display: flex; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; flex-shrink: 0; } .vl-synth-seg-btn { background: var(--panel); border: none; padding: 3px 9px; font-size: 14.5px; font-weight: 600; color: var(--subtext); cursor: pointer; transition: background .12s, color .12s; white-space: nowrap; border-right: 1px solid var(--border); } .vl-synth-seg-btn:last-child { border-right: none; } .vl-synth-seg-btn.active { background: var(--teal); color: #fff; } .vl-synth-seg-btn:not(.active):hover { background: var(--border); color: var(--text); } .vl-preview-text-wrap { padding: 0 8px 6px; } .vl-preview-row { display: flex; align-items: flex-start; gap: 6px; } .vl-preview-lang-btn { flex-shrink: 0; width: 34px; height: 34px; margin-top: 1px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); padding: 0; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: border-color .15s, box-shadow .15s; } .vl-preview-lang-btn:hover { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(37,99,235,.12); filter: none; } .vl-preview-lang-btn .fi { width: 22px; height: 16px; background-size: cover; background-position: center; border-radius: 2px; display: block; } .vl-preview-input { flex: 1; min-width: 0; display: block; box-sizing: border-box; font-size: 16px; line-height: 1.5; background: var(--bg); border: 1px solid var(--border); border-radius: 5px; color: var(--text); padding: 5px 7px; font-family: var(--font); resize: vertical; min-height: 42px; } .vl-preview-input:focus { outline: none; border-color: var(--accent); } .vl-preview-text-wrap.hidden { display: none; } /* Inspector pane */ .voices-inspector-pane { flex: 1; min-width: 0; background: var(--bg); display: flex; flex-direction: column; overflow: hidden; } .inspector-placeholder { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; color: var(--placehld); text-align: center; padding: 24px; } .inspector-placeholder span { font-size: 40px; opacity: .4; } .inspector-placeholder p { font-size: 13px; line-height: 1.6; color: var(--subtext); } /* ── Inspector header ────────────────────────────────────────────────────── */ .inspector-header { background: var(--surface); border-bottom: 1px solid var(--border); flex-shrink: 0; border-top-left-radius: var(--radius); border-top-right-radius: var(--radius); border-bottom-left-radius: 0; border-bottom-right-radius: 0; margin-right: 30px; /* cause of the scrollbar */ } /* Header top: [avatar 72px] + [title-stack flex:1] */ .insp-hd-top { display: flex; align-items: flex-start; gap: 12px; padding: 12px 14px 8px; } /* Avatar — compact square */ .inspector-avatar { width: 120px; height: 120px; border-radius: var(--radius); flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-size: 26px; font-weight: 700; color: #fff; overflow: hidden; } .insp-avatar-photo { padding: 0; } .insp-avatar-img { width: 100%; height: 100%; object-fit: cover; } .inspector-avatar .fi { display: block; width: 100%; height: 100%; background-size: cover; background-position: center; } .insp-avatar-clickable { cursor: pointer; transition: opacity .15s; } .insp-avatar-clickable:hover { opacity: .75; } /* Title stack — all right-side rows stacked vertically */ .insp-title-stack { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; padding-top: 1px; } /* Row 1: name + save button */ .insp-hd-row1 { display: flex; align-items: center; gap: 8px; } .insp-disp-name { flex: 1; min-width: 0; font-size: 18px; font-weight: 600; color: var(--text); margin: 0; line-height: 1.3; cursor: default; user-select: text; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .insp-disp-name:hover { color: var(--accent); } .insp-name-edit { flex: 1; min-width: 0; font-size: 16px; font-weight: 600; color: var(--text); border: none; border-bottom: 2px solid var(--accent); background: transparent; outline: none; padding: 0; line-height: 1.3; } .insp-actions-save { flex-shrink: 0; } /* Row 2: voice ID + copy + active toggle */ .insp-hd-row2 { display: flex; align-items: center; gap: 5px; } .insp-id-block { display: flex; align-items: center; gap: 4px; flex: 1; min-width: 0; } .insp-full-id { font-size: 14px; color: var(--subtext); font-family: monospace; font-weight: 400; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; } .insp-full-id:hover { color: var(--accent); text-decoration: underline dotted; } .insp-copy-id-btn, .insp-edit-id-btn { background: none; border: 1px solid var(--border); color: var(--subtext); font-size: 9px; cursor: pointer; padding: 1px 4px; border-radius: 3px; transition: color .15s, border-color .15s; flex-shrink: 0; white-space: nowrap; } .insp-copy-id-btn:hover, .insp-edit-id-btn:hover { color: var(--accent); border-color: var(--accent); } .insp-edit-id-btn .mdi { font-size: 11px; } /* ── Clone: transcription in-progress feedback ────────────────────────────── */ @keyframes clone-tr-pulse { 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 30%, transparent); } 50% { box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent) 8%, transparent); } } #transcript-area.transcribing { border-color: var(--accent) !important; animation: clone-tr-pulse 1.2s ease-in-out infinite; } .clone-tr-status { font-size: 13px; display: inline-flex; align-items: center; gap: 6px; } .clone-tr-status.working { color: var(--accent); font-weight: 600; } .clone-tr-status.done { color: var(--green); font-weight: 600; } .clone-tr-status.error { color: var(--red); } .clone-tr-status .mdi { font-size: 15px; } .insp-actions-active { display: flex; align-items: center; flex-shrink: 0; } .insp-actions-active .vr-detail-active { display: contents; align-items: center; gap: 5px; } .insp-actions-active .vr-detail-active > label:first-child { padding-right: 10px; font-weight: 500; color: var(--subtext); } .insp-actions-active .toggle { width: 32px; height: 18px; } .insp-actions-active .t-slider::before { height: 12px; width: 12px; left: 3px; bottom: 3px; } .insp-actions-active .toggle input:checked + .t-slider::before { transform: translateX(14px); } /* Divider between ID row and subtitle */ .insp-hd-divider { height: 1px; background: var(--border); margin: 2px 0; } /* Note + delete bottom row */ .insp-note-delete-row { display: flex; align-items: center; min-width: 0; } .insp-note-slot { flex: 1; min-width: 0; display: flex; } .insp-note-slot .vr-note { display: flex; flex: 1; } .insp-note-slot .vr-note label { display: none; } .insp-note-slot .vr-note .vr-inline { display: flex; flex: 1; } .insp-note-slot .vr-note .vr-inline input { width: 100%; padding: 2px 0; font-size: 14px; font-style: italic; font-family: inherit; border: none; background: transparent; color: var(--text); outline: none; transition: color .15s; } .insp-note-slot .vr-note .vr-inline input::placeholder { color: var(--placehld); opacity: .6; } .insp-note-slot .vr-note .vr-inline input:focus { font-style: normal; } .insp-note-slot .ref-transcribe-btn { display: none !important; } .insp-actions-delete { flex-shrink: 0; display: flex; align-items: center; padding-left: 20px; margin-right: 20px } .insp-actions-delete .vr-detail-delete { display: flex; align-items: center; } .insp-actions-delete .delete-btn { background: none; border: none; color: var(--subtext); font-size: 10.5px; cursor: pointer; padding: 0; text-decoration: underline dotted; text-underline-offset: 2px; transition: color .15s; white-space: nowrap; } .insp-actions-delete .delete-btn:hover { color: var(--red); } .insp-actions-delete .delete-confirm { position: static; min-width: auto; max-width: none; box-shadow: none; display: none; align-items: center; gap: 4px; font-size: 10.5px; } .insp-actions-delete .vr-detail-delete.delete-pending .delete-confirm { display: flex; } .insp-actions-delete .vr-detail-delete.delete-pending .delete-btn { display: none; } /* Subtitle: pipe-separated info bar */ .insp-subtitle { display: flex; align-items: center; gap: 0; overflow: hidden; } .insp-subtitle > * { padding: 0 6px; flex-shrink: 0; border-right: 1px solid var(--border); white-space: nowrap; line-height: 1; } .insp-subtitle > *:first-child { padding-left: 0; } .insp-subtitle > *:last-child { border-right: none; flex: 1; flex-shrink: 1; min-width: 0; } /* Flag (small inline icon, dblclick → picker) */ .insp-flag { display: flex; align-items: center; cursor: pointer; gap: 3px; } .insp-flag-icon { display: flex; align-items: center; line-height: 1; } .insp-flag-icon .fi { width: 22px; height: 16px; background-size: cover; background-position: center; border-radius: 2px; display: inline-block; flex-shrink: 0; } .insp-flag-sel { font-size: 14px; padding: 1px 4px; border: 1px solid var(--accent); border-radius: 4px; background: var(--surface); color: var(--text); outline: none; } /* Lang wrap — dblclick to edit language */ .insp-lang-wrap { display: flex; align-items: center; cursor: default; } .insp-lang-code { font-size: 14.5px; font-weight: 600; color: var(--subtext); } .insp-lang-sel { font-size: 14px; padding: 1px 4px; border: 1px solid var(--accent); border-radius: 4px; background: var(--surface); color: var(--text); outline: none; } /* Gender label — click to cycle */ .insp-gender-label { font-size: 14.5px; color: var(--subtext); cursor: pointer; user-select: none; } .insp-gender-label:hover { color: var(--text); } /* Stars */ .insp-stars-wrap { display: flex; align-items: center; gap: 2px; cursor: pointer; } .insp-stars { display: flex; gap: 0; } .insp-star { font-size: 12px; color: var(--border); cursor: pointer; transition: color .1s; line-height: 1; } .insp-star.on { color: #f59e0b; } .insp-rating-label { font-size: 10px; color: var(--subtext); font-weight: 600; opacity: .75; } /* Tag input — inline last item in subtitle */ .insp-tag-input { border: none; background: transparent; color: var(--subtext); font-size: 14.5px; font-family: inherit; outline: none; padding: 0 6px; min-width: 50px; transition: background .15s, color .15s; } .insp-tag-input::placeholder { color: var(--placehld); opacity: .5; font-style: italic; } .insp-tag-input:hover { color: var(--text); } .insp-tag-input:focus { background: var(--panel); color: var(--text); } /* Tag autocomplete dropdown */ .tag-suggest { position: fixed; z-index: 9998; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; box-shadow: 0 4px 16px rgba(0,0,0,0.12); max-height: 220px; overflow-y: auto; padding: 3px 0; } .tag-suggest-item { display: block; width: 100%; text-align: left; padding: 5px 12px; font-size: 12px; font-family: inherit; background: none; border: none; color: var(--text); cursor: pointer; white-space: nowrap; border-radius: 0; transition: background .1s; } .tag-suggest-item:hover { background: var(--panel); filter: none; } .insp-save-btn { white-space: nowrap; font-size: 12px; padding: 4px 10px; } /* ── Searchable picker (flag / language) ─────────────────────────────────── */ .insp-picker { position: fixed; z-index: 9999; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: 0 4px 20px rgba(0,0,0,0.14); width: 244px; max-height: 290px; display: flex; flex-direction: column; overflow: hidden; } .insp-picker-search { padding: 8px 10px; border: none; border-bottom: 1px solid var(--border); font-size: 12px; font-family: inherit; background: var(--panel); color: var(--text); outline: none; flex-shrink: 0; } .insp-picker-search::placeholder { color: var(--placehld); opacity: .65; } .insp-picker-list { flex: 1; overflow-y: auto; padding: 3px 0; } .insp-picker-item { display: flex; align-items: center; width: 100%; gap: 8px; padding: 5px 10px; background: none; border: none; border-radius: 0; font-size: 12px; font-family: inherit; color: var(--text); cursor: pointer; text-align: left; white-space: nowrap; transition: background .1s; } .insp-picker-item:hover { background: var(--panel); filter: none; } .insp-picker-item .ipi-code { font-family: monospace; font-weight: 700; font-size: 14px; color: var(--accent); min-width: 28px; flex-shrink: 0; } .insp-picker-empty { padding: 10px; font-size: 12px; color: var(--subtext); text-align: center; } /* ── Inspector body ───────────────────────────────────────────────────────── */ .inspector-body { display: flex; flex-direction: column; gap: 10px; flex: 1; overflow-y: auto;} /* Optimizer: grid becomes transparent so opt-groups become flex children */ .inspector-body .vr-optimizer { display: block !important; } .inspector-body .optimizer-grid { display: contents; } .inspector-body .opt-status { display: none !important; } /* Each opt-group as a collapsible card — same visual language as .card */ .inspector-body .opt-group { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); /* no overflow:hidden here — it clips title text at rounded corners */ } /* Title row — mirrors .card-collapse-h2 */ .inspector-body .opt-group-title { display: flex; align-items: center; gap: 8px; padding: 12px 14px; font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--subtext); cursor: pointer; user-select: none; transition: background .15s; border-radius: calc(var(--radius) - 1px); /* match inner corner */ } .inspector-body .opt-group-title:hover { background: var(--panel); } .inspector-body .opt-group.open > .opt-group-title { border-bottom: 1px solid var(--border); border-bottom-left-radius: 0; border-bottom-right-radius: 0; } /* Chevron — identical to .card-chev */ .opt-chevron { display: inline-block; flex-shrink: 0; width: 16px; height: 16px; border-radius: 4px; background: var(--panel); border: 1px solid var(--border); color: var(--accent); font-size: 10px; line-height: 16px; text-align: center; transition: transform .2s; } .opt-chevron::before { content: '▾'; } .inspector-body .opt-group:not(.open) .opt-chevron { transform: rotate(-90deg); } .inspector-body .opt-group.open .opt-chevron { transform: none; } /* Title text fills the rest of the row */ .opt-title-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* Subtitle — visible only when expanded, indented to align with title text */ .inspector-body .opt-group-subtitle { font-size: 12px; color: var(--subtext); padding: 0 14px 10px 38px; line-height: 1.5; } .inspector-body .opt-group:not(.open) > .opt-group-subtitle { display: none; } /* Body — hidden when collapsed */ .opt-group-body { overflow: hidden; } .inspector-body .opt-group:not(.open) > .opt-group-body { display: none; } .opt-group-footer { display: flex; justify-content: flex-end; padding: 8px 14px 12px; } .inspector-body .opt-group canvas.opt-wave { display: block; width: 100%; height: 72px; background: var(--bg); } .inspector-body .opt-group .opt-group-body > .opt-controls { padding: 10px 14px; display: flex; flex-wrap: wrap; gap: 7px; align-items: center; } .inspector-body .opt-group .opt-group-body > textarea { display: block; width: 100%; box-sizing: border-box; padding: 10px 14px; margin: 0; background: transparent; border: none; outline: none; resize: vertical; font-size: 16px; color: var(--text); font-family: inherit; min-height: 80px; } /* Section order */ .inspector-body .opt-trim-panel { order: 10; } .inspector-body .opt-maintenance { order: 20; } .inspector-body .opt-text-panel { order: 30; } .inspector-body .opt-compare-panel { order: 40; } .inspector-body .opt-style-panel { order: 50; } /* vr-main-row in inspector — metadata grid (lang, gender, rating, rename) */ .inspector-body .vr-main-row { order: 55; display: flex !important; flex-wrap: wrap; gap: 8px; align-items: center; padding: 10px 14px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); } .inspector-body .vr-main-row .vr-photo, .inspector-body .vr-main-row .vr-type, .inspector-body .vr-main-row .vr-length, .inspector-body .vr-main-row .vr-db, .inspector-body .vr-main-row .vr-bench, .inspector-body .vr-main-row .vr-edit, .inspector-body .vr-main-row .vr-play-group { display: none !important; } .inspector-body .vr-main-row .vr-identity { display: flex; gap: 8px; align-items: center; flex: 1; min-width: 0; } .inspector-body .vr-main-row .vr-rating { display: flex; gap: 2px; align-items: center; } /* vr-detail-row in inspector — Note only (transcript is in opt-text-panel) */ .inspector-body .vr-detail-row { order: 60; display: flex !important; flex-direction: column; gap: 0; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; } .inspector-body .vr-detail-row .vr-detail-active, .inspector-body .vr-detail-row .vr-detail-delete { display: none !important; } .inspector-body .vr-detail-row .vr-ref { display: none !important; } .inspector-body .vr-note, .inspector-body .vr-source { padding: 10px 14px; display: flex; flex-direction: column; gap: 5px; } .inspector-body .vr-note label, .inspector-body .vr-source label { font-size: 10px; font-weight: 700; color: var(--subtext); text-transform: uppercase; letter-spacing: .07em; } .inspector-body .vr-note input, .inspector-body .vr-source input { background: transparent; border: none; outline: none; font-size: 13.5px; color: var(--text); width: 100%; padding: 0; } .inspector-body .vr-inline { display: flex; align-items: center; gap: 8px; } .opt-group-meta { font-size: 14px; font-weight: 600; color: var(--accent); text-transform: none; letter-spacing: 0; flex-shrink: 0; } /* vr-main-row in inspector: always hidden (metadata shown in header grid) */ .inspector-body .vr-main-row { display: none !important; } .inspector-avatar.drag-over { opacity: 0.8; outline: 3px dashed var(--accent); outline-offset: -4px; transition: all 0.2s; } .vl-avatar.drag-over { opacity: 0.8; outline: 2px dashed var(--accent); outline-offset: -2px; transition: all 0.2s; } /* ── AI Backends / LLMs section ──────────────────────────────────────────── */ /* Category pill tabs */ .llm-cat-tabs { display: flex; gap: 6px; margin-bottom: 22px; flex-wrap: wrap; } .llm-cat-tab { padding: 6px 18px; font-size: 13px; font-weight: 600; border: 1.5px solid var(--border); border-radius: 20px; background: var(--surface); color: var(--subtext); cursor: pointer; transition: all .15s; line-height: 1.4; } .llm-cat-tab:hover { border-color: var(--accent); color: var(--accent); filter: none; } .llm-cat-tab.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); box-shadow: 0 2px 10px rgba(37,99,235,.28); } /* Animated section reveal */ .llm-section { animation: llm-fadein .18s ease; } @keyframes llm-fadein { from { opacity:0; transform:translateY(5px); } to { opacity:1; transform:none; } } /* Section header row */ .llm-sec-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 18px; flex-wrap: wrap; } .engines-page-head { align-items: stretch; flex-direction: column; gap: 12px; padding: 18px 20px; } .engines-head-main { display: flex; align-items: center; gap: 14px; min-width: 0; } .engines-head-context { align-items: center; justify-content: space-between; gap: 16px; padding-top: 12px; border-top: 1px solid var(--border); } .engines-context-copy { min-width: 0; } .engines-context-copy .llm-sec-note { max-width: none; } .engines-context-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end; flex-shrink: 0; } @media (max-width: 900px) { .engines-head-context { align-items: flex-start; flex-direction: column; } .engines-context-actions { justify-content: flex-start; } } .llm-sec-title { font-size: 15px; font-weight: 700; color: var(--text); margin: 0 0 4px; } .llm-sec-note { font-size: 13px; color: var(--subtext); margin: 0; line-height: 1.5; max-width: 640px; } .llm-free-badge { font-size: 14px; font-weight: 700; letter-spacing: .02em; color: #166534; background: rgba(22,163,74,.10); border: 1px solid rgba(22,163,74,.22); border-radius: 12px; padding: 3px 12px; white-space: nowrap; flex-shrink: 0; align-self: flex-start; } .llm-free-local { color: #115E59; background: rgba(13,148,136,.10); border-color: rgba(13,148,136,.22); } /* ── Active Language Model (global picker) ─────────────── */ .llm-active-panel { padding: 16px 18px; margin-bottom: 18px; border: 1px solid rgba(13,148,136,.30); background: linear-gradient(135deg, rgba(13,148,136,.06), transparent 60%); } .llm-active-head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 14px; } .llm-active-icon { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 9px; flex-shrink: 0; color: var(--teal); background: rgba(13,148,136,.12); font-size: 18px; } .llm-active-title { font-size: 14px; font-weight: 700; color: var(--text); margin: 0 0 2px; } .llm-active-sub { font-size: 12px; color: var(--subtext); margin: 0; line-height: 1.45; } .llm-active-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } .llm-active-lbl { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 600; color: var(--subtext); white-space: nowrap; } .llm-active-controls select { height: 32px; min-width: 160px; max-width: 260px; } .llm-active-url-inp { height: 32px; min-width: 260px; max-width: 420px; flex: 1 1 260px; } .llm-active-status { font-size: 12px; color: var(--subtext); margin-left: 4px; } .llm-active-status.ok { color: var(--green); } .llm-active-status.err { color: var(--red); } /* ── Cloud service cards grid ─────────────────────────── */ .llm-service-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(440px, 1fr)); gap: 14px; margin-bottom: 8px; } .llm-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px 18px; display: flex; flex-direction: column; gap: 11px; transition: box-shadow .2s, border-color .2s, transform .15s; } .llm-card:hover { box-shadow: 0 4px 18px rgba(37,99,235,.11); border-color: rgba(37,99,235,.28); transform: translateY(-1px); } .llm-card-highlight { border-color: var(--accent); border-width: 1.5px; background: linear-gradient(135deg, rgba(37,99,235,.03) 0%, var(--surface) 100%); } /* Card head: icon bubble + name block + tier badge */ .llm-card-head { display: flex; align-items: center; gap: 11px; } .llm-card-icon { font-size: 20px; flex-shrink: 0; line-height: 1; width: 42px; height: 42px; border-radius: 10px; display: flex; align-items: center; justify-content: center; background: var(--panel); border: 1px solid var(--border); } .llm-card-name { font-size: 14px; font-weight: 700; color: var(--text); line-height: 1.2; } .llm-card-sub { font-size: 14px; color: var(--subtext); margin-top: 2px; line-height: 1.35; } .llm-tier-badge { margin-left: auto; flex-shrink: 0; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; padding: 3px 9px; border-radius: 10px; } .llm-tier-free { color: var(--green); background: rgba(22,163,74,.10); border: 1px solid rgba(22,163,74,.22); } .llm-tier-demo { color: var(--yellow); background: rgba(217,119,6,.10); border: 1px solid rgba(217,119,6,.22); } .llm-tier-paid { color: var(--mauve); background: rgba(124,58,237,.10); border: 1px solid rgba(124,58,237,.22); } /* Quick-stat chips */ .llm-card-stats { display: flex; gap: 5px; flex-wrap: wrap; } .llm-card-stats span { font-size: 14px; color: var(--subtext); background: var(--panel); border: 1px solid var(--border); padding: 2px 9px; border-radius: 10px; white-space: nowrap; } /* API key row */ .llm-field-row { display: flex; align-items: center; gap: 7px; } .llm-label { font-size: 14px; font-weight: 600; color: var(--subtext); white-space: nowrap; flex-shrink: 0; } .llm-input { flex: 1; min-width: 0; font-size: 12px; font-family: monospace; padding: 5px 10px; border: 1px solid var(--border); border-radius: 6px; background: var(--panel); color: var(--text); outline: none; transition: border-color .15s, background .15s; } .llm-input:focus { border-color: var(--accent); background: var(--surface); box-shadow: 0 0 0 3px rgba(37,99,235,.10); } .llm-link { font-size: 14px; color: var(--accent); text-decoration: none; white-space: nowrap; flex-shrink: 0; font-weight: 600; transition: opacity .15s; } .llm-link:hover { opacity: .75; } /* Endpoint display */ .llm-endpoint { display: flex; align-items: center; gap: 8px; padding: 5px 10px; background: var(--panel); border-radius: 6px; border: 1px solid var(--border); font-size: 14px; overflow: hidden; } .llm-endpoint-label { color: var(--subtext); font-weight: 600; white-space: nowrap; flex-shrink: 0; } .llm-endpoint code { font-family: monospace; font-size: 14px; color: var(--teal); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; } /* Model tags */ .llm-models { display: flex; gap: 5px; flex-wrap: wrap; } .llm-model-tag { font-size: 10.5px; font-family: monospace; color: var(--accent); background: rgba(37,99,235,.07); border: 1px solid rgba(37,99,235,.18); padding: 2px 8px; border-radius: 4px; white-space: nowrap; } /* Info / tip box */ .llm-info-box { font-size: 12px; color: var(--subtext); line-height: 1.55; background: var(--panel); border-radius: 7px; padding: 10px 13px; border-left: 3px solid var(--yellow); } .llm-link-inline { color: var(--accent); text-decoration: none; font-weight: 600; } .llm-link-inline:hover { text-decoration: underline; } .llm-info-note { font-size: 14px; color: var(--subtext); font-style: italic; } .llm-info-note code { font-family: monospace; color: var(--teal); font-style: normal; } /* ── Local services ────────────────────────────────────── */ .llm-local-cat { font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; color: var(--subtext); margin: 0 0 10px; padding-bottom: 7px; border-bottom: 1px solid var(--border); } .llm-local-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(380px, 1fr)); gap: 14px; margin-bottom: 6px; } .llm-local-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px; display: flex; flex-direction: column; gap: 9px; transition: box-shadow .2s, border-color .2s, background .2s; } .llm-local-card:hover { box-shadow: 0 4px 16px rgba(13,148,136,.12); border-color: rgba(13,148,136,.35); } .llm-local-card-running { border-color: var(--teal); background: rgba(13,148,136,.025); } .llm-local-card-online { border-color: rgba(22,163,74,.6); background: rgba(22,163,74,.04); } .llm-local-card-offline { border-color: rgba(220,38,38,.4); background: rgba(220,38,38,.03); } .llm-local-head { display: flex; align-items: center; gap: 8px; } .llm-local-icon { font-size: 20px; flex-shrink: 0; width: 26px; text-align: center; line-height: 1; } .llm-local-name { font-size: 13.5px; font-weight: 700; color: var(--text); flex: 1; min-width: 0; } .llm-local-compat { font-size: 10px; font-weight: 600; color: var(--subtext); background: var(--panel); border: 1px solid var(--border); padding: 2px 8px; border-radius: 10px; white-space: nowrap; } .llm-compat-detected { color: #115E59; background: rgba(13,148,136,.10); border-color: rgba(13,148,136,.28); } .llm-compat-running { color: var(--green, #16a34a); background: rgba(22,163,74,.10); border-color: rgba(22,163,74,.30); } .llm-compat-stopped { color: var(--red, #dc2626); background: rgba(220,38,38,.08); border-color: rgba(220,38,38,.25); } .llm-local-desc { font-size: 12px; color: var(--subtext); line-height: 1.5; margin: 0; } .llm-local-url { display: flex; align-items: center; gap: 7px; flex-wrap: nowrap; font-size: 12px; font-weight: 600; color: var(--subtext); background: var(--panel); padding: 4px 9px; border-radius: 5px; border: 1px solid var(--border); transition: border-color .15s; } .llm-local-url:focus-within { border-color: var(--accent); } .llm-local-url code { font-family: monospace; font-size: 14px; color: var(--teal); } .llm-local-url-label { white-space: nowrap; flex-shrink: 0; } .llm-local-url-inp { flex: 1; min-width: 0; font-family: monospace; font-size: 13px; color: var(--teal); background: transparent; border: none; outline: none; padding: 0; } .llm-local-url-inp::placeholder { color: var(--teal); opacity: .55; } /* Code snippet block with dark theme */ .llm-local-snippet { background: #1b1f2e; border-radius: 8px; overflow: hidden; } .llm-snippet-bar { display: flex; align-items: center; justify-content: space-between; padding: 5px 11px; background: rgba(255,255,255,.04); border-bottom: 1px solid rgba(255,255,255,.06); font-size: 10.5px; color: rgba(255,255,255,.4); font-weight: 600; letter-spacing: .02em; cursor: pointer; user-select: none; transition: background .12s; } .llm-snippet-bar:hover { background: rgba(255,255,255,.08); } .llm-snip-chev { color: rgba(255,255,255,.45); margin-right: 5px; font-size: 9px; flex-shrink: 0; } .llm-copy-btn { font-size: 10px; padding: 2px 9px; border-radius: 4px; background: rgba(255,255,255,.10); border: 1px solid rgba(255,255,255,.18); color: rgba(255,255,255,.65); cursor: pointer; transition: background .15s; font-family: inherit; } .llm-copy-btn:hover { background: rgba(255,255,255,.2); filter: none; } .llm-local-snippet pre { margin: 0; padding: 10px 12px; color: #9ecfb0; font-family: monospace; font-size: 14px; line-height: 1.65; overflow-x: auto; white-space: pre; } .llm-local-link { font-size: 14.5px; color: var(--accent); text-decoration: none; font-weight: 600; align-self: flex-start; } .llm-local-link:hover { text-decoration: underline; } /* Metric chips inside AI Backend cards */ .llm-local-metrics { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 4px; } .llm-metric-chip { font-size: 10px; padding: 1px 6px; border-radius: 4px; border: 1px solid rgba(99,102,241,.3); background: rgba(99,102,241,.07); color: #1D4ED8; white-space: nowrap; } /* ── Engine card — unified control bar ──────────────────────────────────── */ .llm-local-actions { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; } /* Single row that holds Connect + Docker controls + Use-as in left→right order */ .dc-controls-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; } .dc-ctrl-group { display: flex; gap: 4px; flex-wrap: wrap; align-items: center; } /* ── Connect / Disconnect — primary CTA, prominent blue ─────────────────── */ .llm-local-ping { padding: 5px 12px; font-size: 11.5px; font-weight: 700; border-radius: 5px; border: 1.5px solid #2563eb; background: #2563eb; color: #fff; cursor: pointer; font-family: inherit; display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; flex-shrink: 0; transition: background .15s, border-color .15s, box-shadow .15s; } .llm-local-ping:hover:not(:disabled) { background: #1d4ed8; border-color: #1d4ed8; box-shadow: 0 2px 8px rgba(37,99,235,.35); } .llm-local-ping:disabled { opacity: .5; cursor: default; } .llm-local-ping.ok { background: rgba(22,163,74,.1); border-color: rgba(22,163,74,.55); color: #16a34a; } .llm-local-ping.ok:hover:not(:disabled) { background: rgba(220,38,38,.07); border-color: var(--red); color: var(--red); } .llm-local-ping.err { border-color: var(--red); color: var(--red); background: rgba(220,38,38,.06); } /* ── Docker action buttons (Stop / Start / Restart) ─────────────────────── */ .llm-use-btn { font-size: 11.5px; padding: 5px 10px; border-radius: 5px; border: 1.5px solid rgba(13,148,136,.4); background: rgba(13,148,136,.07); color: #115E59; cursor: pointer; font-family: inherit; font-weight: 600; transition: background .15s, border-color .15s; display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; flex-shrink: 0; } .llm-use-btn:hover:not(:disabled) { background: rgba(13,148,136,.15); border-color: var(--teal); } .llm-use-btn:active { background: rgba(13,148,136,.25); } .llm-use-btn:disabled { opacity: .5; cursor: default; } /* ── "Use as TTS / STT" — activate action, right-aligned, clearly distinct ─ */ .dc-use-btn { margin-left: auto; padding: 5px 12px; font-size: 11.5px; font-weight: 700; border-radius: 5px; border: 1.5px solid #2563eb; background: #2563eb; color: #fff; cursor: pointer; font-family: inherit; display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; flex-shrink: 0; transition: background .15s, border-color .15s, box-shadow .15s; } .dc-use-btn:hover:not(:disabled) { background: #1d4ed8; border-color: #1d4ed8; box-shadow: 0 2px 8px rgba(37,99,235,.35); } .dc-use-btn.active { background: rgba(13,148,136,.22); border-color: var(--teal); box-shadow: 0 0 0 2px rgba(13,148,136,.18); } .dc-use-btn:disabled { opacity: .5; cursor: default; } /* Password show/hide toggle */ .llm-eye-btn { background: none; border: 1px solid var(--border); border-radius: 5px; padding: 3px 6px; font-size: 13px; cursor: pointer; color: var(--subtext); flex-shrink: 0; transition: border-color .15s, opacity .15s; line-height: 1; opacity: .55; } .llm-eye-btn:hover { border-color: var(--accent); opacity: 1; filter: none; } .llm-eye-btn.active { opacity: 1; border-color: var(--accent); } /* "Key saved" indicator inside card head */ .llm-saved-badge { margin-left: auto; font-size: 10px; font-weight: 700; letter-spacing: .02em; color: var(--green); background: rgba(22,163,74,.10); border: 1px solid rgba(22,163,74,.22); border-radius: 10px; padding: 2px 9px; white-space: nowrap; flex-shrink: 0; } /* Override margin-left:auto on tier badge when saved badge is also present */ .llm-card-head .llm-tier-badge { margin-left: auto; } .llm-card-head .llm-saved-badge { margin-left: 0; } /* How-to tip panel */ .llm-local-howto { display: flex; align-items: flex-start; gap: 14px; margin-top: 20px; background: var(--panel); border-radius: var(--radius); padding: 16px 18px; border-left: 4px solid var(--accent); } .llm-howto-icon { font-size: 22px; flex-shrink: 0; margin-top: 1px; } .llm-howto-body { font-size: 13px; line-height: 1.55; color: var(--text); } .llm-howto-body strong { display: block; margin-bottom: 5px; } .llm-howto-body p { margin: 0; color: var(--subtext); } .llm-howto-body em { font-style: normal; font-weight: 600; color: var(--text); } /* ── Add Custom Engine dialog ─────────────────────────────────────────────── */ .add-engine-dlg { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 0; width: min(460px, 94vw); box-shadow: 0 8px 32px rgba(0,0,0,.28); color: var(--text); } .add-engine-dlg::backdrop { background: rgba(0,0,0,.45); } .add-engine-form { padding: 22px 24px; display: flex; flex-direction: column; gap: 14px; } .add-engine-title { margin: 0 0 2px; font-size: 15px; font-weight: 700; } .add-engine-field { display: flex; flex-direction: column; gap: 5px; } .add-engine-field label { font-size: 12px; font-weight: 600; color: var(--subtext); } .add-engine-opt { font-weight: 400; opacity: .65; } .add-engine-field input, .add-engine-field select { padding: 7px 10px; border: 1px solid var(--border); border-radius: 5px; background: var(--bg); color: var(--text); font-family: inherit; font-size: 13px; transition: border-color .15s; } .add-engine-field input:focus, .add-engine-field select:focus { outline: none; border-color: var(--accent); } .add-engine-actions { display: flex; gap: 8px; justify-content: flex-end; padding-top: 4px; } /* Delete button variant */ .dc-delete-btn { border-color: rgba(220,38,38,.35) !important; color: var(--red, #dc2626) !important; background: rgba(220,38,38,.05) !important; } .dc-delete-btn:hover { background: rgba(220,38,38,.12) !important; border-color: var(--red, #dc2626) !important; } /* ── ElevenLabs Voice Browser ──────────────────────────────────────────────── */ .el-browser { padding: 0; overflow: hidden; } .el-head { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 14px; padding: 16px 20px 14px; border-bottom: 1px solid var(--border); } .el-brand { display: flex; align-items: flex-start; gap: 12px; flex: 1; min-width: 200px; } .el-brand-logo { display: flex; align-items: center; justify-content: center; width: 38px; height: 38px; border-radius: 9px; background: #131314; color: #fff; font-weight: 800; font-size: 13px; letter-spacing: -0.5px; flex-shrink: 0; margin-top: 3px; } .el-brand h2 { margin: 0 0 3px; font-size: 14px; font-weight: 700; } .el-brand p { margin: 0; font-size: 12px; } .el-key-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; } .el-key-field { display: flex; gap: 6px; } .el-key-field input { width: 230px; font-size: 12px; padding: 5px 9px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg); color: var(--text); font-family: monospace; } .el-key-btn { font-size: 12px; padding: 5px 12px; } .el-key-status { font-size: 11px; font-weight: 600; } .el-key-ok { color: #38a169; } .el-key-none { color: var(--subtext); } .el-cats { display: flex; flex-wrap: wrap; gap: 6px; padding: 11px 20px; border-bottom: 1px solid var(--border); } .el-cat { padding: 4px 13px; border-radius: 20px; border: 1px solid var(--border); background: transparent; color: var(--text); font-size: 12px; cursor: pointer; transition: border-color .15s, color .15s, background .15s; white-space: nowrap; } .el-cat:hover { border-color: var(--accent); color: var(--accent); } .el-cat.active { background: var(--accent); border-color: var(--accent); color: #fff; } .el-filters { display: flex; flex-wrap: wrap; gap: 8px; padding: 10px 20px; border-bottom: 1px solid var(--border); align-items: center; } .el-search { flex: 1; min-width: 130px; font-size: 13px; padding: 5px 9px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg); color: var(--text); font-family: inherit; } .el-filters select { font-size: 12px; padding: 5px 8px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text); font-family: inherit; } .el-status-bar { padding: 6px 20px; background: var(--panel); border-bottom: 1px solid var(--border); font-size: 12px; color: var(--subtext); } .el-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(272px, 1fr)); } .el-grid > .el-loading, .el-grid > .el-error, .el-grid > .el-empty, .el-grid > .el-idle { grid-column: 1/-1; padding: 36px 20px; text-align: center; color: var(--subtext); font-size: 13px; } .el-spinner { display: inline-block; width: 13px; height: 13px; border: 2px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: elSpin .75s linear infinite; vertical-align: middle; margin-right: 6px; } @keyframes elSpin { to { transform: rotate(360deg); } } .el-error { color: #e53e3e !important; } .el-card-voice { display: flex; align-items: stretch; gap: 10px; padding: 11px 13px 0; border-bottom: 1px solid var(--border); border-right: 1px solid var(--border); transition: background .12s; flex-wrap: wrap; } .el-card-voice:hover { background: var(--panel); } .el-vc-av { flex-shrink: 0; width: 34px; height: 34px; border-radius: 50%; background: hsl(var(--elh, 220), 52%, 52%); color: #fff; font-weight: 700; font-size: 15px; display: flex; align-items: center; justify-content: center; margin-top: 3px; } .el-vc-body { flex: 1; min-width: 0; padding-bottom: 10px; } .el-vc-name { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-bottom: 4px; } .el-vc-tags { display: flex; flex-wrap: wrap; gap: 3px; margin-bottom: 4px; } .el-tag { font-size: 10px; padding: 1px 5px; border-radius: 3px; background: var(--panel); border: 1px solid var(--border); color: var(--subtext); white-space: nowrap; } .el-lang { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 700; } .el-uc { color: var(--accent); border-color: var(--accent); background: transparent; } .el-free { color: #38a169; border-color: #38a169; background: transparent; } /* ── Get Voices Online — source tabs ──────────────────────────────────────── */ .gvo-tabs { display: flex; gap: 4px; flex-wrap: wrap; align-items: flex-end; margin: 0 0 14px; padding: 0; border-bottom: 1px solid var(--border); } .gvo-tab { display: inline-flex; align-items: center; justify-content: center; gap: 7px; padding: 11px 18px; border: 1px solid transparent; border-bottom: none; background: transparent; color: var(--subtext); font-size: 14px; font-weight: 600; border-radius: 10px 10px 0 0; cursor: pointer; transition: background .15s, color .15s, border-color .15s; font-family: inherit; position: relative; bottom: -1px; } .gvo-tab:hover { background: var(--surface); color: var(--text); } .gvo-tab.active { background: var(--panel); color: var(--accent); border-color: var(--border); border-bottom-color: var(--panel); } .gvo-tab.active::after { content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 2px; background: var(--accent); border-radius: 2px 2px 0 0; } .gvo-tab .mdi { font-size: 18px; } .gvo-tab-sub { font-size: 11px; font-weight: 500; color: var(--subtext); } .gvo-eleven-logo { background: #111; color: #fff; font-weight: 800; font-size: 11px; width: 20px; height: 20px; border-radius: 5px; display: inline-flex; align-items: center; justify-content: center; } .gvo-tab.active .gvo-eleven-logo { background: var(--accent); color: #fff; } .fa-pop-close { background: none; border: none; color: var(--subtext); cursor: pointer; font-size: 18px; padding: 0 2px; line-height: 1; } .fa-pop-close:hover { color: var(--text); } /* ── Fish.audio voice browser (fish.audio-style cards w/ hover actions) ────── */ .fa-toolbar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; padding: 12px 20px; border-bottom: 1px solid var(--border); } .fa-search-wrap { flex: 1; min-width: 200px; position: relative; display: flex; align-items: center; } .fa-search-icon { position: absolute; left: 13px; color: var(--subtext); font-size: 18px; pointer-events: none; } .fa-search-big { width: 100%; padding: 9px 14px 9px 38px; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); color: var(--text); font-family: inherit; font-size: 13px; } .fa-search-big:focus { outline: none; border-color: var(--accent); } .fa-pill-select { font-size: 13px; padding: 8px 12px; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); color: var(--text); font-family: inherit; cursor: pointer; } .fa-browse-btn { border-radius: 999px; padding: 8px 18px; } .fa-filter-anchor { position: relative; } .fa-filter-btn { position: relative; width: 38px; height: 38px; border-radius: 50%; border: 1px solid var(--border); background: var(--surface); color: var(--text); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; font-size: 18px; transition: all .12s; } .fa-filter-btn:hover, .fa-filter-btn.active { border-color: var(--accent); color: var(--accent); } .fa-filter-badge { position: absolute; top: -4px; right: -4px; min-width: 17px; height: 17px; padding: 0 4px; border-radius: 9px; background: var(--accent); color: #fff; font-size: 10px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; } /* Popover */ .fa-filter-pop { position: fixed; z-index: 1000; width: 320px; max-width: 90vw; background: var(--card-bg, var(--panel)); border: 1px solid var(--border); border-radius: 14px; box-shadow: 0 12px 36px rgba(0,0,0,.25); padding: 16px; display: flex; flex-direction: column; gap: 13px; overflow-y: auto; } /* author `display` overrides the UA [hidden] rule — restore it so the popover/badge can hide */ .fa-filter-pop[hidden], .fa-filter-badge[hidden] { display: none; } .fa-pop-head { display: flex; align-items: center; justify-content: space-between; } .fa-pop-head strong { font-size: 15px; } .fa-pop-reset { background: none; border: none; color: var(--subtext); font-size: 12px; cursor: pointer; display: inline-flex; align-items: center; gap: 4px; } .fa-pop-reset:hover { color: var(--accent); } .fa-pop-group { display: flex; flex-direction: column; gap: 7px; } .fa-pop-group > label { font-size: 12px; font-weight: 700; color: var(--text); } .fa-pchips { display: flex; flex-wrap: wrap; gap: 6px; } .fa-pchip { font-size: 12px; padding: 5px 12px; border-radius: 999px; border: 1px solid var(--border); background: var(--surface); color: var(--subtext); cursor: pointer; transition: all .12s; font-family: inherit; } .fa-pchip:hover { color: var(--text); border-color: var(--accent); } .fa-pchip.active { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; } .fa-pop-tag { width: 100%; padding: 7px 11px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); color: var(--text); font-family: inherit; font-size: 12px; } .fa-pop-tag:focus { outline: none; border-color: var(--accent); } .fa-pop-apply { width: 100%; border-radius: 999px; padding: 9px; margin-top: 2px; } @media (max-width: 560px) { .fa-filter-pop { right: auto; left: 0; } } .fa-card { display: flex; gap: 11px; padding: 12px 14px; border-bottom: 1px solid var(--border); border-right: 1px solid var(--border); position: relative; transition: background .12s; } .fa-card:hover { background: var(--panel); } .fa-card-disabled { opacity: .62; } .fa-card-avatar { position: relative; width: 48px; height: 48px; border-radius: 10px; overflow: hidden; flex-shrink: 0; background: linear-gradient(135deg, #0ea5e9, #6366f1); } .fa-avatar { width: 100%; height: 100%; object-fit: cover; display: block; } .fa-avatar-fallback::after { content: "🐟"; position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 22px; } .fa-play { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,.45); color: #fff; border: none; cursor: pointer; font-size: 22px; opacity: 0; transition: opacity .12s; } .fa-card:hover .fa-play, .fa-card-playing .fa-play { opacity: 1; } .fa-card-playing .fa-play .mdi::before { content: "\F03E4"; } /* pause glyph */ .fa-card-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; } .fa-card-top { display: flex; align-items: baseline; gap: 5px; min-width: 0; } .fa-card-name { font-size: 13.5px; font-weight: 700; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .fa-card-author { font-size: 11px; color: var(--subtext); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex-shrink: 1; } .fa-card-desc { font-size: 12px; color: var(--subtext); line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .fa-card-meta { display: flex; flex-wrap: wrap; gap: 4px; } .fa-meta-chip { font-size: 10px; padding: 1px 7px; border-radius: 999px; background: var(--surface); border: 1px solid var(--border); color: var(--subtext); white-space: nowrap; } .fa-card-foot { display: flex; align-items: center; gap: 12px; margin-top: 2px; } .fa-stat { font-size: 11px; color: var(--subtext); display: inline-flex; align-items: center; gap: 3px; } .fa-import { margin-left: auto; padding: 3px 11px; font-size: 12px; opacity: 0; transition: opacity .12s; } .fa-card:hover .fa-import, .fa-card-imported .fa-import { opacity: 1; } .fa-card-imported .fa-import { background: #16a34a; border-color: #16a34a; } @media (hover: none) { .fa-play, .fa-import { opacity: 1 !important; } } .el-feat { color: #d69e2e; border-color: #d69e2e; background: transparent; } .el-clones { opacity: .7; } .el-vc-desc { font-size: 11px; color: var(--subtext); line-height: 1.4; margin-bottom: 6px; } .el-vc-meta { margin-top: 4px; } .el-play { border: 1px solid var(--border); border-radius: 5px; background: var(--surface); color: var(--text); font-size: 11px; padding: 4px 10px; cursor: pointer; white-space: nowrap; transition: border-color .15s, color .15s; font-family: inherit; } .el-play:hover { border-color: var(--accent); color: var(--accent); } .el-vc-footer { width: 100%; display: flex; justify-content: flex-end; padding: 6px 0 10px; border-top: 1px solid var(--border); margin-top: 4px; } .el-clone { border: 1px solid var(--accent); border-radius: 5px; background: var(--accent); color: #fff; font-size: 12px; padding: 5px 12px; cursor: pointer; white-space: nowrap; transition: opacity .15s; font-family: inherit; font-weight: 600; } .el-clone:hover { opacity: .85; } .el-pager { display: flex; align-items: center; gap: 12px; padding: 12px 20px; border-top: 1px solid var(--border); } .el-pager-info { flex: 1; text-align: center; font-size: 12px; color: var(--subtext); } /* Bottom action buttons */ /* ── Card subtitle & opt-group subtitle ─────────────────────────────────── */ .card-subtitle { font-size: 12px; color: var(--subtext); line-height: 1.5; margin: 2px 0 6px; } .opt-group-subtitle { font-size: 11px; color: var(--subtext); padding: 0 14px 6px; line-height: 1.4; } /* ── Docker container grid ──────────────────────────────────────────────── */ .dc-section-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin: 0 0 12px; } .dc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(440px, 1fr)); gap: 14px; margin-bottom: 8px; } .dc-loading { grid-column: 1 / -1; padding: 18px 0; text-align: center; font-size: 13px; color: var(--subtext); } .stt-test-panel { background: var(--surface2); border: 1px solid var(--border); border-radius: 10px; padding: 10px 14px; margin-bottom: 18px; display: flex; flex-direction: column; gap: 8px; } .stt-test-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } .stt-test-label { font-size: 12px; font-weight: 600; color: var(--subtext); text-transform: uppercase; letter-spacing: .04em; white-space: nowrap; } .stt-test-select { font-size: 13px; padding: 4px 8px; border-radius: 6px; border: 1px solid var(--border); background: var(--surface); color: var(--text); flex: 1; min-width: 160px; max-width: 320px; } .stt-test-mic { width: 34px; height: 34px; border-radius: 50%; border: none; background: var(--accent); color: #fff; font-size: 16px; cursor: pointer; display: flex; align-items: center; justify-content: center; flex-shrink: 0; transition: background .15s, transform .1s; } .stt-test-mic:hover { background: var(--accent-hover, #0060d0); } .stt-test-mic.recording { background: #e53e3e; animation: stt-pulse 1s infinite; } .stt-test-mic.busy { background: var(--subtext); cursor: not-allowed; } @keyframes stt-pulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.1); } } .stt-test-status { font-size: 12px; color: var(--subtext); min-width: 60px; } .stt-test-result { font-size: 13px; color: var(--text); background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 8px 10px; white-space: pre-wrap; word-break: break-word; min-height: 32px; } .dc-load-err { color: var(--red) !important; } /* ── Engine card skeleton ────────────────────────────────────────────────── */ .dc-skel-card { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; min-height: 120px; } .dc-skel-line { border-radius: 5px; height: 13px; background: linear-gradient(90deg, var(--panel), rgba(37,99,235,.08), var(--panel)); background-size: 220% 100%; animation: skeletonSweep 1.15s ease-in-out infinite; } .dc-skel-line.wide { width: 70%; } .dc-skel-line.medium { width: 45%; } .dc-skel-line.short { width: 28%; } .dc-skel-line.tall { height: 20px; width: 55%; } .dc-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px; display: flex; flex-direction: column; gap: 6px; transition: border-color .15s, box-shadow .15s; } .dc-card:hover { box-shadow: var(--shadow); border-color: rgba(37,99,235,.3); } .dc-card-running { border-color: rgba(22,163,74,.45); background: rgba(22,163,74,.025); } .dc-card-head { display: flex; align-items: center; gap: 8px; margin-bottom: 2px; } .dc-icon { font-size: 20px; flex-shrink: 0; width: 26px; text-align: center; line-height: 1; } .dc-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; } .dc-running { background: var(--green); box-shadow: 0 0 0 3px rgba(22,163,74,.18); } .dc-stopped { background: var(--yellow); } .dc-absent { background: var(--border); } .dc-name { font-size: 13px; font-weight: 700; color: var(--text); flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .dc-badges { display: flex; align-items: center; gap: 4px; flex-shrink: 0; } .dc-role { font-size: 10px; font-weight: 700; padding: 1px 5px; border-radius: 3px; background: var(--accent); color: #fff; white-space: nowrap; } .dc-port { font-size: 10px; font-weight: 600; padding: 1px 5px; border-radius: 3px; background: var(--panel); border: 1px solid var(--border); color: var(--subtext); font-family: monospace; } .dc-status-label { font-size: 11px; color: var(--subtext); margin-bottom: 2px; } .dc-desc { font-size: 12px; color: var(--subtext); line-height: 1.45; margin: 0; flex: 1; } .dc-metrics { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 4px; } .dc-metric-chip { font-size: 10px; padding: 1px 6px; border-radius: 4px; border: 1px solid rgba(99,102,241,.3); background: rgba(99,102,241,.07); color: var(--accent); white-space: nowrap; } .dc-actions { margin-top: 6px; display: flex; gap: 6px; flex-wrap: wrap; } .dc-btn { font-size: 11.5px; padding: 4px 12px; } /* ── Performance benchmark ──────────────────────────────────────────────── */ .perf-table-wrap { overflow-x: auto; margin-top: 12px; } .perf-table { width: 100%; border-collapse: collapse; font-size: 13px; } .perf-table th, .perf-table td { padding: 7px 10px; border-bottom: 1px solid var(--border); text-align: left; white-space: nowrap; } .perf-table th { color: var(--subtext); font-size: 11px; text-transform: uppercase; letter-spacing: .05em; } .perf-th-sort { cursor: pointer; user-select: none; } .perf-th-sort:hover { color: var(--text); } .perf-th-sort::after { content: ' ⇅'; opacity: .35; font-size: 10px; } .perf-th-sort.perf-sort-asc::after { content: ' ↑'; opacity: 1; color: var(--accent); } .perf-th-sort.perf-sort-desc::after { content: ' ↓'; opacity: 1; color: var(--accent); } .perf-row-error td { color: var(--subtext); } .perf-ok { color: var(--green); font-weight: 600; } .perf-err { color: var(--red); } .perf-summary { display: flex; flex-wrap: wrap; gap: 10px 18px; padding: 10px 0 4px; font-size: 13px; } .perf-stat { display: flex; align-items: center; gap: 5px; } .perf-good { color: var(--green); } .perf-slow { color: var(--yellow); } .perf-trend-row { display: flex; align-items: center; gap: 10px; padding: 6px 0 2px; flex-wrap: wrap; font-size: 13px; } .perf-trend-label { color: var(--subtext); } .perf-trend-badge { display: inline-flex; align-items: center; gap: 4px; font-weight: 600; padding: 2px 8px; border-radius: 12px; font-size: 12px; } .perf-trend-better { background: rgba(var(--green-rgb,72,199,116),.15); color: var(--green); } .perf-trend-worse { background: rgba(var(--red-rgb,255,91,91),.15); color: var(--red); } .perf-trend-stable { background: var(--panel); color: var(--subtext); } .perf-sparkline-wrap { display: flex; align-items: center; } .perf-sparkline { width: 120px; height: 32px; display: block; } .bench-history-toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 8px; } .bench-history-filter { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--subtext); cursor: pointer; } .perf-history-list { display: flex; flex-direction: column; gap: 0; overflow-x: auto; } .perf-history-empty { font-size: 13px; color: var(--subtext); padding: 16px 0; } .perf-history-row { display: grid; grid-template-columns: 118px minmax(96px,.8fr) 34px 72px 72px minmax(190px,1.4fr) 92px 82px 68px 70px 24px; min-width: 1010px; gap: 0 10px; padding: 7px 4px; border-bottom: 1px solid var(--border); font-size: 12px; align-items: center; } .perf-history-row:last-child { border-bottom: none; } .perf-history-head { font-size: 11px; color: var(--subtext); text-transform: uppercase; letter-spacing: .05em; font-weight: 600; } .perf-history-sort { display: inline-flex; align-items: center; gap: 3px; max-width: 100%; padding: 0; border: 0; background: transparent; color: inherit; font: inherit; text-transform: inherit; letter-spacing: inherit; cursor: pointer; text-align: left; } .perf-history-sort:hover, .perf-history-sort.active { color: var(--accent); } .perf-history-sort .mdi { font-size: 13px; line-height: 1; } .perf-history-avatar { width: 24px; height: 24px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; overflow: hidden; color: #fff; font-size: 11px; font-weight: 700; } .perf-history-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; } .perf-history-avatar-icon { width: 24px !important; height: 24px !important; } .perf-history-avatar-init { flex-shrink: 0; } .perf-history-ts { color: var(--subtext); font-size: 11px; } .perf-history-del { cursor: pointer; color: var(--subtext); font-size: 11px; text-align: right; } .perf-history-del:hover { color: var(--red); } .batch-voice-toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; } .batch-search-wrap { display: flex; align-items: center; gap: 7px; min-width: min(300px, 100%); flex: 1 1 260px; height: 34px; padding: 0 10px; border: 1px solid var(--border); border-radius: 7px; background: var(--panel); color: var(--subtext); } .batch-search-wrap input { flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; color: var(--text); font-size: 13px; } .batch-voice-list { display: flex; flex-direction: column; gap: 0; max-height: 320px; overflow-y: auto; border: 1px solid var(--border); border-radius: 7px; background: var(--panel); } .batch-voice-item { display: grid; grid-template-columns: auto auto minmax(0,1fr) auto auto auto; align-items: center; gap: 9px; padding: 7px 10px; cursor: pointer; font-size: 13px; border-bottom: 1px solid var(--border); transition: background .1s; } .batch-voice-item:last-child { border-bottom: none; } .batch-voice-item:hover { background: var(--hover); } .batch-voice-item.is-selected { background: rgba(37,99,235,.08); } .batch-voice-item.is-active .batch-voice-name { font-weight: 700; } .batch-voice-avatar { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; color: #fff; font-size: 12px; font-weight: 700; } .batch-voice-main { min-width: 0; display: flex; flex-direction: column; gap: 1px; } .batch-voice-name, .batch-voice-id { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .batch-voice-name { color: var(--text); font-weight: 600; } .batch-voice-id { color: var(--subtext); font-size: 11px; font-family: monospace; } .batch-voice-meta { font-size: 11px; color: var(--subtext); white-space: nowrap; } .batch-voice-stats { font-size: 11px; color: var(--subtext); font-variant-numeric: tabular-nums; white-space: nowrap; } .batch-voice-tag { font-size: 10px; padding: 1px 6px; border-radius: 10px; background: rgba(var(--accent-rgb,99,102,241),.15); color: var(--accent); font-weight: 600; white-space: nowrap; } @media (max-width: 700px) { .batch-voice-item { grid-template-columns: auto auto minmax(0,1fr); } .batch-voice-stats, .batch-voice-tag { grid-column: 3; justify-self: start; } } .batch-progress { margin-top: 12px; display: flex; flex-direction: column; gap: 6px; } .batch-progress-head { display: flex; justify-content: space-between; font-size: 12px; color: var(--subtext); } .batch-progress-track { height: 6px; border-radius: 3px; background: var(--border); overflow: hidden; } .batch-progress-bar { height: 100%; border-radius: 3px; background: var(--accent); transition: width .3s; width: 0%; } .bench-tabs { display: flex; gap: 4px; flex-wrap: wrap; align-items: flex-end; margin: 0; padding: 0 0 0 0; border-bottom: none; } .bench-tab { display: inline-flex; align-items: center; justify-content: center; gap: 7px; padding: 11px 18px; border: 1px solid transparent; border-bottom: none; background: transparent; color: var(--subtext); font-size: 14px; font-weight: 700; border-radius: 10px 10px 0 0; cursor: pointer; font-family: inherit; position: relative; bottom: -1px; } .bench-tab:hover { background: var(--surface); color: var(--text); filter: none; } .bench-tab.active { background: var(--surface); color: var(--accent); border-color: var(--border); border-bottom-color: var(--surface); } .bench-tab.active::after { content: ""; position: absolute; left: 0; right: 0; top: -1px; height: 2px; background: var(--accent); border-radius: 2px 2px 0 0; } .bench-tab .mdi { font-size: 18px; } .bench-tab span:last-child { font-size: 11px; font-weight: 600; color: var(--subtext); } .bench-pane { display: none; } .bench-pane.active { display: flex; flex-direction: column; gap: 14px; margin-top: 0; } .bench-pane.active > .card:first-child { border-top-left-radius: 0; } .bench-card { gap: 14px; } .bench-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; flex-wrap: wrap; } .bench-card-head h3 { margin: 0; font-size: 15px; color: var(--text); } .bench-card-head p { margin: 4px 0 0; font-size: 13px; color: var(--subtext); line-height: 1.45; max-width: 920px; } .bench-input-grid { align-items: end; } .bench-library-source { margin: 2px 0 4px; max-width: 760px; } .bench-source-row { display: flex; gap: 8px; align-items: center; } .bench-source-row select { flex: 1; min-width: 220px; } .bench-source-note { margin-top: 5px; color: var(--subtext); font-size: 12px; } .bench-source-row .bvp-root { flex: 1 1 360px; min-width: min(360px, 100%); } .bvp-root { position: relative; display: inline-flex; flex-direction: column; min-width: 260px; flex: 1 1 300px; } .bvp-trigger { width: 100%; min-height: 34px; display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 0 10px; cursor: pointer; background: var(--input-bg, var(--panel)); color: var(--text); border: 1px solid var(--border); border-radius: var(--radius, 8px); text-align: left; user-select: none; } .bvp-trigger:hover, .bvp-trigger.open { border-color: var(--accent); filter: none; } .bvp-trigger-content { min-width: 0; flex: 1; display: flex; align-items: center; gap: 8px; overflow: hidden; } .bvp-trigger-main { min-width: 0; display: flex; flex-direction: column; line-height: 1.15; } .bvp-trigger-name { min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; color: var(--text); } .bvp-trigger-stats { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; color: var(--subtext); font-variant-numeric: tabular-nums; } .bvp-placeholder { font-size: 13px; color: var(--subtext); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .bvp-chevron { color: var(--subtext); flex-shrink: 0; transition: transform .15s; } .bvp-trigger.open .bvp-chevron { transform: rotate(180deg); } .bvp-avatar { flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; } .bvp-drop[hidden] { display: none !important; } .bvp-drop { position: absolute; z-index: 245; top: calc(100% + 4px); left: 0; right: 0; min-width: min(520px, 92vw); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius, 8px); box-shadow: 0 10px 28px rgba(0,0,0,.18); overflow: hidden; } .bvp-search-wrap { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-bottom: 1px solid var(--border); color: var(--subtext); background: var(--panel); } .bvp-search { flex: 1; min-width: 0; border: none; outline: none; background: transparent; color: var(--text); font-size: 13px; } .bvp-list { max-height: 340px; overflow-y: auto; padding: 4px; } .bvp-item { width: 100%; display: flex; align-items: center; gap: 9px; padding: 7px 8px; border: 0; border-radius: 6px; background: transparent; color: var(--text); text-align: left; cursor: pointer; } .bvp-item:hover, .bvp-item:focus { background: var(--hover); outline: none; filter: none; } .bvp-item.selected { background: rgba(37,99,235,.12); color: var(--accent); } .bvp-item-main { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 1px; } .bvp-item-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 600; } .bvp-item-sub { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; color: var(--subtext); } .bvp-item-stats { flex-shrink: 0; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; color: var(--subtext); font-variant-numeric: tabular-nums; } .bvp-empty { padding: 14px 12px; text-align: center; color: var(--subtext); font-size: 12px; } .bench-inline-controls .bvp-root { min-width: min(340px, 100%); flex: 1 1 340px; } @media (max-width: 700px) { .bench-source-row { flex-direction: column; align-items: stretch; } .bvp-drop { min-width: 100%; } .bvp-item-stats { max-width: 110px; } } .bench-engine-panel { border: 1px solid var(--border); border-radius: var(--radius); background: var(--panel); overflow: hidden; } .bench-engine-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 12px; border-bottom: 1px solid var(--border); font-size: 13px; } .bench-engine-head span { color: var(--subtext); } .bench-engine-list { max-height: 420px; min-height: 180px; overflow: auto; display: flex; flex-direction: column; resize: vertical; } .bench-engine-empty, .bench-empty { color: var(--subtext); font-size: 13px; padding: 16px; text-align: left; } .bench-engine-item { display: grid; grid-template-columns: auto minmax(180px, 1fr) auto minmax(160px, .45fr); gap: 10px; align-items: center; padding: 9px 12px; border-bottom: 1px solid var(--border); cursor: pointer; background: var(--surface); } .bench-engine-item:last-child { border-bottom: none; } .bench-engine-item:hover { background: color-mix(in srgb, var(--panel) 70%, var(--surface)); } .bench-engine-main { min-width: 0; display: flex; flex-direction: column; gap: 2px; } .bench-engine-main strong { font-size: 13px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .bench-engine-main small { font-size: 11px; color: var(--subtext); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: monospace; } .bench-engine-state { font-size: 11px; font-weight: 700; padding: 2px 7px; border-radius: 999px; background: var(--panel); color: var(--subtext); border: 1px solid var(--border); } .bench-engine-item.is-online .bench-engine-state { color: var(--green); background: rgba(22,163,74,.09); border-color: rgba(22,163,74,.28); } .bench-engine-item.is-offline .bench-engine-state { color: var(--red); background: rgba(220,38,38,.07); border-color: rgba(220,38,38,.24); } .bench-engine-model { min-width: 0; } .bench-model-picker { position: relative; min-width: 0; } .bench-model-trigger { width: 100%; min-height: 32px; display: flex; align-items: center; justify-content: space-between; gap: 8px; background: var(--panel); color: var(--text); border: 1px solid var(--border); border-radius: 5px; padding: 6px 8px; font-family: monospace; font-size: 12px; cursor: pointer; text-align: left; } .bench-model-trigger.open, .bench-model-trigger:hover { border-color: var(--accent); filter: none; } .bench-model-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .bench-model-menu { position: absolute; z-index: 240; top: calc(100% + 4px); left: 0; right: 0; background: var(--surface); border: 1px solid var(--border); border-radius: 7px; box-shadow: var(--shadow); overflow: hidden; min-width: min(520px, 90vw); } .bench-model-menu[hidden] { display: none !important; } .bench-model-search-row { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-bottom: 1px solid var(--border); color: var(--subtext); } .bench-model-search { flex: 1; min-width: 0; border: none; outline: none; background: transparent; color: var(--text); font-size: 13px; } .bench-model-options { max-height: 260px; overflow: auto; padding: 4px; } .bench-model-option { width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 9px; border: 0; border-radius: 5px; background: transparent; color: var(--text); font-family: monospace; font-size: 12px; cursor: pointer; text-align: left; } .bench-model-option:hover, .bench-model-option.selected { background: var(--hover); filter: none; } .bench-model-option.selected { color: var(--accent); font-weight: 700; } .bench-model-empty { color: var(--subtext); font-size: 12px; padding: 12px; } .bench-status { font-size: 13px; color: var(--subtext); } .bench-summary { display: flex; gap: 10px 18px; flex-wrap: wrap; min-height: 20px; } .bench-table td strong { display: block; } .bench-table td small { display: block; color: var(--subtext); font-size: 11px; font-family: monospace; max-width: 280px; overflow: hidden; text-overflow: ellipsis; } .bench-output { max-width: 680px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .bench-device { display: inline-flex; align-items: center; padding: 2px 7px; border-radius: 999px; background: var(--panel); border: 1px solid var(--border); color: var(--subtext); font-size: 11px; font-weight: 700; } .bench-device.gpu { color: var(--green); background: rgba(22,163,74,.09); border-color: rgba(22,163,74,.28); } .bench-device.cpu { color: var(--yellow); background: rgba(217,119,6,.09); border-color: rgba(217,119,6,.28); } .bench-device.cloud { color: var(--accent); background: rgba(37,99,235,.08); border-color: rgba(37,99,235,.24); } .bench-inline-controls { display: flex; gap: 6px; min-width: min(620px, 100%); } .bench-inline-controls input, .bench-inline-controls select { min-width: 0; } .bench-turn-main .bench-turn-log { min-height: 440px; overflow-y: auto; padding: 16px; } .bench-turn-log audio { width: min(520px, 100%); margin: 8px 0 0; } @media (max-width: 900px) { .bench-engine-item { grid-template-columns: auto minmax(0, 1fr); } .bench-engine-state, .bench-engine-model { grid-column: 2; } .bench-inline-controls { flex-wrap: wrap; min-width: 0; } .bench-inline-controls input, .bench-inline-controls select, .bench-inline-controls .bvp-root { flex: 1 1 180px; } } @media (max-width: 560px) { .bench-tab span:not(.mdi) { display: none; } .bench-tab { padding: 9px 12px; } } /* ── Chunked TTS toggle ──────────────────────────────────────────────────── */ .chunk-toggle-label { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--subtext); cursor: pointer; user-select: none; } .chunk-toggle-label input { cursor: pointer; } .chunk-progress { font-size: 12px; color: var(--accent); padding: 6px 0 0; display: flex; align-items: center; gap: 6px; } .chunk-progress::before { content: '⏳'; font-size: 13px; } /* ── Effects panel ───────────────────────────────────────────────────────── */ .effects-preset-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 16px; } .field-label-sm { font-size: 12px; font-weight: 600; color: var(--subtext); white-space: nowrap; } .effects-grid { display: flex; flex-direction: column; gap: 14px; } .fx-row { display: flex; align-items: flex-start; gap: 16px; flex-wrap: wrap; } .fx-toggle { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--text); min-width: 120px; flex-shrink: 0; cursor: pointer; user-select: none; padding: 4px 0; } .fx-toggle input { cursor: pointer; } .fx-sliders { display: flex; flex-wrap: wrap; gap: 8px 20px; flex: 1; min-width: 0; } .fx-sliders label { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--subtext); white-space: nowrap; } .fx-sliders input[type=range] { width: 110px; cursor: pointer; } .fx-sliders span { font-size: 11px; color: var(--text); font-variant-numeric: tabular-nums; min-width: 44px; } /* ── History panel ───────────────────────────────────────────────────────── */ .history-list { display: flex; flex-direction: column; gap: 8px; } .history-empty { font-size: 13px; color: var(--subtext); padding: 12px 0; } /* ── Voice Picker ───────────────────────────────────────────────────────── */ .vp-root { position: relative; display: inline-flex; flex-direction: column; min-width: 200px; flex: 1 1 200px; } .vp-trigger { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 0 10px; height: 34px; cursor: pointer; background: var(--input-bg, var(--panel)); border: 1px solid var(--border); border-radius: var(--radius, 8px); user-select: none; transition: border-color .15s; } .vp-trigger:hover, .vp-trigger.open { border-color: var(--accent); } .vp-trigger-content { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; overflow: hidden; } .vp-trigger-name { font-size: 13px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; } .vp-trigger-meta { margin-left: auto; flex-shrink: 0; color: var(--subtext); font-size: 12px; font-variant-numeric: tabular-nums; letter-spacing: 0; } .vp-trigger-placeholder { font-size: 13px; color: var(--subtext); } .vp-chevron { font-size: 16px; color: var(--subtext); flex-shrink: 0; transition: transform .15s; } .vp-trigger.open .vp-chevron { transform: rotate(180deg); } .vp-avatar { flex-shrink: 0; } .vp-flag { font-size: 13px; flex-shrink: 0; } .vp-drop[hidden] { display: none !important; } /* attachTextPicker's floating variant — appended to and positioned in JS (next to whatever text input it's browsing for), not inside a .vp-root. */ .vp-drop-floating { position: fixed; z-index: 2001; } .route-output-wrap { position: relative; display: inline-flex; align-items: center; width: 100%; } .route-output-wrap .route-output { flex: 1; min-width: 0; padding-right: 24px; } .vp-text-browse { position: absolute; right: 4px; top: 50%; transform: translateY(-50%); border: none; background: none; color: var(--subtext); cursor: pointer; padding: 2px; line-height: 1; border-radius: 4px; } .vp-text-browse:hover { color: var(--accent); background: var(--panel); } .vp-drop { position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 200; background: var(--card-bg, var(--panel)); border: 1px solid var(--border); border-radius: var(--radius, 8px); box-shadow: 0 8px 24px rgba(0,0,0,.18); display: flex; flex-direction: column; max-height: 320px; overflow: hidden; } .vp-search-wrap { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-bottom: 1px solid var(--border); flex-shrink: 0; } .vp-search-icon { color: var(--subtext); font-size: 15px; } .vp-search { flex: 1; background: none; border: none; outline: none; font-size: 13px; color: var(--text); } .vp-list { overflow-y: auto; flex: 1; padding: 4px 0; } .vp-item { display: flex; align-items: center; gap: 8px; padding: 6px 12px; cursor: pointer; font-size: 13px; color: var(--text); } .vp-item:hover, .vp-item:focus { background: rgba(37,99,235,.08); outline: none; } .vp-item.selected { background: rgba(37,99,235,.13); font-weight: 600; } .vp-item-name { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; } .vp-item-meta { margin-left: auto; flex: 0 0 auto; min-width: 42px; text-align: right; color: var(--subtext); font-size: 12px; font-weight: 600; letter-spacing: 0; font-variant-numeric: tabular-nums; } .vp-empty { padding: 12px; text-align: center; font-size: 12px; color: var(--subtext); } /* ── Generation History ─────────────────────────────────────────────────── */ .hist-avatar { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; flex-shrink: 0; } .hist-avatar-init { display: inline-flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; color: #fff; } .hist-del-btn { margin-left: auto; background: none; border: none; cursor: pointer; color: var(--subtext); font-size: 14px; padding: 2px 4px; border-radius: 4px; display: flex; align-items: center; opacity: .6; transition: opacity .15s, color .15s; } .hist-del-btn:hover { opacity: 1; color: var(--red, #ef4444); } .history-item { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; display: flex; flex-direction: column; gap: 5px; transition: border-color .15s; } .history-item:hover { border-color: rgba(37,99,235,.3); } .history-item-row1 { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; } .history-voice { font-size: 12px; font-weight: 700; color: var(--accent); background: rgba(37,99,235,.07); border: 1px solid rgba(37,99,235,.2); border-radius: 4px; padding: 1px 7px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 200px; } .history-backend { font-size: 11px; color: var(--subtext); white-space: nowrap; } .history-time { font-size: 11px; color: var(--subtext); font-variant-numeric: tabular-nums; white-space: nowrap; } .history-item-text { font-size: 12px; color: var(--text); line-height: 1.4; } .history-item-actions { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 2px; } .btn-xs { font-size: 11px; padding: 3px 9px; border-radius: 5px; } /* ── Playlist panel ──────────────────────────────────────────────────────── */ .playlist-list { display: flex; flex-direction: column; gap: 6px; } .playlist-item { display: flex; align-items: center; gap: 10px; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 8px 12px; transition: border-color .15s; } .playlist-item:hover { border-color: rgba(37,99,235,.3); } .playlist-num { font-size: 12px; font-weight: 700; color: var(--subtext); min-width: 18px; text-align: center; } .playlist-info { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; } .playlist-text { font-size: 12px; color: var(--subtext); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .playlist-item-actions { display: flex; gap: 4px; flex-shrink: 0; } /* ── Refine panel ────────────────────────────────────────────────────────── */ .refine-config { display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-start; margin-bottom: 14px; } .refine-toggles { display: flex; flex-wrap: wrap; gap: 8px 20px; margin-bottom: 4px; } .refine-toggle { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text); cursor: pointer; user-select: none; } .refine-toggle input { cursor: pointer; } /* ── Persona inspector panel ─────────────────────────────────────────────── */ .insp-persona-panel { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px; margin: 12px 0; display: flex; flex-direction: column; gap: 10px; } .insp-persona-head { display: flex; flex-direction: column; gap: 3px; } .insp-persona-title { font-size: 13px; font-weight: 700; color: var(--text); } .insp-persona-hint { font-size: 11px; color: var(--subtext); line-height: 1.4; } .insp-persona-input { width: 100%; font-size: 13px; padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text); font-family: var(--font); resize: vertical; min-height: 72px; transition: border-color .15s; } .insp-persona-input:focus { border-color: var(--accent); outline: none; } .insp-persona-actions { display: flex; align-items: center; gap: 10px; } .insp-persona-status { font-size: 11px; color: var(--green); } /* ── Preview persona button ──────────────────────────────────────────────── */ .preview-persona-btn { font-size: 12px; padding: 4px 11px; border: 1px solid var(--teal); color: var(--teal); background: rgba(13,148,136,.06); border-radius: 5px; cursor: pointer; font-family: var(--font); transition: background .15s; } .preview-persona-btn:hover { background: rgba(13,148,136,.12); } .preview-persona-btn:disabled { opacity: .5; cursor: default; } /* ── Seed Finder ─────────────────────────────────────────────────────────── */ .seed-finder-group { margin-top: 12px; } .seed-finder-body { padding: 12px 14px 14px; display: flex; flex-direction: column; gap: 12px; } .seed-finder-config { display: flex; flex-direction: column; gap: 10px; } .seed-finder-row { display: flex; flex-direction: column; gap: 5px; } .seed-finder-label { font-size: 12px; font-weight: 600; color: var(--subtext); } .seed-finder-text { width: 100%; font-size: 13px; padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text); font-family: var(--font); resize: vertical; min-height: 68px; transition: border-color .15s; } .seed-finder-text:focus { border-color: var(--accent); outline: none; } .seed-finder-params { flex-direction: row; flex-wrap: wrap; align-items: flex-end; gap: 14px; } .seed-finder-field { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text); } .seed-finder-field input[type="number"] { width: 64px; padding: 5px 7px; font-size: 13px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text); text-align: center; } .seed-finder-field select { padding: 5px 8px; font-size: 13px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text); } .seed-finder-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } .seed-finder-status { font-size: 12px; color: var(--subtext); } .seed-finder-pin { margin-top: 12px; padding-top: 10px; border-top: 1px dashed var(--border); } .seed-finder-pin-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 4px; } .seed-finder-pin-input { width: 90px; padding: 5px 8px; border: 1px solid var(--border); border-radius: 7px; background: var(--panel); color: var(--text); font-family: var(--font); } .seed-finder-pin-status { font-size: 12px; color: var(--accent); font-weight: 600; } .seed-finder-progress { display: flex; flex-direction: column; gap: 6px; } .seed-finder-progress-head { display: flex; justify-content: space-between; font-size: 12px; color: var(--subtext); } .seed-finder-bar { height: 100%; background: var(--accent); transition: width .2s ease; border-radius: 999px; } .seed-finder-results { display: flex; flex-direction: column; gap: 6px; } .seed-finder-result-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 7px 10px; border-radius: var(--radius); border: 1px solid var(--border); background: var(--surface); font-size: 13px; } .seed-finder-result-row.sfr-active { border-color: var(--accent); background: rgba(var(--accent-rgb, 124,77,255),.07); } .seed-finder-result-row.seed-finder-result-error { opacity: .6; } .sfr-seed { font-weight: 700; min-width: 58px; color: var(--text); } .sfr-dur { color: var(--subtext); font-size: 12px; min-width: 170px; flex: 1 1 210px; } .sfr-play-btn { font-size: 12px; padding: 4px 10px; } .sfr-use-btn { font-size: 12px; padding: 4px 10px; } .sfr-saved { font-size: 12px; color: var(--green); font-weight: 600; } .sfr-error { font-size: 12px; color: var(--red); } .sfr-batch-header { font-size: 12px; color: var(--subtext); font-style: italic; padding: 4px 2px 2px; } .sfr-batch-tag { color: var(--accent); font-style: italic; } .seed-finder-result-row.sfr-batch { background: rgba(var(--accent-rgb, 124,77,255),.03); } /* ── Conversation Playground ─────────────────────────────────────────────── */ .conv-config-bar { padding: 14px 18px 10px; margin-bottom: 0; } /* Conversation: stack STT · LLM · TTS · System prompt full-width, one per row */ .conv-config-bar .engine-setup-row { flex-direction: column; flex-wrap: nowrap; gap: 12px; } .conv-config-bar .engine-setup-col { flex: 1 1 auto; width: 100%; min-width: 0; border-right: none; padding-right: 0; border-bottom: 1px solid var(--border); padding-bottom: 12px; } .conv-config-bar .engine-setup-col:last-child { border-bottom: none; padding-bottom: 0; } .conv-config-bar .conv-prompt-row { margin-top: 12px; } .conv-config-bar .conv-system-textarea { width: 100%; } /* ── Unified Engine Config Rows (used across tools) ───────────────── */ .engine-setup-row { display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-start; margin-bottom: 16px; padding: 12px 14px; background: rgba(0,0,0,0.02); border: 1px solid var(--border); border-radius: 8px; } .engine-setup-col { flex: 1; min-width: 220px; display: flex; flex-direction: column; gap: 6px; border-right: 1px solid var(--border); padding-right: 16px; } .engine-setup-col:last-child, .engine-setup-col.no-border { border-right: none; padding-right: 0; } .engine-setup-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--subtext); display: flex; align-items: center; gap: 6px; margin-bottom: 2px; } .engine-setup-label .mdi { font-size: 14px; } .engine-setup-link { color: var(--subtext); opacity: 0.6; transition: opacity 0.15s, color 0.15s; margin-left: auto; /* Push gear to the right */ } .engine-setup-link:hover { opacity: 1; color: var(--accent); } .engine-setup-controls { display: flex; gap: 6px; align-items: center; width: 100%; flex-wrap: wrap; } .engine-setup-controls select, .engine-setup-controls input[type="text"] { flex: 1; min-width: 0; background: var(--bg); border: 1px solid var(--border); color: var(--text); border-radius: var(--radius); padding: 8px 12px; font-size: 14px; } .conv-config-row { display: flex; gap: 14px; flex-wrap: wrap; align-items: flex-end; } .conv-config-group { display: flex; flex-direction: column; gap: 4px; } .conv-config-group--actions { margin-left: auto; } .conv-cfg-label { font-size: 11px; font-weight: 700; color: var(--subtext); text-transform: uppercase; letter-spacing: .06em; } .conv-url-inp { width: 220px; padding: 6px 10px; border: 1px solid var(--border); border-radius: var(--radius); font-size: 13px; background: var(--surface); color: var(--text); } .conv-prompt-row { display: flex; align-items: flex-start; gap: 10px; margin-top: 10px; border-top: 1px solid var(--border); padding-top: 10px; } .conv-prompt-row .conv-cfg-label { padding-top: 7px; white-space: nowrap; } .conv-system-textarea { flex: 1; resize: vertical; min-height: 34px; max-height: 120px; padding: 6px 10px; border: 1px solid var(--border); border-radius: var(--radius); font-size: 13px; font-family: var(--font); background: var(--surface); color: var(--text); } /* Layout */ .conv-main { display: flex; gap: 14px; margin-top: 14px; } /* Chat panel grows to fill remaining height; input bar stays pinned at bottom */ .conv-chat-panel { flex: 1; display: flex; flex-direction: column; gap: 0; min-width: 0; height: calc(100vh - 340px); min-height: 440px; } /* Collapsible engine/prompt config */ .conv-config-toggle { display: flex; align-items: center; gap: 8px; cursor: pointer; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--subtext); user-select: none; padding-bottom: 2px; } .conv-config-toggle:hover { color: var(--text); } .conv-config-toggle .conv-config-chevron { margin-left: auto; transition: transform .18s ease; font-size: 18px; } .conv-config-summary { font-weight: 500; text-transform: none; letter-spacing: 0; opacity: .8; font-size: 11.5px; } .conv-config-body { margin-top: 10px; } .conv-config-bar.collapsed .conv-config-body { display: none; } .conv-config-bar.collapsed .conv-config-chevron { transform: rotate(180deg); } .conv-config-bar.collapsed .conv-config-summary { display: inline; } .conv-config-bar:not(.collapsed) .conv-config-summary { display: none; } /* Desktop: make the Conversation fill the viewport height — no page scroll; only the chat window scrolls internally. Collapsing the config frees space. */ @media (min-width: 768px) { #s-conversation.is-active { display: flex; flex-direction: column; height: 100%; min-height: 0; } #s-conversation .conv-main { flex: 1; min-height: 0; margin-top: 12px; } #s-conversation .conv-chat-panel { height: auto; min-height: 0; } #s-conversation .conv-stats-panel { overflow-y: auto; } } /* Read Aloud — fit the document area into the viewport height so the transport + synthesise bar below it stay visible (only the document scrolls). */ @media (min-width: 768px) { #s-reader.is-active, #s-caststudio.is-active { display: flex; flex-direction: column; height: 100%; min-height: 0; } #s-caststudio #tab-caststudio { flex: 1; min-height: 0; display: flex !important; flex-direction: column; } #s-caststudio #stu-phase-2:not([hidden]) { flex: 1; min-height: 0; display: flex; flex-direction: column; } #s-caststudio #stu-cast-slot:not([hidden]), #s-caststudio #stu-castchars-slot:not([hidden]) { flex: 1; min-height: 0; display: flex; flex-direction: column; } #s-reader #tab-reader { flex: 1; min-height: 0; display: flex; flex-direction: column; } #s-caststudio #tab-reader { flex: 1; min-height: 0; display: flex; flex-direction: column; } #s-reader #reader-main-view { flex: 1; min-height: 0; display: flex; flex-direction: column; } #s-caststudio #reader-main-view { flex: 1; min-height: 0; display: flex; flex-direction: column; } #s-reader #reader-main-view[hidden] { display: none; } #s-caststudio #reader-main-view[hidden] { display: none; } #s-reader #reader-doc { flex: 1; min-height: 0; overflow-y: auto; } #s-caststudio #reader-doc { flex: 1; min-height: 0; overflow-y: auto; } } .conv-stats-panel { width: 280px; flex-shrink: 0; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; display: flex; flex-direction: column; gap: 8px; } /* Chat window fills the space above the input bar; warning box scrolls inside it */ .conv-chat-window { flex: 1; min-height: 0; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius) var(--radius) 0 0; padding: 16px; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; } .conv-chat-welcome { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; flex: 1; color: var(--subtext); text-align: center; } /* Bubbles */ .conv-bubble-wrap { display: flex; flex-direction: column; max-width: 72%; } .conv-bubble-wrap--user { align-self: flex-end; align-items: flex-end; } .conv-bubble-wrap--assistant { align-self: flex-start; align-items: flex-start; } .conv-bubble { padding: 10px 14px; border-radius: 16px; font-size: 14.5px; line-height: 1.55; word-break: break-word; } .conv-bubble--user { background: var(--accent); color: #fff; border-bottom-right-radius: 4px; } .conv-bubble--assistant { background: var(--panel); color: var(--text); border: 1px solid var(--border); border-bottom-left-radius: 4px; } .conv-bubble--error { background: rgba(220,38,38,.08); color: var(--red); border: 1px solid rgba(220,38,38,.2); border-radius: 10px; padding: 8px 12px; font-size: 13px; } .conv-bubble-meta { font-size: 11px; color: var(--subtext); margin-top: 3px; padding: 0 4px; } /* Typing dots */ .conv-typing { display: inline-flex; gap: 4px; align-items: center; padding: 4px 2px; } .conv-typing span { width: 7px; height: 7px; border-radius: 50%; background: var(--subtext); opacity: .4; animation: convDot 1.2s infinite; } .conv-typing span:nth-child(2) { animation-delay: .2s; } .conv-typing span:nth-child(3) { animation-delay: .4s; } @keyframes convDot { 0%,80%,100% { transform: scale(.7); opacity:.3; } 40% { transform: scale(1); opacity:.9; } } /* ── Collapsible "thinking" (reasoning) panel inside an assistant bubble ── */ .conv-think-wrap { border: 1px solid var(--border); border-radius: 10px; margin-bottom: 8px; background: var(--surface); overflow: hidden; } .conv-think-toggle { width: 100%; display: flex; align-items: center; gap: 6px; padding: 7px 10px; background: none; border: none; cursor: pointer; font-family: inherit; font-size: 12.5px; font-weight: 600; color: var(--subtext); text-align: left; } .conv-think-toggle:hover { color: var(--text); } .conv-think-toggle .mdi { transition: transform .15s; font-size: 15px; } .conv-think-toggle[aria-expanded="true"] .mdi { transform: rotate(90deg); } .conv-think-body { padding: 0 12px 10px; font-size: 12.5px; line-height: 1.5; color: var(--subtext); max-height: 220px; overflow-y: auto; white-space: pre-wrap; word-break: break-word; } .conv-answer-text { white-space: pre-wrap; word-break: break-word; } /* ── Conversation input bar (text field + send + mic) ───────────────────── */ .conv-input-bar { background: var(--surface); border: 1px solid var(--border); border-top: none; border-radius: 0 0 var(--radius) var(--radius); padding: 10px 16px 12px; display: flex; flex-direction: column; gap: 8px; } .conv-mic-status { font-size: 13px; color: var(--subtext); min-height: 18px; } .conv-text-row { display: flex; align-items: center; gap: 8px; } .conv-text-inp { flex: 1; min-width: 0; padding: 10px 14px; font-size: 14px; font-family: inherit; border: 1.5px solid var(--border); border-radius: 22px; background: var(--panel); color: var(--text); outline: none; transition: border-color .15s; } .conv-text-inp:focus { border-color: var(--accent); } .conv-text-inp::placeholder { color: var(--subtext); opacity: .7; } .conv-send-btn { flex-shrink: 0; width: 40px; height: 40px; border-radius: 50%; border: none; background: var(--accent); color: #fff; font-size: 17px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background .15s, transform .1s; } .conv-send-btn:hover:not(:disabled) { background: #1d4ed8; transform: scale(1.05); } .conv-send-btn:disabled { opacity: .4; cursor: default; } .conv-divider { width: 1px; height: 32px; background: var(--border); flex-shrink: 0; } .conv-mic-btn { flex-shrink: 0; width: 52px; height: 52px; border-radius: 50%; border: 2px solid var(--accent); background: var(--accent); color: #fff; font-size: 22px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background .15s, transform .1s, box-shadow .15s; } .conv-mic-btn:hover { background: #1d4ed8; } .conv-mic-btn.live { background: var(--accent); border-color: var(--accent); box-shadow: 0 0 0 4px rgba(37,99,235,.16); } .conv-mic-btn.live:not(.processing) { animation: convPulse 1.4s infinite; } .conv-mic-btn.recording:not(.live) { background: var(--red); border-color: var(--red); animation: convPulse 1s infinite; } .conv-mic-btn.processing { background: var(--yellow); border-color: var(--yellow); cursor: pointer; } .conv-mic-btn.processing.live { background: var(--accent); border-color: var(--accent); } @keyframes convPulse { 0%,100% { box-shadow: 0 0 0 0 rgba(220,38,38,.4); } 50% { box-shadow: 0 0 0 8px rgba(220,38,38,0); } } .conv-mic-timer { font-size: 13px; font-variant-numeric: tabular-nums; color: var(--red); min-width: 36px; text-align: right; } .conv-vad-label { display: flex; align-items: center; gap: 4px; font-size: 11px; color: var(--subtext); cursor: pointer; white-space: nowrap; user-select: none; } .conv-vad-label input { cursor: pointer; accent-color: var(--accent); } /* Noise gate row — grouped with the mic controls, not a full-width row */ .conv-gate-row { display: flex; align-items: center; gap: 6px; flex-shrink: 0; width: 96px; } .conv-gate-label { font-size: 13px; color: var(--subtext); flex-shrink: 0; line-height: 1; } .conv-gate-slider { flex: 1; min-width: 0; height: 3px; cursor: pointer; accent-color: var(--accent); } .conv-level-wrap { position: relative; flex: 1; min-width: 0; height: 6px; background: var(--panel); border-radius: 3px; margin: 0; overflow: visible; display: none; } .conv-level-wrap.active { display: block; } .conv-level-fill { height: 100%; background: var(--red); border-radius: 3px; width: 0%; transition: width .07s linear; } .conv-level-gate { position: absolute; top: -2px; bottom: -2px; width: 2px; background: var(--accent); border-radius: 1px; pointer-events: none; } .conv-text-inp.listening { border-color: var(--red); font-style: italic; color: var(--subtext); } /* Stats panel collapsible */ .conv-stats-toggle-row { display: flex; align-items: center; justify-content: space-between; } .conv-stats-collapse-btn { background: none; border: none; cursor: pointer; color: var(--subtext); font-size: 16px; line-height: 1; padding: 2px; border-radius: 4px; } .conv-stats-collapse-btn:hover { color: var(--text); background: var(--panel); } .conv-stats-panel.collapsed { width: 0 !important; padding: 0; overflow: hidden; border: none; min-width: 0; } .conv-stats-expand-btn { align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; background: var(--surface); border: 1px solid var(--border); cursor: pointer; color: var(--subtext); font-size: 16px; flex-shrink: 0; align-self: flex-start; margin-top: 4px; } .conv-stats-expand-btn:hover { color: var(--text); background: var(--panel); } /* Stats panel */ .conv-stats-head { font-size: 10px; font-weight: 800; color: var(--subtext); text-transform: uppercase; letter-spacing: .08em; } .conv-pipeline { display: flex; flex-direction: column; gap: 8px; } .conv-pipe-step { display: grid; grid-template-columns: 1fr auto; grid-template-rows: auto auto; gap: 2px 8px; } .conv-pipe-label { grid-column: 1; font-size: 12px; color: var(--subtext); } .conv-pipe-val { grid-column: 2; grid-row: 1 / 3; font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--text); align-self: center; text-align: right; min-width: 52px; } .conv-pipe-bar { grid-column: 1; height: 4px; background: var(--panel); border-radius: 2px; overflow: hidden; } .conv-pipe-fill { height: 100%; background: var(--teal); border-radius: 2px; width: 0%; transition: width .4s ease; } .conv-pipe-total .conv-pipe-label { font-weight: 700; color: var(--text); } .conv-pipe-total .conv-pipe-val { color: var(--accent); font-size: 15px; } /* Turn history */ .conv-turn-history { display: flex; flex-direction: column; gap: 5px; overflow-y: auto; max-height: 220px; } .conv-history-empty { font-size: 12px; color: var(--subtext); font-style: italic; } .conv-hist-item { display: flex; align-items: center; gap: 6px; font-size: 12px; padding: 4px 6px; border-radius: 5px; background: var(--panel); } .conv-hist-num { font-weight: 700; color: var(--subtext); min-width: 20px; } .conv-hist-time { font-variant-numeric: tabular-nums; font-weight: 700; margin-left: auto; } .conv-hist-ok { color: var(--green); } .conv-hist-err { color: var(--red); } /* ── Character library section ─────────────────────────────────────────── */ .cl-toolbar { margin-bottom: 14px; } .cl-tool-field { display: flex; flex-direction: column; gap: 4px; } .cl-tool-label { font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--subtext); } .cl-tool-field select, .cl-tool-field input { padding: 7px 10px; border: 1px solid var(--border); border-radius: 7px; background: var(--surface); color: var(--text); font-size: 13px; } .cl-grid { display: flex; flex-direction: column; gap: 20px; } .cl-book-group { } .cl-book-head { display: flex; align-items: center; gap: 8px; font-weight: 800; font-size: 14px; padding: 4px 0 10px; border-bottom: 2px solid var(--border); margin-bottom: 12px; } .cl-book-head .mdi { color: var(--accent); } .cl-book-count { margin-left: 6px; font-size: 11px; font-weight: 700; color: var(--subtext); border: 1px solid var(--border); border-radius: 20px; padding: 1px 8px; } .cl-book-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(430px, 1fr)); gap: 18px; align-items: start; } .cl-card-wrap { position: relative; min-width: 0; } .cl-card-tools { position: absolute; top: 8px; right: 8px; display: flex; gap: 4px; z-index: 2; opacity: 0; transition: opacity .15s; } .cl-card-wrap:hover .cl-card-tools { opacity: 1; } .cl-card-tools .btn-sm { font-size: 11px; padding: 2px 7px; } .cl-empty { text-align: center; padding: 60px 20px; color: var(--subtext); } .cl-empty p { margin: 8px 0 0; } .cl-empty-hint { font-size: 12.5px; } .cl-edit-box { max-width: 620px; width: 92vw; } .cl-edit-body { display: flex; flex-direction: column; gap: 10px; max-height: 70vh; overflow-y: auto; padding: 4px 2px; } .cl-edit-row { display: flex; flex-direction: column; gap: 3px; } .cl-edit-row > span { font-size: 11px; font-weight: 700; color: var(--subtext); } .cl-edit-row input, .cl-edit-row textarea, .cl-edit-row select { padding: 7px 9px; border: 1px solid var(--border); border-radius: 7px; background: var(--surface); color: var(--text); font-size: 13px; font-family: inherit; resize: vertical; } /* ── Recast unknown: gap between non-contiguous passages ───────────────── */ .ab-cv-divider { text-align: center; color: var(--subtext); font-size: 20px; letter-spacing: .35em; line-height: 1; padding: 10px 0 6px; opacity: .55; user-select: none; } .ab-cv-divider-expand { cursor: pointer; font-size: 14px; letter-spacing: normal; transition: opacity .15s; } .ab-cv-divider-expand:hover { opacity: .9; color: var(--accent); } .ab-cv-divider-hint { font-size: 11px; font-weight: 500; text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 2px; vertical-align: middle; } .ab-cv-ctx-row { opacity: .7; background: color-mix(in srgb, var(--panel) 60%, transparent); border-left: 2px solid var(--border); } .reader-synth-prog { display: inline-flex; align-items: center; gap: 8px; margin-left: auto; } .reader-synth-track { width: 130px; height: 38px; border-radius: 4px; background: var(--panel); overflow: hidden; } .reader-synth-fill { height: 100%; width: 0; background: rgba(234,179,8,.9); transition: width .15s; } .reader-synth-label { font-size: 12px; color: var(--subtext); min-width: 52px; } /* Select mode: clickable sentences (pointer, not crosshair — it's click-to-pick) */ .reader-doc.reader-selecting { box-shadow: inset 0 0 0 2px var(--accent); } .reader-doc.reader-selecting .reader-stat { pointer-events: auto; cursor: pointer; } .reader-doc.reader-selecting .reader-stat:hover { background: rgba(37,99,235,.16); outline: 1px dashed rgba(37,99,235,.5); } .reader-doc.reader-selecting .reader-word { cursor: pointer; } .reader-doc.reader-selecting .reader-word:hover { background: rgba(37,99,235,.18); } /* committed range */ .reader-stat.sel { outline: 2px solid var(--accent); outline-offset: 0; background: rgba(37,99,235,.10); } .reader-word.in-sel { box-shadow: inset 0 -2px 0 var(--accent); } /* the start "anchor" pulses while waiting for the end click */ .reader-stat.sel-anchor { outline: 2px solid var(--accent); background: rgba(37,99,235,.20); animation: readerAnchorPulse 1s ease-in-out infinite; } .reader-word.sel-anchor { box-shadow: inset 0 -2px 0 var(--accent); background: rgba(37,99,235,.22); animation: readerAnchorPulse 1s ease-in-out infinite; } @keyframes readerAnchorPulse { 0%,100% { opacity: 1; } 50% { opacity: .45; } } /* Zoom toolbar + status legend */ .reader-toolbar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; margin-top: 10px; padding: 8px 12px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); } .reader-zoom { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; } .reader-zoom-sep { width: 1px; height: 20px; background: var(--border); margin: 0 4px; } .reader-zoom-pct { font-size: 12px; font-weight: 700; min-width: 44px; text-align: center; color: var(--subtext); } /* PDF Search */ .reader-search-wrap { display: flex; align-items: center; gap: 5px; } .reader-search-icon { font-size: 14px; color: var(--subtext); } .reader-search-inp { width: 160px; padding: 4px 10px; border: 1px solid var(--border); border-radius: 20px; background: var(--panel); color: var(--text); font-size: 13px; outline: none; transition: border-color .15s, width .2s; } .reader-search-inp:focus { border-color: var(--accent); width: 220px; } .reader-search-status { font-size: 11px; color: var(--subtext); white-space: nowrap; min-width: 32px; } .reader-search-btn { width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 5px; background: transparent; color: var(--subtext); cursor: pointer; padding: 0; } .reader-search-btn:hover { background: var(--panel); color: var(--accent); } .reader-search-btn .mdi { font-size: 17px; } .reader-legend { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; font-size: 11px; color: var(--subtext); } .reader-leg { display: inline-flex; align-items: center; gap: 5px; } .reader-leg::before { content: ''; width: 11px; height: 11px; border-radius: 3px; } .reader-leg-pending::before { background: rgba(239,68,68,.45); } .reader-leg-synth::before { background: rgba(234,179,8,.65); } .reader-leg-ready::before { background: rgba(34,197,94,.55); } .reader-leg-reading::before { background: rgba(37,99,235,.6); } .reader-doc { margin-top: 14px; max-height: 72vh; overflow-y: auto; overflow-x: auto; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; display: flex; flex-direction: column; gap: 16px; } .reader-doc:empty { display: none; } .reader-parsing { display: flex; align-items: center; gap: 10px; padding: 24px; color: var(--subtext); font-size: 13px; margin: 0 auto; } .reader-doc.two-page { flex-direction: row; flex-wrap: wrap; justify-content: center; align-items: flex-start; } .reader-page { position: relative; flex: 0 0 auto; margin: 0 auto; box-shadow: 0 2px 10px rgba(0,0,0,.12); background: #fff; contain: layout paint style; content-visibility: auto; contain-intrinsic-size: auto 1100px; } .reader-canvas { display: block; } .reader-overlay { position: absolute; inset: 0; pointer-events: none; } .reader-hl-word { position: absolute; border-radius: 3px; background: rgba(37,99,235,.30); z-index: 3; box-shadow: 0 0 0 1.5px rgba(37,99,235,.75); transition: left .12s, top .12s, width .12s, height .12s; } .reader-search-hit { position: absolute; z-index: 2; border-radius: 2px; background: rgba(250, 204, 21, .35); box-shadow: 0 0 0 1px rgba(202, 138, 4, .25); pointer-events: none; } .reader-search-hit.search-current { z-index: 4; background: rgba(250, 204, 21, .72); box-shadow: 0 0 0 2px rgba(202, 138, 4, .85); } .reader-word.search-hit { background: rgba(250, 204, 21, .35); border-radius: 3px; } .reader-word.search-current { background: rgba(250, 204, 21, .75); box-shadow: 0 0 0 2px rgba(202, 138, 4, .8); } /* Per-sentence synthesis-state overlay (PDF) — the "not synthesised" tint stays invisible until synthesis actually starts (button, play, or a restored doc with saved audio); otherwise a freshly-imported book reads as wall-to-wall red. */ .reader-stat { position: absolute; border-radius: 3px; z-index: 1; transition: background .2s; } .reader-stat-pending { background: transparent; } .reader-doc.reader-synth-started .reader-stat-pending { background: rgba(239,68,68,.12); } .reader-stat-synth { background: rgba(234,179,8,.30); } .reader-stat-ready { background: rgba(34,197,94,.18); } .reader-stat-reading { background: rgba(37,99,235,.20); z-index: 2; } .reader-text { max-width: 760px; background: var(--surface); color: var(--text); border: 1px solid var(--border); border-radius: var(--radius); padding: 28px 34px; line-height: 1.85; font-size: 17px; } .reader-text-para { margin: 0 0 1em; } .reader-word { border-radius: 3px; padding: 0 1px; cursor: pointer; transition: background .12s; } /* Per-sentence synthesis-state tint (text) */ .reader-word.stat-pending { background: transparent; } .reader-doc.reader-synth-started .reader-word.stat-pending { background: rgba(239,68,68,.09); } .reader-word.stat-synth { background: rgba(234,179,8,.22); } .reader-word.stat-ready { background: rgba(34,197,94,.14); } .reader-word.stat-reading { background: rgba(37,99,235,.15); } .reader-word.is-word { background: rgba(37,99,235,.42) !important; box-shadow: 0 0 0 1px rgba(37,99,235,.6); } /* ── Fish Audio inline emotion-tag reference (Try It Out) ─────────────────── */ /* Uses the app's real theme tokens (--surface/--panel/--border/--text), not hardcoded colours, so the panel follows both the light and dark themes. */ .fish-tags-panel { border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; background: var(--panel); } .fish-tags-head { display: flex; align-items: center; gap: 8px; font-weight: 600; color: var(--text); } .fish-tags-group { margin-top: 10px; } .fish-tags-group-title { font-size: 12px; font-weight: 600; color: var(--subtext); margin-bottom: 6px; } .fish-tags-grid { display: flex; flex-wrap: wrap; gap: 6px; } .fish-tag { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; padding: 3px 8px; border-radius: 5px; border: 1px solid var(--border); background: var(--surface); color: var(--text); cursor: pointer; transition: background .12s, border-color .12s, color .12s; } .fish-tag:hover { background: var(--accent); border-color: var(--accent); color: var(--on-accent); } .fish-tag:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; } /* Quotation-mark visibility toggle in the casting view */ .ab-cv-quote-toggle.is-on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); } /* Quotation marks are wrapped in .ab-q so the toggle is a pure CSS flip */ body.ab-hide-quotes .ab-q { display: none; } .lcd-book-context { display: flex; align-items: center; gap: 12px; margin: 0 0 12px; padding: 10px 14px; border: 1px solid #d9e2f0; border-left: 4px solid #2563eb; border-radius: 8px; background: #f7faff; color: #334155; font-size: 12px; } .lcd-book-context-text { flex: 1; line-height: 1.45; } .ab-book-context-banner { display: flex; align-items: center; gap: 12px; padding: 10px 14px; border-bottom: 1px solid var(--border); background: color-mix(in srgb, var(--primary) 7%, var(--surface)); } .ab-book-context-copy { display: flex; align-items: baseline; gap: 8px; flex: 1; flex-wrap: wrap; font-size: 12px; } .ab-book-context-copy > span:last-child { color: var(--subtext); line-height: 1.4; } @media (max-width: 700px) { .ab-book-context-banner { align-items: flex-start; flex-direction: column; } } /* ── Consistent-image generation status panel ──────────────────────────────── Live progress for the "Consistent profile + reference" render, which can legitimately run 10-20+ minutes on a local GPU. A non-blocking corner panel (not a modal) so the user can keep working while it shows elapsed time and flags when the backend has gone quiet for too long. */ .cij-progress-panel { position: fixed; right: 20px; bottom: 20px; z-index: 10000; background: var(--surface); color: var(--text); border: 1px solid var(--border); border-radius: 8px; padding: 10px 14px; min-width: 260px; max-width: 340px; box-shadow: 0 6px 24px rgba(0,0,0,0.35); font-size: 0.85em; } .cij-progress-head { display: flex; align-items: center; gap: 6px; font-weight: 700; margin-bottom: 8px; } .cij-progress-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .cij-progress-close { background: none; border: none; cursor: pointer; color: inherit; opacity: 0.7; padding: 2px; } .cij-progress-close:hover { opacity: 1; } .cij-progress-steps { display: flex; flex-direction: column; gap: 4px; margin-bottom: 8px; } .cij-step { display: flex; align-items: center; gap: 6px; opacity: 0.5; } .cij-step.is-active, .cij-step.is-done { opacity: 1; } .cij-step-icon { font-size: 1.1em; } .cij-step.is-done .cij-step-icon { color: #4caf50; } .cij-step.is-active .cij-step-icon { color: var(--primary, #4a90d9); } .cij-progress-detail { opacity: 0.85; margin-bottom: 4px; } .cij-progress-elapsed { font-variant-numeric: tabular-nums; opacity: 0.7; } .cij-progress-panel.cij-progress-warn .cij-progress-elapsed { color: #e0a030; opacity: 1; } .cij-progress-panel.cij-progress-ok { border-color: #4caf50; } .cij-progress-panel.cij-progress-err { border-color: #e05a4e; } /* ── Attribution confidence: colour-coded certainty + review navigation ────── Green >=80 is measured at ~95% correct, orange 50-79 at ~77%, red <50 at ~54%. Reviewing only orange+red covers ~90% of real errors while reading just over half the book, which is the entire point of showing this. */ .ab-cv-conf { flex: 0 0 auto; align-self: flex-start; min-width: 92px; margin-right: 6px; padding: 1px 5px; border-radius: 999px; font-size: 10px; font-weight: 700; font-variant-numeric: tabular-nums; text-align: center; line-height: 1.5; cursor: help; border: 1px solid transparent; } .ab-conf-green { background: rgba(34,160,90,.14); color: #1f8a4c; border-color: rgba(34,160,90,.35); } .ab-conf-orange { background: rgba(217,138,10,.16); color: #b3760a; border-color: rgba(217,138,10,.4); } .ab-conf-red { background: rgba(206,60,55,.14); color: #c0392b; border-color: rgba(206,60,55,.38); } @media (prefers-color-scheme: dark) { .ab-conf-green { color: #56d98b; } .ab-conf-orange { color: #f0b44a; } .ab-conf-red { color: #ff8178; } } .ab-review-ctl { display: flex; align-items: center; gap: 4px; margin-left: 6px; } .ab-review-ctl select { font-size: 11px; padding: 2px 4px; border-radius: 4px; border: 1px solid var(--border); background: var(--surface); color: var(--text); outline: none; } /* The jump button carries a count, so it is wider than the icon-only nav buttons it sits beside and needs its own padding rather than the square .ab-page-nav-btn box. */ .ab-review-ctl .ab-review-jump { /* A read-out, not a control — the chevrons either side do the navigating. */ cursor: default; user-select: none; padding: 0 6px; gap: 4px; display: inline-flex; align-items: center; white-space: nowrap; font-size: 11px; font-weight: 600; color: #b3760a; } @media (prefers-color-scheme: dark) { .ab-review-ctl .ab-review-jump { color: #f0b44a; } } /* The row the reviewer was just sent to — must stand out from the character highlight, which uses a left border, so this uses a full-row wash. */ .ab-cv-row.ab-review-focus { background: rgba(217,138,10,.13); outline: 2px solid rgba(217,138,10,.5); outline-offset: -2px; border-radius: 4px; } /* Manually-checked marker: a human verdict outranks the confidence score, so a checked line reads as settled and drops out of the review queue. */ .ab-conf-checked { background: rgba(34,160,90,.16); color: #1f8a4c; border-color: rgba(34,160,90,.4); } @media (prefers-color-scheme: dark) { .ab-conf-checked { color: #56d98b; } } .ab-cv-check { flex: 0 0 auto; align-self: flex-start; margin-right: 6px; padding: 0 2px; border: none; background: none; cursor: pointer; color: var(--subtext); opacity: .45; line-height: 1.4; font-size: 14px; } .ab-cv-check:hover { opacity: 1; color: #1f8a4c; } .ab-cv-check.is-checked { opacity: 1; color: #1f8a4c; } @media (prefers-color-scheme: dark) { .ab-cv-check:hover, .ab-cv-check.is-checked { color: #56d98b; } } /* Dim a settled row so the eye skips it while scrolling. */ .ab-cv-row[data-mark="ok"] { opacity: .62; } .ab-cv-row[data-mark="wrong"] { opacity: 1; } .ab-cv-row[data-mark="wrong"] .ab-cv-txt { text-decoration: underline wavy rgba(206,60,55,.55); text-underline-offset: 3px; } .ab-conf-wrong { background: rgba(206,60,55,.16); color: #c0392b; border-color: rgba(206,60,55,.45); } @media (prefers-color-scheme: dark) { .ab-conf-wrong { color: #ff8178; } } /* Language code combobox: free text with a searchable datalist, so any code can be entered (the old it replaced so the surrounding row still lines up. */ input[list="lang-codes"] { width: 88px; text-transform: uppercase; font-variant-numeric: tabular-nums; } /* ── Searchable language dropdown with flags ───────────────────────────────── The underlying is slow to scan and slow to reach by scrolling; this filters by typing and shows an icon per type instead. */ .mag-type-picker { position: relative; display: flex; align-items: center; gap: 6px; flex: 1; min-width: 0; } .mag-type-picker label { flex-shrink: 0; } .mag-type-picker-input { flex: 1; min-width: 0; font: inherit; font-size: 12px; padding: 4px 6px; border: 1px solid var(--border); border-radius: 4px; background: var(--surface); color: var(--text); } .mag-type-picker-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px rgba(37,99,235,.18); } .mag-type-picker-list { position: absolute; top: 100%; left: 0; right: 0; z-index: 5; margin: 2px 0 0; padding: 4px; list-style: none; max-height: 240px; overflow-y: auto; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: 0 6px 18px rgba(0,0,0,.25); } .mag-type-picker-option { display: flex; align-items: center; gap: 8px; padding: 6px 8px; font-size: 13px; border-radius: 5px; cursor: pointer; color: var(--text); } .mag-type-picker-option .mdi { color: var(--subtext); font-size: 16px; flex-shrink: 0; } .mag-type-picker-option:hover { background: var(--panel); } .mag-type-picker-option.is-selected { background: var(--accent); color: var(--on-accent); } .mag-type-picker-option.is-selected .mdi { color: inherit; } .mag-type-picker-empty { padding: 6px 8px; font-size: 13px; } /* Editing an already-detected region: move by dragging the box body, resize from its corner handle, delete with its ×. Handles and the delete button only render usably in edit mode -- kept in the DOM at all times (rather than added/removed) so toggling edit mode doesn't cost a re-render. */ .mag-review-page-wrap.is-editing { cursor: default; } .mag-review-page-wrap.is-editing .mag-review-box { cursor: move; } .mag-review-box-delete { display: none; position: absolute; top: -27px; right: -9px; width: 18px; height: 18px; border-radius: 50%; border: 1px solid var(--border); background: var(--red); color: #fff; font-size: 11px; line-height: 1; align-items: center; justify-content: center; padding: 0; cursor: pointer; z-index: 3; } .mag-review-page-wrap.is-editing .mag-review-box-delete { display: flex; } /* One handle per edge/corner -- lets a single boundary shared by two stacked or side-by-side regions be dragged on its own (e.g. moving where one paragraph block ends and the next begins) instead of only being able to resize from the bottom-right corner. */ .mag-review-box-resize { display: none; position: absolute; width: 10px; height: 10px; border-radius: 3px; border: 1px solid var(--accent); background: var(--panel); z-index: 3; } .mag-review-page-wrap.is-editing .mag-review-box-resize { display: block; } .mag-review-box-resize.rz-nw { top: -5px; left: -5px; cursor: nwse-resize; } .mag-review-box-resize.rz-n { top: -5px; left: 50%; margin-left: -5px; cursor: ns-resize; } .mag-review-box-resize.rz-ne { top: -5px; right: -5px; cursor: nesw-resize; } .mag-review-box-resize.rz-e { top: 50%; right: -5px; margin-top: -5px; cursor: ew-resize; } .mag-review-box-resize.rz-se { bottom: -5px; right: -5px; cursor: nwse-resize; } .mag-review-box-resize.rz-s { bottom: -5px; left: 50%; margin-left: -5px; cursor: ns-resize; } .mag-review-box-resize.rz-sw { bottom: -5px; left: -5px; cursor: nesw-resize; } .mag-review-box-resize.rz-w { top: 50%; left: -5px; margin-top: -5px; cursor: ew-resize; } #mag-edit-toggle.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); } .mag-block { display: grid; grid-template-columns: auto auto max-content 1fr; gap: 10px; align-items: start; padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); margin-bottom: 4px; cursor: pointer; } .mag-blocks-head { display: grid; grid-template-columns: auto auto max-content 1fr; gap: 10px; align-items: center; position: sticky; top: 0; z-index: 3; padding: 8px 10px; margin-bottom: 6px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--panel); color: var(--subtext); font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; } .mag-blocks-check-all { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; color: var(--text); text-transform: none; letter-spacing: 0; white-space: nowrap; } .mag-blocks-check-all input { width: 17px; height: 17px; accent-color: var(--accent); } .mag-block.is-aux { opacity: .68; } .mag-block:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(37,99,235,.14); } .mag-block-num { font-size: 11px; font-weight: 700; color: var(--accent); min-width: 1.4em; } .mag-block-type { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--subtext); display: flex; align-items: center; gap: 3px; } .mag-block-type .mdi { font-size: 13px; } .mag-region-type-picker { flex: 0 1 10.5em; width: 10.5em; text-transform: none; letter-spacing: 0; } .mag-region-type-picker .mag-type-picker-input { width: 100%; min-height: 28px; font-size: 11px; padding: 2px 5px; } .mag-region-type-picker .mag-type-picker-list { right: auto; min-width: 210px; z-index: 8; text-transform: none; letter-spacing: 0; } .mag-block-type-select { display: none; } .mag-block-delete { padding: 2px 6px; min-width: 28px; min-height: 28px; color: var(--subtext); } .mag-block-delete:hover, .mag-block-delete:focus-visible { color: var(--danger, #dc2626); } .mag-block-owner { display: inline-block; padding: 1px 6px; margin-right: 6px; border-radius: 999px; background: var(--mauve); color: #fff; font-size: 10px; font-weight: 700; } .mag-block-image { cursor: default; grid-template-columns: auto auto max-content 1fr; } .mag-block-text { font-size: 14px; line-height: 1.45; } .mag-block-text.is-editable { cursor: text; border-radius: 4px; } .mag-block-text.is-editable:hover { background: var(--panel); box-shadow: 0 0 0 4px var(--panel); } /* A 5th grid child in the 4-column .mag-block-image row -- span the whole row below the thumbnail rather than being squeezed into column 1 alone. */ .mag-describe { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 4px; padding-top: 8px; border-top: 1px dashed var(--border); } .mag-describe-label { flex-basis: 100%; font-size: 11px; color: var(--subtext); } .mag-describe-text { flex: 1 1 100%; min-width: 0; min-height: 44px; resize: vertical; background: var(--panel); border: 1px solid var(--border); color: var(--text); border-radius: var(--radius); padding: 6px 8px; font-size: 13px; font-family: inherit; } .mag-review-foot { display: flex; gap: 8px; justify-content: flex-end; margin-top: 16px; position: sticky; bottom: 0; padding: 12px 0; background: var(--bg); } @media (max-width: 980px) { .mag-review-split { grid-template-columns: 1fr; } .mag-review-page { position: static; } } /* ── Two-page spread: page images on top, each page's OCR results directly underneath it -- a 2x2 grid read top-to-bottom-then-across, matching how an open magazine spread is laid out. Only one of the two pages is draggable/drawable at a time (see magReviewSetActivePage); the other is shown for context with working checkboxes. ───────────────────────────── */ .mag-review-spread { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 18px; align-items: start; } .mag-review-spread-page { min-width: 0; } .mag-review-spread-pagehead { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; } .mag-review-spread-page.is-active-page .mag-review-page-wrap { border-color: var(--accent); } .mag-review-page-wrap.is-context-page { opacity: .94; } .mag-review-spread-list { min-width: 0; } @media (max-width: 980px) { .mag-review-spread { grid-template-columns: 1fr; } } .mag-controls { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-bottom: 14px; padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); } .mag-voice-pick { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--subtext); } .mag-pane-tabs { margin-left: auto; display: flex; gap: 4px; } .mag-pane-tabs .active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); } .mag-article { max-width: 68ch; margin: 0 auto; font-size: 17px; line-height: 1.7; color: var(--text); } .mag-article h2 { font-size: 26px; line-height: 1.25; margin: 0 0 8px; } .mag-article .mag-deck { font-size: 19px; color: var(--subtext); margin: 0 0 12px; } .mag-article .mag-byline { font-size: 14px; color: var(--subtext); margin: 0 0 20px; } .mag-article .mag-caption { font-size: 14px; color: var(--subtext); font-style: italic; } /* A synthesised aside, not part of the article's own written text -- set off visually the same way a caption is, so a reader following along silently (word-highlighting off) can tell it apart from the real prose. */ .mag-article .mag-figure-description { font-size: 15px; color: var(--subtext); font-style: italic; padding-left: 10px; border-left: 2px solid var(--border); margin: 4px 0 14px; } .mag-article .mag-figure-description::before { content: '\1F5BC '; font-style: normal; } .mag-article [data-i] { cursor: pointer; border-radius: 3px; } .mag-article [data-i]:hover { background: rgba(37,99,235,.07); } .mag-article [data-i]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } /* The reading highlight has to survive both themes and colour-blindness, so it carries a left rule as well as a tint rather than relying on hue alone. */ .mag-article [data-i].is-current { background: rgba(37,99,235,.16); box-shadow: -3px 0 0 0 var(--accent); } /* Word-level highlighting is an ESTIMATE (see estimateWordTimings in magazine.js) -- a distinct, lighter treatment from the sentence highlight above keeps that difference legible rather than implying frame-accurate timing. */ .mag-w.is-current { background: var(--accent); color: var(--on-accent); border-radius: 2px; } .mag-karaoke-toggle { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--subtext); } .mag-read-image { max-width: 68ch; margin: 0 auto 18px; } .mag-read-image img { width: 100%; height: auto; max-height: 360px; object-fit: contain; border-radius: var(--radius); border: 1px solid var(--border); background: var(--panel); display: block; } .mag-page-wrap { position: relative; max-width: 900px; margin: 0 auto; } .mag-page-wrap img { width: 100%; height: auto; display: block; border: 1px solid var(--border); border-radius: var(--radius); } .mag-page-box { position: absolute; border: 2px solid var(--accent); background: rgba(37,99,235,.12); border-radius: 2px; pointer-events: none; transition: all .25s ease; } .mag-podcast { margin-top: 24px; } .mag-podcast summary { cursor: pointer; font-weight: 600; } .mag-hosts { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; margin: 12px 0; } .mag-hosts label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--subtext); } .mag-podcast-actions { display: flex; gap: 8px; margin-bottom: 12px; } .mag-turns { padding-left: 20px; line-height: 1.6; } .mag-turns li { margin-bottom: 8px; border-radius: 3px; padding: 2px 4px; } .mag-turns li.is-current { background: rgba(37,99,235,.16); box-shadow: -3px 0 0 0 var(--accent); } /* ── Driving mode ────────────────────────────────────────────────────────── Full-screen, minimal-interaction overlay: two oversized controls, large type, nothing else competing for attention. Reuses whatever is already playing/queued -- it is a view, not a separate playback path. */ .mag-driving { position: fixed; inset: 0; z-index: 500; background: var(--bg); display: flex; align-items: center; justify-content: center; } .mag-driving-exit { position: absolute; top: 20px; right: 20px; width: 52px; height: 52px; border-radius: 50%; border: 1px solid var(--border); background: var(--surface); color: var(--text); font-size: 22px; display: flex; align-items: center; justify-content: center; cursor: pointer; } .mag-driving-exit:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; } .mag-driving-body { text-align: center; max-width: 90vw; } .mag-driving-body h2 { font-size: clamp(28px, 5vw, 48px); margin: 0 0 8px; } .mag-driving-queue { margin-bottom: 20px; font-size: 18px; } .mag-driving-controls { display: flex; align-items: center; justify-content: center; gap: 32px; margin-top: 32px; } .mag-driving-btn { width: 96px; height: 96px; border-radius: 50%; border: none; background: var(--panel); color: var(--text); font-size: 36px; cursor: pointer; display: flex; align-items: center; justify-content: center; } .mag-driving-btn.mag-driving-play { width: 140px; height: 140px; font-size: 56px; background: var(--accent); color: var(--on-accent); } .mag-driving-time { margin-top: 26px; font-size: 22px; font-variant-numeric: tabular-nums; } .mag-driving-progress { width: min(680px, 80vw); height: 10px; margin: 12px auto 0; overflow: hidden; border-radius: 999px; background: var(--border); } .mag-driving-progress span { display: block; width: 0; height: 100%; background: var(--accent); transition: width .2s linear; } .mag-driving-btn:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; } .mag-driving-btn:active { transform: scale(0.96); } @media (prefers-reduced-motion: reduce) { .mag-page-box { transition: none; } .mag-driving-btn:active { transform: none; } } @media (max-width: 860px) { .mag-new-grid, .mag-review-head { grid-template-columns: 1fr; } .mag-toc-row { grid-template-columns: 1fr; } .mag-toc-actions { justify-content: flex-start; } .mag-block { grid-template-columns: auto auto 1fr; } /* The type picker and delete button stay on small screens; the text drops to its own line. */ .mag-block-type { grid-column: 3; } .mag-block-type > .mdi { display: none; } .mag-block-text, .mag-block-thumb-wrap, .mag-row-editor { grid-column: 1 / -1; } /* iOS zooms into any field under 16px, so on a phone the paragraph is 16px too: the editors then match it exactly. */ .mag-block-text { font-size: 16px; } .mag-article { font-size: 18px; } .mag-driving-controls { gap: 20px; } .mag-driving-btn { width: 76px; height: 76px; font-size: 28px; } .mag-driving-btn.mag-driving-play { width: 110px; height: 110px; font-size: 44px; } } .mag-review-box.is-advertisement { border-color: var(--red); background: rgba(220,38,38,.08); } .mag-review-box.is-advertisement .mag-review-box-num { background: var(--red); } /* Appearance forms: manage / rename / delete / per-form image generation */ .lcd-forms-manage { margin: 10px 0 14px; padding: 8px 12px; border: 1px solid var(--border); border-radius: var(--radius, 8px); background: var(--surface); } .lcd-forms-manage > summary { cursor: pointer; font-weight: 600; } .lcd-forms-list { list-style: none; margin: 8px 0; padding: 0; display: grid; gap: 6px; } .lcd-forms-list li { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px; } .lcd-forms-list li.is-active { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(37,99,235,.14); } .lcd-form-name, .lcd-form-new-name { flex: 1 1 180px; min-width: 0; padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px; background: var(--panel); color: var(--text); } .lcd-form-flag { font-size: 12px; color: var(--subtext); white-space: nowrap; } .lcd-form-flag.is-review { color: var(--warning, #d97706); font-weight: 600; } .lcd-form-flag.is-ok { color: var(--green, #16a34a); } .lcd-form-add { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 8px; } .lcd-forms-manage :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } /* Reading order by hand (magazine review) */ .mag-block-order { width: 3.2em; min-height: 28px; padding: 2px 4px; font: inherit; font-size: 12px; text-align: center; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); } .mag-order-bar:empty { display: none; } .mag-order-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 6px; padding: 6px 10px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); font-size: 13px; } .mag-review-page-wrap.is-ordering .mag-review-box { cursor: pointer; } .mag-review-box.is-ordered { outline: 3px solid var(--accent, #2563eb); outline-offset: -1px; } .mag-review-box.is-ordered .mag-review-box-num { background: var(--accent, #2563eb); color: #fff; } /* Quick corrections in the magazine review list */ .mag-list-keys { font-size: 12px; margin: 0 0 6px; } .mag-row-actions { display: inline-flex; gap: 2px; grid-column: -2 / -1; justify-self: end; } .mag-row-action { padding: 2px 6px; min-width: 28px; min-height: 28px; color: var(--subtext); } .mag-row-action:hover, .mag-row-action:focus-visible { color: var(--text); } .mag-row-editor { display: flex; flex-direction: column; gap: 6px; min-width: 0; } /* the text's own column, so the wrapping does not change */ .mag-row-editor textarea { width: 100%; font: inherit; font-size: 13px; padding: 6px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text); resize: vertical; } .mag-row-editor-actions { display: flex; flex-wrap: wrap; gap: 6px; } .mag-blocks [data-block-row]:focus-within { outline: 2px solid var(--accent, #2563eb); outline-offset: 1px; } /* Select several boxes (magazine review) */ .mag-review-page-wrap.is-selecting { cursor: crosshair; } .mag-review-page-wrap.is-selecting .mag-review-box { cursor: pointer; } .mag-review-box.is-selected { outline: 3px solid var(--warning, #d97706); outline-offset: -1px; background: color-mix(in srgb, var(--warning, #d97706) 18%, transparent); } .mag-blocks [data-block-row].is-selected { border-color: var(--warning, #d97706); box-shadow: inset 3px 0 0 var(--warning, #d97706); } /* Live region boxes on the page being read (magazine ingestion preview) */ .mag-ingest-preview-canvas { position: relative; flex: 1 1 auto; min-height: 0; display: flex; } .mag-ingest-preview-canvas { align-items: flex-start; } .mag-ingest-preview-canvas img { width: 100%; height: 100%; object-fit: contain; object-position: center top; } .mag-ingest-preview-boxes { position: absolute; pointer-events: none; } .mag-preview-box { position: absolute; border: 2px solid var(--c, #3b82f6); border-radius: 2px; background: color-mix(in srgb, var(--c, #3b82f6) 14%, transparent); } .mag-preview-box.is-new { animation: mag-preview-in .45s ease-out; } @keyframes mag-preview-in { from { opacity: 0; transform: scale(1.04); } to { opacity: 1; transform: none; } } @media (prefers-reduced-motion: reduce) { .mag-preview-box.is-new { animation: none; } } .mag-preview-box-num { position: absolute; left: -2px; top: -2px; min-width: 16px; padding: 0 3px; font: 600 10px/16px system-ui, sans-serif; color: #fff; background: var(--c, #3b82f6); border-radius: 2px 0 2px 0; text-align: center; } .mag-ingest-preview-legend { display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: 11px; color: #b6bac2; } .mag-preview-legend-item { display: inline-flex; align-items: center; gap: 4px; } .mag-preview-swatch { width: 10px; height: 10px; border-radius: 2px; background: var(--c); } .mag-preview-legend-count { margin-left: auto; color: #8a8f98; } .mag-review-legend { display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: 12px; color: var(--subtext); margin: 6px 0 10px; } .mpt-title { --c: #a855f7; } .mpt-heading { --c: #d8b4fe; } .mpt-deck { --c: #06b6d4; } .mpt-byline { --c: #10b981; } .mpt-body { --c: #3b82f6; } .mpt-quote { --c: #8b5cf6; } .mpt-caption { --c: #14b8a6; } .mpt-pullquote { --c: #ec4899; } .mpt-sidebar { --c: #f59e0b; } .mpt-ad { --c: #ef4444; } .mpt-furniture, .mpt-page_number { --c: #9ca3af; } /* Visual regions: their own colours and a stronger fill, so a picture reads as an area, not a text line. */ .mpt-image, .mpt-figure { --c: #f97316; } .mpt-chart { --c: #84cc16; } .mpt-table { --c: #eab308; } .mpt-code { --c: #94a3b8; } .mag-preview-box.mpt-image, .mag-preview-box.mpt-figure, .mag-preview-box.mpt-chart, .mag-preview-box.mpt-table, .mag-preview-box.mpt-code { border-width: 3px; border-style: dashed; background: color-mix(in srgb, var(--c) 24%, transparent); } .mag-preview-legend-hint { color: #8a8f98; font-style: italic; } .mag-quality-report { margin: 8px 0; padding: 10px 14px; border: 1px solid var(--border, #c9c9c9); border-radius: 8px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; } .mag-quality-report[hidden] { display: none; } .mag-quality-report progress { flex: 1 1 100%; height: 10px; } /* Live quality-run indicator on the page grid */ .mag-card .mag-live-tag { position: absolute; top: 8px; right: 8px; z-index: 2; padding: 2px 8px; border-radius: 999px; font-size: 12px; font-weight: 600; background: var(--surface); border: 1px solid var(--accent); color: var(--accent); } .mag-card.is-page-working { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent); animation: mag-working-pulse 1.2s ease-in-out infinite; } .mag-card.is-page-live-done .mag-live-tag { border-color: var(--green); color: var(--green); } @keyframes mag-working-pulse { 50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent) 15%, transparent); } } @media (prefers-reduced-motion: reduce) { .mag-card.is-page-working { animation: none; } } .mag-card .mag-thumb-flag { position: absolute; top: 8px; left: 8px; z-index: 2; display: inline-flex; gap: 4px; align-items: center; padding: 2px 8px; border-radius: 999px; font-size: 12px; font-weight: 700; cursor: pointer; background: #9333EA; color: #fff; border: 2px solid #fff; } .mag-card .mag-thumb-flag:hover, .mag-card .mag-thumb-flag:focus-visible { background: #7E22CE; outline: 2px solid #9333EA; outline-offset: 2px; } .mag-card.is-page-flagged { border-width: 2px; } .mag-card.is-page-ad { border-color: var(--green); border-style: dashed; border-width: 2px; background: color-mix(in srgb, var(--green) 12%, var(--surface)); } .mag-pages-legend-swatch.is-page-ad { border-color: var(--green); border-style: dashed; background: color-mix(in srgb, var(--green) 20%, var(--surface)); } /* Driving mode: Spotify-style transport, text + image toggles, speed */ .mag-driving { overflow-y: auto; } .mag-driving-body { width: min(760px, 94vw); padding: 24px 0; } .mag-driving-visual { margin: 0 auto 16px; max-height: 32vh; display: flex; flex-direction: column; align-items: center; gap: 6px; } .mag-driving-visual img { max-width: 100%; max-height: 28vh; object-fit: contain; border-radius: 12px; } .mag-driving-visual figcaption { font-size: 14px; color: var(--subtext); } .mag-driving-lyrics { margin: 8px auto 18px; max-width: 640px; } .mag-driving-line { margin: 6px 0; font-size: clamp(16px, 2.4vw, 22px); line-height: 1.4; color: var(--subtext); opacity: .6; } .mag-driving-line.is-current { font-size: clamp(22px, 3.6vw, 34px); font-weight: 700; color: var(--text); opacity: 1; } .mag-driving-seek { margin-top: 12px; } .mag-driving-seek input[type="range"] { width: min(680px, 88vw); height: 28px; accent-color: var(--accent); } .mag-driving-seek .mag-driving-time { margin-top: 2px; } .mag-driving-controls { gap: 20px; margin-top: 18px; } .mag-driving-btn.is-small { width: 64px; height: 64px; font-size: 26px; } .mag-driving-options { display: flex; justify-content: center; gap: 12px; margin-top: 22px; flex-wrap: wrap; } .mag-driving-opt { min-width: 76px; min-height: 52px; padding: 0 18px; border-radius: 26px; font-size: 18px; font-weight: 700; border: 2px solid var(--border); background: var(--surface); color: var(--text); cursor: pointer; } .mag-driving-opt[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); } .mag-driving-opt:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; } @media (max-width: 860px) { .mag-driving-btn.is-small { width: 52px; height: 52px; font-size: 22px; } .mag-driving-controls { gap: 12px; } } /* Driving mode text: three sentences only (previous, current, next) in a fixed-height box, so nothing jumps */ .mag-driving-lyrics { height: clamp(210px, 40vh, 360px); max-height: none; overflow-y: auto; overscroll-behavior: contain; display: flex; flex-direction: column; justify-content: safe center; gap: 12px; text-align: left; padding: 8px 14px; border-radius: 12px; background: color-mix(in srgb, var(--surface) 70%, transparent); scroll-behavior: auto; } .mag-driving-lyrics .mag-driving-line { margin: 0; flex: none; font-size: clamp(15px, 4vw, 20px); line-height: 1.35; opacity: .6; color: var(--text); font-weight: 400; cursor: pointer; display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .mag-driving-lyrics .mag-driving-line[hidden] { display: none; } .mag-driving-lyrics .mag-driving-line:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; } .mag-driving-lyrics .mag-driving-line.is-current { opacity: 1; font-weight: 700; font-size: clamp(21px, 6vw, 32px); cursor: default; display: block; overflow: visible; -webkit-line-clamp: unset; line-clamp: unset; border-left: 4px solid var(--accent); padding-left: 10px; } @media (prefers-reduced-motion: no-preference) { .mag-driving-lyrics .mag-driving-line.is-current { animation: mag-line-in .3s ease-out; } .mag-driving-lyrics .mag-driving-line.is-prev, .mag-driving-lyrics .mag-driving-line.is-next { animation: mag-line-fade .3s ease-out; } } @keyframes mag-line-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } } @keyframes mag-line-fade { from { opacity: 0; } to { opacity: .6; } } .mag-fw.is-current { background: var(--accent); color: var(--on-accent, #fff); border-radius: 4px; padding: 0 3px; } .mag-dw.is-current { background: var(--accent); color: var(--on-accent, #fff); border-radius: 4px; padding: 0 3px; } .mag-driving-lyrics { position: relative; } /* Transport buttons are always perfect circles: fixed square size that scales with the viewport (never flex-shrinks or stretches), so five buttons fit at 320-375 px. */ .mag-driving { align-items: flex-start; } .mag-driving-body { margin: auto; } .mag-driving-controls { flex-wrap: nowrap; align-items: center; max-width: 100%; } .mag-driving-btn, .mag-driving-btn.is-small, .mag-driving-btn.mag-driving-play { flex: 0 0 auto; align-self: center; box-sizing: border-box; padding: 0; min-width: 0; min-height: 0; aspect-ratio: 1 / 1; border-radius: 50%; overflow: hidden; line-height: 1; } @media (max-width: 480px) { .mag-driving-controls { gap: 8px; } .mag-driving-btn { width: clamp(44px, 14vw, 56px); height: clamp(44px, 14vw, 56px); font-size: 24px; } .mag-driving-btn.is-small { width: 44px; height: 44px; font-size: 22px; } .mag-driving-btn.mag-driving-play { width: clamp(64px, 22vw, 84px); height: clamp(64px, 22vw, 84px); font-size: 36px; } } /* On a phone the review toolbar wraps to many rows; sticky, it would cover most of the page image (drag-drawing and box selection then start on a toolbar button), so it scrolls away instead. */ @media (max-width: 860px) { .mag-review-pagenav { position: static; box-shadow: none; } } /* Magazine: spelling marks and the pronunciation/spelling panel */ .mag-spell { background: transparent; color: inherit; text-decoration: underline wavy #d93025; text-decoration-thickness: 1.5px; text-underline-offset: 3px; cursor: help; } .mag-spell-panel, .mag-checks { margin-top: 12px; } .mag-spell-list { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 8px; } .mag-spell-list li { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; } .mag-spell-ctx { flex: 1 1 320px; min-width: 0; overflow-wrap: anywhere; } .mag-acr-table { width: 100%; border-collapse: collapse; margin: 8px 0 12px; } .mag-acr-table th, .mag-acr-table td { text-align: left; padding: 4px 8px; vertical-align: middle; } .mag-acr-table input { width: 100%; min-width: 140px; } .mag-checks h4 { margin: 14px 0 4px; } @media (max-width: 700px) { .mag-acr-table thead { display: none; } .mag-acr-table tr { display: grid; gap: 2px; padding: 6px 0; border-bottom: 1px solid var(--border, #ddd); } } /* Magazine review: what is wrong on this page */ .mag-issues-panel { margin: 10px 0; padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); } .mag-issues-head { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; } .mag-issues-list { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 6px; } .mag-issues-list li { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; } .mag-issues-text { flex: 1 1 320px; min-width: 0; } .mag-issue-swatch { width: 18px; height: 14px; border-radius: 3px; flex: 0 0 auto; border: 3px solid transparent; } .mag-issue-swatch.is-uncovered { border: 2px dashed #dc2626; } .mag-issue-swatch.is-issue-clipped { border-right: 5px solid #f97316; border-top-color: #bbb; border-bottom-color: #bbb; border-left-color: #bbb; } .mag-issue-swatch.is-issue-overlap { border: 2px dashed #9333ea; } .mag-issue-swatch.is-issue-suspect { border: 2px dotted #ca8a04; } .mag-issue-swatch.is-spell { border: 0; background: repeating-linear-gradient(135deg, transparent 0 3px, #d93025 3px 5px); height: 4px; margin-top: 8px; } .mag-issue-box { position: absolute; pointer-events: none; z-index: 3; box-sizing: border-box; } .mag-issue-box.is-uncovered { border: 3px dashed #dc2626; background: rgba(220,38,38,.10); } .mag-issue-box::after, .mag-review-box[data-issue]::after { display: none; content: attr(data-issue); position: absolute; left: -2px; bottom: 100%; max-width: 240px; padding: 1px 5px; font-size: 11px; line-height: 1.3; color: #fff; background: #b91c1c; border-radius: 3px 3px 0 0; white-space: nowrap; pointer-events: none; } .mag-review-box.is-issue-clipped[data-issue]::after { background: #c2410c; } .mag-review-box.is-issue-overlap[data-issue]::after { background: #7e22ce; } .mag-review-box.is-issue-suspect[data-issue]::after { background: #a16207; } .mag-review-box.is-issue-overlap { outline: 3px dashed #9333ea; outline-offset: 1px; } .mag-review-box.is-issue-suspect { outline: 3px dotted #ca8a04; outline-offset: 1px; } .mag-review-box.is-issue-clipped.side-right { box-shadow: 5px 0 0 0 #f97316; } .mag-review-box.is-issue-clipped.side-left { box-shadow: -5px 0 0 0 #f97316; } .mag-review-box.is-issue-clipped.side-top { box-shadow: 0 -5px 0 0 #f97316; } .mag-review-box.is-issue-clipped.side-bottom { box-shadow: 0 5px 0 0 #f97316; } /* Magazine: text flow arrows, chips and reading-view seam markers */ .mag-flow-toggle { margin-left: auto; font-size: 13px; display: inline-flex; align-items: center; gap: 4px; } .mag-flow-svg { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 4; overflow: visible; } .mag-flow-path { fill: none; stroke: #16a34a; stroke-width: 3; stroke-linecap: round; } .mag-flow-label { font-size: 11px; font-weight: 600; fill: #fff; paint-order: stroke; stroke: #15803d; stroke-width: 4px; stroke-linejoin: round; } .mag-flow-chip { position: absolute; right: 2px; z-index: 5; padding: 0 5px; font-size: 11px; line-height: 1.5; border-radius: 3px; color: #fff; background: #15803d; white-space: nowrap; pointer-events: none; } .mag-flow-chip.is-top { top: 2px; } .mag-flow-chip.is-bottom { bottom: 2px; } .mag-flow-chip.is-edge { background: #475569; } .mag-seam { display: inline-block; margin: 0 2px; padding: 0 5px; font-size: .72em; line-height: 1.6; vertical-align: middle; border-radius: 999px; color: #14532d; background: #dcfce7; border: 1px solid #86efac; user-select: none; } .mag-seam.is-page { background: #bbf7d0; font-weight: 600; } @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .mag-seam { color: #dcfce7; background: #14532d; border-color: #166534; } } :root[data-theme="dark"] .mag-seam { color: #dcfce7; background: #14532d; border-color: #166534; } .mag-review-page-wrap.has-room .mag-issue-box::after, .mag-review-page-wrap.has-room .mag-review-box[data-issue]::after, .mag-review-box[data-issue]:hover::after { display: block; } .mag-flow-path.is-paragraph { stroke-dasharray: 7 5; } .mag-link-page { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: 13px; } .mag-report-group { margin-top: 8px; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; } details.mag-report-group { display: block; } details.mag-report-group summary { cursor: pointer; margin-bottom: 6px; } .mag-page-pill { min-width: 2.6em; padding: 2px 8px; } .mag-flow-chip.is-open { pointer-events: auto; cursor: pointer; background: #b45309; border: 0; font: inherit; font-size: 11px; } .mag-flow-chip.is-open:hover, .mag-flow-chip.is-open:focus-visible { background: #92400e; } /* Page thumbnail as the Review target */ .mag-card-thumb-btn { display: block; width: 100%; padding: 0; margin: 0; border: 0; background: none; cursor: pointer; border-radius: 6px; } .mag-card-thumb-btn:hover .mag-card-thumb, .mag-card-thumb-btn:focus-visible .mag-card-thumb { outline: 3px solid var(--accent); outline-offset: 2px; } .mag-card-thumb-btn:focus-visible { outline: none; } .mag-pagenav-break { flex-basis: 100%; height: 0; margin: 0; padding: 0; } .mag-spell-edit { cursor: text; border-radius: 3px; } .mag-spell-edit:hover, .mag-spell-edit:focus-visible { background: rgba(217,48,37,.14); outline: 2px solid rgba(217,48,37,.5); outline-offset: 1px; } .mag-spell-editor { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; flex: 1 1 320px; } .mag-spell-input { flex: 1 1 200px; min-width: 140px; font: inherit; padding: 4px 8px; border: 1px solid var(--accent); border-radius: 4px; background: var(--surface); color: var(--text); } /* Find an issue online (magazine library) */ .mag-find { margin-bottom: 18px; } .mag-find-form { display: flex; flex-wrap: wrap; gap: 10px; align-items: end; } .mag-find-form label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; } .mag-find-query { flex: 1 1 240px; } .mag-find-results { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 8px; } .mag-find-item { display: flex; gap: 12px; align-items: center; justify-content: space-between; padding: 10px 12px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); } .mag-find-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; } .mag-find-summary { color: var(--subtext); font-size: 13px; overflow-wrap: anywhere; } @media (max-width: 600px) { .mag-find-item { flex-direction: column; align-items: stretch; } } /* Review: text edited by hand, and its difference from the recognised text */ .mag-edited { display: inline-flex; align-items: center; gap: 6px; margin-left: 8px; font-size: 12px; color: var(--subtext); } .mag-diff { margin: 6px 0 2px 28px; padding: 8px 10px; border: 1px dashed var(--border); border-radius: var(--radius); background: var(--surface); line-height: 1.6; overflow-wrap: anywhere; } .mag-diff del { text-decoration: line-through; color: var(--red, #b91c1c); } .mag-diff ins { text-decoration: underline; color: var(--green); } .mag-diff del::before { content: "\2212\00a0"; } .mag-diff ins::before { content: "+\00a0"; } /* Reading view: sidebars shown in place but not narrated */ .mag-aside { display: block; margin: 12px 0; padding: 10px 14px; border-left: 4px solid var(--border); background: var(--surface); border-radius: 0 var(--radius) var(--radius) 0; color: var(--subtext); font-size: .92em; } .mag-aside-label { display: block; font-size: 12px; font-weight: 600; margin-bottom: 4px; } .mag-aside-text { display: block; white-space: pre-line; overflow-wrap: anywhere; } /* Order by clicking: a floating panel, so "Save order" is always on screen and never inside the way */ .mag-order-bar.is-floating { position: fixed; right: 16px; bottom: calc(76px + env(safe-area-inset-bottom)); z-index: 60; /* 76px clears the sticky Back/Approve bar */ flex-direction: column; align-items: stretch; gap: 8px; width: min(340px, calc(100vw - 32px)); margin: 0; padding: 14px; border: 2px solid var(--accent, #2563eb); border-radius: 12px; background: var(--surface); box-shadow: 0 10px 30px rgba(0, 0, 0, .28); font-size: 14px; } .mag-order-head { display: flex; align-items: center; gap: 8px; font-size: 15px; } .mag-order-hint { margin: 0; color: var(--subtext); line-height: 1.4; } .mag-order-progress { display: flex; align-items: center; gap: 10px; font-variant-numeric: tabular-nums; } .mag-order-progress progress { flex: 1; height: 10px; accent-color: var(--accent, #2563eb); } .mag-order-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; } .mag-order-save { flex: 1 1 100%; min-height: 48px; font-size: 16px; font-weight: 700; } .mag-order-save.is-ready { background: var(--green, #16a34a); border-color: var(--green, #16a34a); color: #fff; } .mag-order-actions .btn-sm { min-height: 40px; flex: 1 1 auto; } .mag-order-bar.is-floating :focus-visible { outline: 3px solid var(--accent, #2563eb); outline-offset: 2px; } @media (max-width: 700px) { .mag-order-bar.is-floating { left: 8px; right: 8px; bottom: calc(76px + env(safe-area-inset-bottom)); width: auto; padding: 10px; gap: 6px; } .mag-order-hint { font-size: 12px; } .mag-order-save { min-height: 44px; } .mag-order-actions .btn-sm { min-height: 40px; } } @media (prefers-reduced-motion: no-preference) { .mag-order-bar.is-floating { animation: mag-order-in .18s ease-out; } @keyframes mag-order-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } } } /* Page grid: bigger legend, and a status / articles / next-step block on every card */ .mag-pages-help { margin: 0 0 14px; padding: 12px 14px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); } .mag-pages-summary { margin: 0 0 8px; font-size: 15px; } .mag-pages-filter { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; margin-bottom: 10px; cursor: pointer; } .mag-pages-filter input { width: 18px; height: 18px; accent-color: var(--accent); } .mag-pages-help .mag-pages-legend { font-size: 14px; gap: 8px 20px; margin: 0; color: var(--text); } .mag-pages-help .mag-pages-legend-swatch { width: 18px; height: 18px; border-radius: 4px; } .mag-card-count { display: block; font-size: 13px; font-weight: 600; color: var(--text); } .mag-flows { display: flex; flex-direction: column; gap: 2px; } .mag-flow { font-size: 12px; color: var(--subtext); } .mag-page-guide { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border); font-size: 13px; line-height: 1.4; } .mag-page-status { font-weight: 700; display: inline-flex; gap: 6px; align-items: flex-start; } .mag-page-todo { padding: 6px 8px; border-radius: 6px; background: color-mix(in srgb, var(--accent) 12%, var(--surface)); border-left: 4px solid var(--accent); } .mag-page-todo ul { margin: 4px 0 0; padding-left: 18px; } .mag-page-todo li { margin: 2px 0; } .mag-page-done { color: var(--subtext); } .mag-page-note { color: var(--subtext); font-size: 12px; } .mag-card.needs-you { box-shadow: 0 0 0 2px var(--accent); } .mag-cat { margin: 10px 0; } .mag-cat > summary { cursor: pointer; font-weight: 600; } .mag-cat-list { list-style: none; margin: 8px 0; padding: 0; display: grid; gap: 6px; } .mag-cat-list li { display: flex; gap: 10px; align-items: center; justify-content: space-between; padding: 6px 10px; border: 1px solid var(--border); border-radius: var(--radius); overflow-wrap: anywhere; } .mag-cat-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; align-items: end; } .mag-cat-form label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; } /* Editing a region's text in place: same type and wrapping as the displayed paragraph, flagged words still underlined */ .mag-hl-wrap { position: relative; display: block; font-size: 14px; line-height: 1.45; } .mag-hl-back, .mag-hl-area { font: inherit; line-height: inherit; letter-spacing: inherit; white-space: pre-wrap; overflow-wrap: break-word; word-break: normal; padding: 0; margin: 0; border: 0; width: 100%; box-sizing: border-box; } .mag-hl-back { position: absolute; inset: 0; color: transparent; pointer-events: none; overflow: hidden; } .mag-hl-back .mag-spell { color: transparent; } .mag-row-editor textarea.mag-hl-area { position: relative; display: block; font: inherit; font-size: inherit; line-height: inherit; padding: 0; border: 0; width: 100%; background: transparent; color: var(--text); resize: none; overflow: hidden; min-height: 1.45em; border-radius: 4px; outline: none; box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 18%, transparent), 0 0 0 5px var(--accent); } /* One flagged word being corrected where it stands */ .mag-spell.is-editing { text-decoration: none; background: none; outline: none; position: relative; cursor: text; } .mag-word-input { font: inherit; line-height: inherit; color: var(--text); background: var(--surface); padding: 0 2px; margin: 0; min-width: 3ch; max-width: 100%; border: 0; border-radius: 3px; box-shadow: 0 0 0 2px var(--accent); } .mag-word-pop { position: absolute; left: 0; top: 100%; z-index: 40; display: flex; flex-wrap: wrap; gap: 4px; align-items: center; margin-top: 4px; padding: 6px; min-width: 220px; max-width: min(420px, 80vw); border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); box-shadow: 0 8px 24px rgba(0, 0, 0, .25); font-size: 13px; font-style: normal; line-height: 1.3; } .mag-word-hint { flex: 1 1 100%; font-size: 12px; } @media (max-width: 860px) { .mag-hl-wrap { font-size: 16px; } } /* after the desktop rule above, so it wins */ .mag-turn-group { display: inline-flex; gap: 4px; padding: 2px; border: 1px dashed var(--border); border-radius: var(--radius); } .mag-type-picker-list.opens-up { top: auto; bottom: 100%; margin: 0 0 2px; } .mag-type-picker-list { overflow-y: auto; } /* Content that cannot break (an OCR loop, a long URL) must never widen the review or push the page picture out: grid tracks are min-content by default, so every track that holds text is allowed to shrink and text wraps anywhere. */ .mag-review-split > *, .mag-blocks, .mag-block { min-width: 0; } .mag-block-text, .mag-diff, .mag-article, .mag-driving-lyrics, .mag-page-article-title, .mag-toc-title { overflow-wrap: anywhere; } .mag-turn-state { align-self: center; padding: 0 6px; font-size: 12px; font-weight: 600; color: var(--subtext); } .mag-turn-busy { margin: 8px 0; padding: 10px 14px; border-radius: var(--radius); border: 2px solid var(--accent); background: color-mix(in srgb, var(--accent) 12%, var(--surface)); font-size: 14px; line-height: 1.4; } .mag-review-split.is-stale { opacity: .4; pointer-events: none; filter: grayscale(.6); transition: opacity .15s; } @media (prefers-reduced-motion: reduce) { .mag-review-split.is-stale { transition: none; } } /* Legend entries are status filters */ .mag-legend-filter { font: inherit; color: var(--text); background: transparent; border: 1px solid transparent; border-radius: 999px; padding: 4px 10px; cursor: pointer; } .mag-legend-filter:hover:not(:disabled) { border-color: var(--border); background: var(--panel); } .mag-legend-filter:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; } .mag-legend-filter.is-on, .mag-legend-filter.is-on:hover:not(:disabled) { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 16%, var(--surface)); font-weight: 700; } .mag-legend-filter:disabled { opacity: .45; cursor: default; } .mag-legend-count { min-width: 1.6em; padding: 0 6px; border-radius: 999px; background: var(--panel); font-size: 12px; text-align: center; } .mag-pages-shown { font-weight: 600; color: var(--accent); } .mag-legend-clear { margin-left: 6px; } /* Share: the overlay's share button mirrors the exit button; the Play prompt is the big first tap a deep link needs. */ .mag-driving-share { position: absolute; top: 20px; left: 20px; width: 52px; height: 52px; border-radius: 50%; border: 1px solid var(--border); background: var(--surface); color: var(--text); font-size: 22px; display: flex; align-items: center; justify-content: center; cursor: pointer; } .mag-driving-share:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; } .mag-driving-start { display: flex; align-items: center; justify-content: center; gap: 12px; width: 100%; min-height: 72px; margin: 0 0 16px; padding: 12px 20px; border: 0; border-radius: 16px; background: var(--accent); color: var(--on-accent, #fff); font-size: clamp(22px, 6vw, 32px); font-weight: 700; cursor: pointer; } .mag-driving-start[hidden] { display: none; } .mag-driving-start:focus-visible { outline: 3px solid var(--text); outline-offset: 3px; } .mag-share-sheet { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); z-index: 600; box-sizing: border-box; width: min(520px, calc(100vw - 32px)); display: flex; flex-direction: column; gap: 8px; padding: 16px; background: var(--surface); color: var(--text); border: 2px solid var(--accent); border-radius: 12px; box-shadow: 0 12px 40px rgba(0,0,0,.35); } .mag-share-sheet[hidden] { display: none; } .mag-share-sheet input { width: 100%; box-sizing: border-box; } /* ── Driving mode layout: top bar / flexible content / controls dock ─────────────────────────────── The overlay is one fixed full-viewport column (dvh with a vh fallback, safe-area padding). Only the content area flexes and scrolls internally; the dock is a flex:none sibling, so it can never be pushed off screen or covered, from 320x568 up to tablets and landscape. This block supersedes the older driving-mode rules above. */ .mag-driving { display: flex; flex-direction: column; align-items: stretch; justify-content: flex-start; inset: 0; height: 100vh; height: 100dvh; overflow: hidden; box-sizing: border-box; padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); } .mag-driving[hidden], .mag-driving [hidden] { display: none !important; } .mag-driving-top { flex: none; display: flex; align-items: center; gap: 6px; padding: 8px; box-sizing: border-box; min-width: 0; } .mag-driving-top .mag-driving-share, .mag-driving-top .mag-driving-exit { position: static; flex: none; width: 44px; height: 44px; min-width: 44px; min-height: 44px; padding: 0; box-sizing: border-box; border-radius: 50%; border: 1px solid var(--border); background: var(--surface); color: var(--text); font-size: 22px; display: flex; align-items: center; justify-content: center; cursor: pointer; } .mag-driving-zoomctl button:focus-visible, .mag-driving-backcur:focus-visible, .mag-driving-stage:focus-visible, .mag-driving-full:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; } .mag-driving-menuwrap { position: relative; flex: none; } .mag-driving-menubtn, .mag-driving-indexbtn { flex: none; min-width: 44px; height: 44px; padding: 0 12px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; gap: 6px; border: 1px solid var(--border); border-radius: 22px; background: var(--surface); color: var(--text); font-size: 15px; font-weight: 600; cursor: pointer; } .mag-driving-menubtn .mdi, .mag-driving-indexbtn .mdi { font-size: 22px; } .mag-driving-menubtn[aria-expanded="true"], .mag-driving-indexbtn[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); } .mag-driving-topspacer { flex: 1 1 auto; min-width: 0; } .mag-driving-menubtn:focus-visible, .mag-driving-indexbtn:focus-visible, .mag-driving-menu button:focus-visible, .mag-driving-index button:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; } .mag-driving-menu { position: absolute; top: calc(100% + 6px); left: 0; z-index: 3; width: max-content; max-width: calc(100vw - 16px); min-width: 220px; overflow-y: auto; padding: 6px; box-sizing: border-box; border: 1px solid var(--border); border-radius: 14px; background: var(--surface); box-shadow: 0 8px 24px rgba(0,0,0,.35); } .mag-driving-menu button { width: 100%; min-height: 44px; padding: 0 12px; display: flex; align-items: center; gap: 10px; text-align: left; border: 0; border-radius: 10px; background: transparent; color: var(--text); font-size: 16px; font-weight: 600; cursor: pointer; } .mag-driving-menu button .mdi { font-size: 22px; flex: none; } .mag-driving-menu button[aria-checked="true"] { background: var(--accent); color: var(--on-accent, #fff); } .mag-driving-menu hr { border: 0; border-top: 1px solid var(--border); margin: 4px 0; } .mag-driving-index { position: absolute; left: 0; right: 0; z-index: 2; display: flex; flex-direction: column; box-sizing: border-box; background: var(--surface); border-top: 1px solid var(--border); } .mag-driving-index[hidden] { display: none; } .mag-driving-index-head { flex: none; display: flex; align-items: center; justify-content: space-between; padding: 4px 8px 4px 16px; } .mag-driving-index-head h3 { margin: 0; font-size: 17px; } .mag-driving-index-head button { width: 44px; height: 44px; border: 0; border-radius: 50%; background: transparent; color: var(--text); font-size: 22px; cursor: pointer; } .mag-driving-index-list { list-style: none; margin: 0; padding: 0 8px 8px; flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; } .mag-driving-index-list button { width: 100%; min-height: 44px; padding: 6px 10px; display: flex; align-items: center; gap: 10px; text-align: left; border: 0; border-radius: 10px; background: transparent; color: var(--text); font-size: 15px; cursor: pointer; } .mag-driving-index-list button[aria-current="true"] { background: var(--accent); color: var(--on-accent, #fff); font-weight: 700; } .mag-driving-index-list button:disabled { opacity: .5; cursor: default; } .mag-driving-index-list .mdi-num { flex: none; min-width: 2.2em; text-align: right; font-variant-numeric: tabular-nums; opacity: .7; } .mag-driving-index-list .ix-title { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; } .mag-driving-index-list .ix-dur { flex: none; font-variant-numeric: tabular-nums; font-size: 14px; } .mag-driving-view-label { display: none; } @media (min-width: 560px) { .mag-driving-view-label { display: inline; } } .mag-driving-content { flex: 1 1 auto; min-height: 0; width: 100%; max-width: 760px; margin: 0 auto; box-sizing: border-box; padding: 0 12px; display: flex; flex-direction: column; gap: 8px; overflow: hidden; } .mag-driving-head { flex: none; text-align: center; min-width: 0; } .mag-driving-head h2 { margin: 0; font-size: clamp(18px, 5vw, 30px); line-height: 1.2; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .mag-driving-head p { margin: 2px 0 0; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .mag-driving-head .mag-driving-queue { margin: 2px 0 0; font-size: 14px; } .mag-driving-start { flex: none; margin: 0; } .mag-driving-stagewrap { flex: 1 1 0; min-height: 0; position: relative; display: flex; flex-direction: column; } .mag-driving-visual { margin: 0; flex: 1 1 auto; min-height: 0; max-height: none; position: relative; display: flex; flex-direction: column; gap: 4px; align-items: stretch; } .mag-driving-stage { flex: 1 1 0; min-height: 0; position: relative; overflow: hidden; touch-action: none; border-radius: 12px; background: color-mix(in srgb, var(--surface) 70%, transparent); cursor: zoom-in; user-select: none; -webkit-user-select: none; } .mag-driving-stage.is-zoomed { cursor: grab; } .mag-driving-pz { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; transform-origin: 50% 50%; will-change: transform; } .mag-driving-pz img { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; border-radius: 0; -webkit-user-drag: none; pointer-events: none; } .mag-driving-visual figcaption { flex: none; font-size: 13px; line-height: 1.25; text-align: center; color: var(--subtext); display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; } .mag-driving-visual figcaption span { margin-left: 6px; } .mag-driving-zoomctl { position: absolute; top: 6px; right: 6px; display: flex; gap: 6px; } .mag-driving-zoomctl button { width: 44px; height: 44px; padding: 0; border-radius: 50%; border: 1px solid var(--border); background: color-mix(in srgb, var(--surface) 88%, transparent); color: var(--text); font-size: 22px; cursor: pointer; display: flex; align-items: center; justify-content: center; } .mag-driving-zoomctl button:disabled { opacity: .4; cursor: default; } .mag-driving-noimg { margin: auto; text-align: center; padding: 12px; } /* the three-line view: fixed height so nothing moves as lines change */ .mag-driving-lyrics { flex: none; box-sizing: border-box; margin: 0; max-width: none; width: 100%; height: clamp(110px, 26vh, 220px); height: clamp(110px, 26dvh, 220px); } /* the whole text */ .mag-driving-full-wrap { flex: 1 1 0; min-height: 0; position: relative; display: none; } .mag-driving-full { height: 100%; box-sizing: border-box; overflow-y: auto; overscroll-behavior: contain; position: relative; padding: 30vh 12px; padding: 30dvh 12px; text-align: left; border-radius: 12px; background: color-mix(in srgb, var(--surface) 70%, transparent); } .mag-driving-fline { margin: 0 0 14px; font-size: clamp(16px, 4.5vw, 22px); line-height: 1.35; color: var(--text); opacity: .5; cursor: pointer; overflow-wrap: anywhere; } .mag-driving-fline.is-past { opacity: .35; } .mag-driving-fline.is-current { opacity: 1; font-weight: 700; font-size: clamp(22px, 6.2vw, 34px); cursor: default; border-left: 4px solid var(--accent); padding-left: 10px; } .mag-driving-fline:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; } .mag-driving-backcur { position: absolute; left: 50%; bottom: 10px; transform: translateX(-50%); min-height: 44px; padding: 0 18px; border: 0; border-radius: 22px; background: var(--accent); color: var(--on-accent, #fff); font-size: 16px; font-weight: 700; cursor: pointer; display: flex; align-items: center; gap: 8px; box-shadow: 0 2px 10px rgba(0,0,0,.3); } /* views */ .mag-driving[data-view="image"] .mag-driving-lyrics, .mag-driving[data-view="lyrics"] .mag-driving-lyrics, .mag-driving[data-view="lyrics"] .mag-driving-stagewrap, .mag-driving[data-view="compact"] .mag-driving-full-wrap, .mag-driving[data-view="image"] .mag-driving-full-wrap { display: none; } .mag-driving[data-view="lyrics"] .mag-driving-full-wrap { display: block; } .mag-driving[data-view="compact"] .mag-driving-stagewrap { flex: 1 1 0; } /* the dock */ .mag-driving-dock { flex: none; box-sizing: border-box; width: 100%; padding: 8px 12px 10px; border-top: 1px solid var(--border); background: var(--surface); display: flex; flex-direction: column; align-items: center; gap: 8px; margin-top: auto; } .mag-driving-dock .mag-driving-seek { margin: 0; width: min(680px, 100%); display: flex; align-items: center; gap: 10px; } .mag-driving-dock .mag-driving-seek input[type="range"] { flex: 1 1 auto; width: auto; min-width: 0; height: 32px; } .mag-driving-dock .mag-driving-time { margin: 0; flex: none; font-size: 14px; white-space: nowrap; } .mag-driving-dock .mag-driving-controls { margin: 0; } .mag-driving-dock .mag-driving-seek .mag-driving-opt { flex: none; min-width: 44px; min-height: 44px; padding: 0 8px; font-size: 16px; border: 1px solid var(--border); border-radius: 22px; background: var(--surface); color: var(--text); font-weight: 700; cursor: pointer; } .mag-driving-dock .mag-driving-opt:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; } @media (max-height: 760px) { .mag-driving-btn { width: 64px; height: 64px; font-size: 26px; } .mag-driving-btn.is-small { width: 52px; height: 52px; font-size: 22px; } .mag-driving-btn.mag-driving-play { width: 84px; height: 84px; font-size: 40px; } } @media (max-width: 480px) { .mag-driving-btn.is-small { width: 44px; height: 44px; } .mag-driving-dock { padding: 6px 8px 8px; gap: 6px; } } @media (max-width: 480px) and (max-height: 640px) { .mag-driving-btn { width: 48px; height: 48px; font-size: 22px; } .mag-driving-btn.mag-driving-play { width: 64px; height: 64px; font-size: 34px; } } /* landscape phones: compact controls, picture and text side by side */ @media (orientation: landscape) and (max-height: 500px) { .mag-driving-top { padding: 4px 8px; } .mag-driving-head h2 { font-size: 16px; -webkit-line-clamp: 1; line-clamp: 1; } .mag-driving-head p, .mag-driving-head .mag-driving-queue { display: none; } .mag-driving-btn, .mag-driving-btn.is-small { width: 48px; height: 48px; font-size: 22px; } .mag-driving-btn.mag-driving-play { width: 60px; height: 60px; font-size: 30px; } .mag-driving-dock { padding: 4px 12px 6px; gap: 2px; } .mag-driving-dock .mag-driving-seek input[type="range"] { height: 24px; } .mag-driving[data-view="compact"] .mag-driving-content { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-template-rows: auto auto minmax(0, 1fr); column-gap: 12px; row-gap: 4px; max-width: none; } .mag-driving[data-view="compact"] .mag-driving-start { grid-column: 1 / -1; grid-row: 1; min-height: 44px; } .mag-driving[data-view="compact"] .mag-driving-head { grid-column: 1 / -1; grid-row: 2; } .mag-driving[data-view="compact"] .mag-driving-stagewrap { grid-column: 1; grid-row: 3; } .mag-driving[data-view="compact"] .mag-driving-lyrics { grid-column: 2; grid-row: 3; height: auto; min-height: 0; } } /* minimized: just the current line above the dock */ .mag-driving[data-collapsed="true"] .mag-driving-stagewrap, .mag-driving[data-collapsed="true"] .mag-driving-full-wrap, .mag-driving[data-collapsed="true"] .mag-driving-head p, .mag-driving[data-collapsed="true"] .mag-driving-head .mag-driving-queue { display: none; } .mag-driving[data-collapsed="true"] .mag-driving-content { flex: 0 1 auto; display: flex; } .mag-driving[data-collapsed="true"] .mag-driving-head h2 { font-size: 15px; -webkit-line-clamp: 1; line-clamp: 1; } .mag-driving[data-collapsed="true"] .mag-driving-lyrics { display: flex; height: auto; max-height: 30vh; max-height: 30dvh; min-height: 0; grid-column: auto; grid-row: auto; } .mag-driving[data-collapsed="true"] .mag-driving-lyrics .is-prev, .mag-driving[data-collapsed="true"] .mag-driving-lyrics .is-next { display: none; } @media (prefers-reduced-motion: reduce) { .mag-driving-pz { transition: none; } .mag-driving-backcur { transition: none; } }