feat(gui+search): Inhaltssuche mit Zeitstempel-Verweis, Papierkorb (30 Tage), Titel umbenennen

Drei vom Nutzer angeforderte Funktionen.

Titel umbenennen: PATCH /api/recordings/:id/title. Neue Spalte recordings.title_locked,
damit eine spaetere KI-Analyse einen von Hand gesetzten Titel nicht wieder ueberschreibt.

Papierkorb: DELETE setzt nur noch deleted_at, nichts wird angefasst. Wiederherstellen,
endgueltig loeschen, Aufraeumlauf beim Boot und taeglich (Boot-Lauf, weil das Board oft
tagelang aus ist). Behebt nebenbei einen Altfehler: das bisherige Loeschen liess die
words-Zeilen verwaist in der DB und die WAV/MP3-Dateien auf der Karte zurueck.

Inhaltssuche: neue Tabelle passages (1689 Zeilen aus 22 Aufnahmen) mit Sprecher und
Zeitfenster je Passage. Trefferliste kommt aus SQL in Millisekunden, die formulierte
Antwort mit Belegen [1][2] wird nachgereicht (Qwen3-4B, ~140 s) - deshalb zweistufig.
Hybrid: MariaDB FULLTEXT + Passage-Embedding + Dokument-Embedding aus Titel/Zusammenfassung
(multilingual-e5-small, ONNX/CPU, taskset A55). Ohne Modell degradiert die Suche sauber
auf Volltext statt auszufallen.

Gemessene Grenzen sind dokumentiert statt geschoent: e5-small trennt auf verrauschtem
ASR-Text schwach (Kosinus-Spanne aller Passagen nur 0,8085-0,8560; eine irrelevante
Passage schlug die richtige Antwort). Dokument-Ebene und IDF-Wertung heben die richtige
Passage auf Rang 11 von 296, aber nicht in die Top-8, die das LLM liest. Naechster Hebel
waere multilingual-e5-base.

Relative Datumsangaben werden deterministisch aufgeloest, nicht vom LLM - ein falsches
Kalenderfenster waere schlimmer als gar keins.

Neu: app/search.js, app/emb.js, app/inference/embed_text.py, docs/09-suche-und-verwaltung.md
Board-Backups: .bak-search
This commit is contained in:
2026-07-31 13:20:20 +00:00
parent c09827bf86
commit d5b9733e78
10 changed files with 3651 additions and 2286 deletions
+1260 -1010
View File
File diff suppressed because it is too large Load Diff
+485 -387
View File
@@ -1,397 +1,495 @@
/* Aime — Anruf-Assistent. Zwei Themes über CSS-Variablen; data-theme setzt
index.html vor dem ersten Paint, der Header-Toggle schaltet um.
Akzent = warmes Kupfer (Tonband-Anmutung), Rot bleibt der Aufnahme reserviert. */
/* Aime — Anruf-Assistent. Zwei Themes über CSS-Variablen; data-theme setzt
index.html vor dem ersten Paint, der Header-Toggle schaltet um.
Akzent = warmes Kupfer (Tonband-Anmutung), Rot bleibt der Aufnahme reserviert. */
:root[data-theme="dark"] {
--bg: #14161b;
--surface: #1d222a;
--surface-2: #262c36;
--inset: #10141a;
--line: #303845;
--fg: #e9ecf1;
--muted: #98a2b3;
--accent: #e8804c;
--accent-fg: #17130f;
--accent-soft: rgba(232, 128, 76, .14);
--ok: #55b381;
--ok-soft: rgba(85, 179, 129, .14);
--warn: #e2b93b;
--warn-soft: rgba(226, 185, 59, .14);
--danger: #e5595e;
--danger-soft: rgba(229, 89, 94, .12);
--shadow: none;
--sp0: #5bc0eb; --sp1: #ef8fc0; --sp2: #a8cc4e; --sp3: #e6b84c;
--sp0-w: #8fd6f5; --sp1-w: #f5b5d6; --sp2-w: #c5e07e; --sp3-w: #f0d08a;
}
:root[data-theme="light"] {
--bg: #f4f2ee;
--surface: #ffffff;
--surface-2: #eceae4;
--inset: #f7f6f2;
--line: #ddd8cf;
--fg: #23272e;
--muted: #6d7683;
--accent: #c05f2d;
--accent-fg: #ffffff;
--accent-soft: rgba(192, 95, 45, .1);
--ok: #2f8a5d;
--ok-soft: rgba(47, 138, 93, .12);
--warn: #9a7514;
--warn-soft: rgba(154, 117, 20, .12);
--danger: #c73e43;
--danger-soft: rgba(199, 62, 67, .09);
--shadow: 0 1px 3px rgba(35, 39, 46, .07);
--sp0: #1179a8; --sp1: #c2417f; --sp2: #5f7d16; --sp3: #a4700d;
--sp0-w: #0e6b95; --sp1-w: #ad3a72; --sp2-w: #556f14; --sp3-w: #92640b;
}
* { box-sizing: border-box; }
/* Scrollbalken im Logo-Kupfer (Firefox: scrollbar-color; Chromium/WebKit: ::-webkit-*) */
* { scrollbar-width: thin; scrollbar-color: var(--accent) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
background: var(--accent); border-radius: 5px;
border: 2px solid transparent; background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--accent); filter: brightness(1.15); }
::-webkit-scrollbar-corner { background: transparent; }
html { color-scheme: dark; }
:root[data-theme="light"] { color-scheme: light; }
body {
margin: 0; background: var(--bg); color: var(--fg);
font: 15px/1.5 system-ui, "Segoe UI", Roboto, sans-serif;
transition: background .2s, color .2s;
}
/* Volle Fensterbreite für ALLE Tabs. Früher war nur „Aufnahmen“ breit (.wide)
und der Rest auf 860px zentriert — dadurch sprang die Kopfzeile beim
Tabwechsel in die Mitte. */
#app { max-width: none; margin: 0 auto; padding: 16px 28px; }
/* ---- Topbar / Brand ---- */
.topbar { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; padding: 6px 0 14px; }
.brand { display: flex; align-items: center; gap: 10px; }
.brand-wave { width: 30px; height: 18px; color: var(--accent); flex: none; }
.topbar h1 { margin: 0; font-size: 21px; font-weight: 650; letter-spacing: -.01em; }
.topbar .sub { font-size: 12px; color: var(--muted); font-weight: 400; margin-left: 6px; }
nav { display: flex; gap: 6px; flex-wrap: wrap; }
.topbar nav { margin-left: 0; }
/* Aufnahme-Steuerung in der Kopfzeile (zwischen Logo und Tabs) */
.rec-ctl { display: flex; align-items: center; gap: 10px; margin: 0 auto; }
.recbtn {
width: 38px; height: 38px; border-radius: 50%; border: 2px solid var(--danger);
background: var(--danger-soft); color: var(--danger); font-size: 15px; line-height: 1;
cursor: pointer; transition: background .15s, transform .1s;
}
.recbtn:hover { background: var(--danger); color: #fff; }
.recbtn.rec { background: var(--danger); color: #fff; animation: recpulse 1.4s ease-in-out infinite; }
@keyframes recpulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(229, 89, 94, .45); } 50% { box-shadow: 0 0 0 9px rgba(229, 89, 94, 0); } }
.recbtn:disabled { opacity: .55; cursor: default; animation: none; }
.rec-ctl .rectime { font-size: 14px; font-variant-numeric: tabular-nums; color: var(--muted); min-width: 44px; }
/* Namensmarker während der Aufnahme (Kopfzeile) */
.rec-ctl .mkin { width: 210px; padding: 6px 10px; font-size: 13px; }
@media (max-width: 1100px) { .rec-ctl .mkin { width: 130px; } }
/* Live-Platzhalter in der Aufnahmen-Liste */
.item.rec-live { border-color: var(--danger); background: var(--danger-soft); }
.rec-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--danger); flex: none; animation: recpulse 1.4s ease-in-out infinite; }
.item.rec-live .rectime { color: var(--danger); font-weight: 650; }
.mkchips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.mkchip {
display: inline-flex; align-items: center; gap: 5px; font-size: 12px;
background: var(--surface-2); border: 1px solid var(--line); border-radius: 999px; padding: 2px 4px 2px 8px;
}
.mkchip .mono { color: var(--muted); }
.mkdel { background: none; border: none; color: var(--muted); cursor: pointer; font-size: 12px; line-height: 1; padding: 2px 4px; }
.mkdel:hover { color: var(--danger); }
/* Namensmarker-Liste im Detail */
.markers { list-style: none; margin: 6px 0; padding: 0; }
.markers li { display: flex; align-items: center; gap: 7px; padding: 3px 0; font-size: 13px; }
.mkseek {
background: var(--surface-2); border: 1px solid var(--line); border-radius: 7px;
color: var(--accent); cursor: pointer; font-size: 12px; padding: 2px 7px;
}
.mkseek:hover { border-color: var(--accent); }
.mkadd-row { margin-top: 8px; }
.mkadd-row .input { flex: 1; min-width: 120px; }
.tab {
background: transparent; color: var(--muted); border: 1px solid transparent;
padding: 7px 14px; border-radius: 999px; cursor: pointer; font-size: 14px;
transition: color .12s, background .12s;
}
.tab:hover { color: var(--fg); background: var(--surface-2); }
.tab.active { color: var(--accent); background: var(--accent-soft); font-weight: 600; }
.btn.icon.theme { border-radius: 999px; font-size: 15px; line-height: 1; }
/* ---- Bausteine ---- */
.card {
background: var(--surface); border: 1px solid var(--line); border-radius: 14px;
padding: 18px; margin-bottom: 16px; box-shadow: var(--shadow);
}
h2 { margin: 0 0 12px; font-size: 18px; letter-spacing: -.01em; }
.row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.grow { flex: 1; }
.btn {
background: var(--surface-2); color: var(--fg); border: 1px solid var(--line);
border-radius: 9px; padding: 8px 14px; cursor: pointer; font-size: 14px;
transition: border-color .12s, background .12s;
}
.btn:hover { border-color: var(--accent); }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); font-weight: 600; }
.btn.primary:hover { filter: brightness(1.07); }
.btn.danger { background: var(--danger-soft); border-color: transparent; color: var(--danger); }
.btn.danger:hover { border-color: var(--danger); }
.btn.big { font-size: 17px; padding: 14px 22px; border-radius: 12px; }
.btn.small { font-size: .8rem; padding: 3px 8px; }
.btn.icon { padding: 6px 9px; flex: none; }
.btn:disabled { opacity: .55; cursor: default; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.lbl { display: block; font-size: 12px; color: var(--muted); margin: 10px 0 4px; text-transform: uppercase; letter-spacing: .04em; }
.input, select {
width: 100%; background: var(--inset); color: var(--fg);
border: 1px solid var(--line); border-radius: 9px; padding: 9px 12px; font-size: 14px;
}
.input:focus, select:focus { border-color: var(--accent); outline: none; }
.list { display: flex; flex-direction: column; gap: 8px; }
.item {
display: flex; gap: 10px; align-items: center;
background: var(--inset); border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px;
}
.badge { font-size: 11px; padding: 3px 8px; border-radius: 20px; background: var(--surface-2); color: var(--muted); }
.badge.ok { background: var(--ok-soft); color: var(--ok); }
.badge.warn { background: var(--warn-soft); color: var(--warn); }
.muted { color: var(--muted); }
.mono { font-family: ui-monospace, monospace; font-size: 12px; }
.err { color: var(--danger); }
.err-inline { color: var(--danger); margin-bottom: 6px; }
.hint { font-size: .8rem; color: var(--muted); margin-bottom: 10px; }
/* ---- Aufnahme ---- */
.rec-panel { display: flex; flex-direction: column; align-items: center; gap: 14px; margin: 22px 0 8px; }
.timer { font-size: 40px; font-variant-numeric: tabular-nums; font-weight: 600; }
audio { width: 100%; margin-bottom: 10px; }
/* ---- Transkript ---- */
.transcript { line-height: 2.1; font-size: 16px; }
.transcript .w { cursor: pointer; padding: 1px 2px; border-radius: 4px; transition: background .1s; }
.transcript .w:hover { background: var(--surface-2); }
.transcript .w.active, .w.active { background: var(--accent); color: var(--accent-fg); }
.w.sp0 { color: var(--sp0-w); } .w.sp1 { color: var(--sp1-w); }
.w.sp2 { color: var(--sp2-w); } .w.sp3 { color: var(--sp3-w); }
.toast {
position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
background: var(--surface); border: 1px solid var(--accent); color: var(--fg);
padding: 10px 18px; border-radius: 10px; z-index: 99; max-width: 90%; box-shadow: var(--shadow);
}
.toast.err { border-color: var(--danger); }
/* ---- Analyse ---- */
.summary-snip {
font-size: .85rem; color: var(--muted); margin-top: 4px; overflow: hidden;
text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.analysis { margin: 10px 0; padding: 12px; background: var(--inset); border: 1px solid var(--line); border-radius: 10px; }
.analysis h3 { margin: 8px 0 4px; font-size: .9rem; color: var(--muted); }
.summary { margin: 0 0 6px; line-height: 1.55; white-space: pre-line; }
.todos { margin: 0; padding-left: 20px; }
.todos li { margin: 3px 0; }
.parts { margin: 0; padding-left: 20px; } .parts li { margin: 3px 0; }
.clean { line-height: 1.55; background: var(--inset); padding: 8px 10px; border-radius: 8px; margin: 0 0 4px; }
/* ---- Dialog-Turns / Sprecher ---- */
.dialog { display: flex; flex-direction: column; gap: 6px; margin-bottom: 4px; }
.turn { padding: 6px 10px; border-radius: 8px; background: var(--inset); line-height: 1.5; }
.turn .spk { margin-right: 6px; font-weight: 600; }
.turn.sp0 { border-left: 3px solid var(--sp0); } .turn.sp0 .spk { color: var(--sp0); }
.turn.sp1 { border-left: 3px solid var(--sp1); } .turn.sp1 .spk { color: var(--sp1); }
.turn.sp2 { border-left: 3px solid var(--sp2); } .turn.sp2 .spk { color: var(--sp2); }
.turn.sp3 { border-left: 3px solid var(--sp3); } .turn.sp3 .spk { color: var(--sp3); }
.spk-edit { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 8px; }
.spk-row { display: flex; align-items: center; gap: 8px; padding: 4px 8px; border-radius: 8px; background: var(--inset); border-left: 3px solid var(--line); }
.spk-row.sp0 { border-left-color: var(--sp0); } .spk-row.sp1 { border-left-color: var(--sp1); }
.spk-row.sp2 { border-left-color: var(--sp2); } .spk-row.sp3 { border-left-color: var(--sp3); }
.spk-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--muted); flex: none; }
.spk-row.sp0 .spk-dot { background: var(--sp0); } .spk-row.sp1 .spk-dot { background: var(--sp1); }
.spk-row.sp2 .spk-dot { background: var(--sp2); } .spk-row.sp3 .spk-dot { background: var(--sp3); }
.spk-name { flex: 1; min-width: 80px; padding: 5px 8px; border-radius: 6px; border: 1px solid var(--line); background: var(--surface); color: var(--fg); }
.spk-learn-l { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--muted); white-space: nowrap; }
.ana-time { font-variant-numeric: tabular-nums; font-weight: 600; }
.ana-time.live { color: var(--accent); }
.ana-line { margin: 2px 0 8px; }
/* ---- Aufnahmen: Liste links, Detail rechts ---- */
.rec-layout { display: flex; gap: 8px; align-items: flex-start; }
.rec-splitter {
flex: 0 0 6px; align-self: stretch; cursor: col-resize; border-radius: 3px;
background: transparent; transition: background .12s;
}
.rec-splitter:hover, body.resizing .rec-splitter { background: var(--accent-soft); }
body.resizing { cursor: col-resize; user-select: none; }
.rec-list-pane {
flex: 0 0 340px; position: sticky; top: 12px; max-height: calc(100vh - 90px);
overflow-y: auto; background: var(--surface); border: 1px solid var(--line);
border-radius: 14px; padding: 10px; box-shadow: var(--shadow);
}
.rec-detail-pane { flex: 1 1 auto; min-width: 0; }
.rec-detail-pane .card { margin-bottom: 0; }
.detail-empty { padding: 40px 16px; text-align: center; border: 1px dashed var(--line); border-radius: 14px; }
.day-group { margin-bottom: 6px; }
.day-head {
display: flex; align-items: center; gap: 6px; cursor: pointer; font-size: 12px;
color: var(--muted); text-transform: uppercase; letter-spacing: .03em; padding: 6px 4px; user-select: none;
}
.day-head:hover { color: var(--fg); }
.day-caret { display: inline-block; transition: transform .15s; font-size: 10px; }
.day-group.collapsed .day-caret { transform: rotate(-90deg); }
.day-group.collapsed .day-items { display: none; }
.day-count { margin-left: auto; background: var(--accent-soft); color: var(--accent); border-radius: 20px; padding: 1px 8px; font-size: 11px; font-weight: 650; }
.day-items { display: flex; flex-direction: column; gap: 6px; }
.item.rec { cursor: pointer; transition: border-color .1s, background .1s; }
.item.rec:hover { border-color: var(--accent); }
.item.rec.sel { border-color: var(--accent); background: var(--accent-soft); }
/* ---- Detail: Module links, Transkript als eigene Scroll-Spalte rechts ---- */
.status-line { margin: 4px 0 10px; }
.detail-cols { display: flex; gap: 12px; align-items: flex-start; }
.mod-col { min-width: 0; display: flex; flex-direction: column; gap: 10px; min-height: 60px; }
.mod-col[data-col="main"] { flex: 1 1 54%; }
.mod-col[data-col="side"] { flex: 1 1 46%; }
.mod-col.empty { flex: 0 0 26px; border: 1px dashed var(--line); border-radius: 12px; align-self: stretch; }
.transcript-panel { max-height: calc(100vh - 90px); overflow-y: auto; position: sticky; top: 12px; }
.transcript-panel .mod-head { position: sticky; top: 0; z-index: 1; }
@media (max-width: 1100px) {
.detail-cols { flex-direction: column; }
.mod-col { width: 100%; }
.mod-col.empty { display: none; }
.transcript-panel { max-height: 60vh; position: static; }
}
.module { background: var(--inset); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
.module.dragging { opacity: .45; border-style: dashed; }
.mod-head {
display: flex; align-items: center; gap: 8px; padding: 8px 12px; cursor: grab;
background: var(--surface-2); user-select: none;
}
.mod-head:active { cursor: grabbing; }
.mod-head h3 { margin: 0; font-size: .85rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.mod-grip { color: var(--muted); font-size: 14px; letter-spacing: -1px; }
.mod-body { padding: 12px; }
.mod-body h3 { margin: 10px 0 4px; font-size: .9rem; color: var(--muted); }
/* ---- Sprecher-Verlauf: fester Streifen unter dem Player, volle Breite ---- */
.viz-top {
position: relative; margin: 4px 0 10px; padding: 10px 12px; background: var(--inset);
border: 1px solid var(--line); border-radius: 12px;
}
.lane-label { cursor: pointer; }
.lane-label:hover { color: var(--fg); text-decoration: underline dotted; }
.spkpop {
position: absolute; left: 12px; z-index: 5; width: 270px;
background: var(--surface); border: 1px solid var(--accent); border-radius: 10px;
padding: 10px 12px; box-shadow: 0 6px 22px rgba(0, 0, 0, .35);
}
.spkpop .input { margin-bottom: 6px; }
.viz-wrap { display: flex; gap: 14px; align-items: stretch; }
.lanes { flex: 1 1 58%; min-width: 0; position: relative; display: flex; flex-direction: column; gap: 6px; cursor: pointer; }
.lane-row { display: flex; align-items: center; gap: 8px; }
.lane-label {
flex: 0 0 92px; display: flex; align-items: center; gap: 6px; font-size: 12px;
color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.lane { position: relative; flex: 1; height: 22px; background: var(--surface-2); border-radius: 6px; overflow: hidden; }
.seg { position: absolute; top: 3px; bottom: 3px; border-radius: 3px; opacity: .75; transition: opacity .15s; }
.lane-row.sp0 .seg { background: var(--sp0); } .lane-row.sp1 .seg { background: var(--sp1); }
.lane-row.sp2 .seg { background: var(--sp2); } .lane-row.sp3 .seg { background: var(--sp3); }
.lane-row.sp0 .spk-dot { background: var(--sp0); } .lane-row.sp1 .spk-dot { background: var(--sp1); }
.lane-row.sp2 .spk-dot { background: var(--sp2); } .lane-row.sp3 .spk-dot { background: var(--sp3); }
.lane-row.speaking .seg { opacity: 1; }
.lane-row.speaking .lane-label { color: var(--fg); font-weight: 600; }
.lane-row.speaking .spk-dot { animation: pulse 1s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.55); } }
.playhead {
display: none; position: absolute; top: 0; bottom: 0; width: 2px;
background: var(--accent); border-radius: 1px; pointer-events: none; z-index: 2;
transition: left .25s linear;
}
.lanes { flex: 1 1 100%; }
@media (max-width: 800px) {
#app { padding: 16px; }
.rec-layout { flex-direction: column; }
.rec-list-pane { flex: none !important; width: 100%; position: static; max-height: 340px; }
.rec-splitter { display: none; }
.topbar nav { margin-left: 0; width: 100%; order: 3; }
}
/* ---- Konferenztisch-Ansicht ---- */
.hidden { display: none !important; }
.tablescene { position: relative; width: 100%; aspect-ratio: 4 / 3; max-height: 420px; }
.ttable {
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
width: 58%; height: 44%; border-radius: 18px;
background: var(--surface-2); border: 2px solid var(--line);
box-shadow: inset 0 0 0 6px var(--inset);
}
.tperson { position: absolute; transform: translate(-50%, -50%); display: flex; flex-direction: column; align-items: center; gap: 2px; width: 76px; }
.thead {
position: relative; width: 30px; height: 30px; border-radius: 50%;
background: var(--muted); z-index: 2;
}
.tperson.sp0 .thead, .tperson.sp0 .tbody { background: var(--sp0); }
.tperson.sp1 .thead, .tperson.sp1 .tbody { background: var(--sp1); }
.tperson.sp2 .thead, .tperson.sp2 .tbody { background: var(--sp2); }
.tperson.sp3 .thead, .tperson.sp3 .tbody { background: var(--sp3); }
.teyes { position: absolute; left: 0; right: 0; top: 9px; display: flex; justify-content: center; gap: 7px; transition: transform .25s; }
.teye { width: 5px; height: 5px; border-radius: 50%; background: #17130f; }
:root[data-theme="light"] .teye { background: #fff; }
.tmouth {
position: absolute; left: 50%; bottom: 6px; transform: translateX(-50%);
width: 9px; height: 3px; border-radius: 2px; background: rgba(0, 0, 0, .55); opacity: 0;
}
:root[data-theme="light"] .tmouth { background: rgba(255, 255, 255, .8); }
.tperson.talking .tmouth { opacity: 1; animation: talkmouth .35s ease-in-out infinite; }
@keyframes talkmouth { 0%, 100% { height: 2px; } 50% { height: 7px; } }
.tperson.talking .thead { box-shadow: 0 0 0 3px var(--accent-soft), 0 0 10px var(--accent-soft); }
.tbody { width: 46px; height: 20px; border-radius: 12px 12px 5px 5px; background: var(--muted); margin-top: -6px; opacity: .85; }
.tname { font-size: 11px; color: var(--muted); max-width: 76px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tperson.talking .tname { color: var(--fg); font-weight: 650; }
.tbubble {
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
width: 54%; z-index: 3;
background: var(--surface); border: 2px solid var(--line); border-radius: 14px;
padding: 8px 10px; font-size: 13px; line-height: 1.55;
transition: border-color .2s;
}
.tb-scroll { max-height: 150px; overflow-y: auto; }
.tbubble .tb-name { display: block; font-weight: 650; font-size: 11px; margin-bottom: 2px; position: sticky; top: 0; background: var(--surface); padding: 2px 0; z-index: 1; }
.tbubble .bw { cursor: pointer; padding: 1px 2px; border-radius: 4px; }
.tbubble .bw:hover { background: var(--surface-2); }
.tbubble .bw.active { background: var(--accent); color: var(--accent-fg); }
.tbubble.sp0 { border-color: var(--sp0); } .tbubble.sp0 .tb-name { color: var(--sp0); }
.tbubble.sp1 { border-color: var(--sp1); } .tbubble.sp1 .tb-name { color: var(--sp1); }
.tbubble.sp2 { border-color: var(--sp2); } .tbubble.sp2 .tb-name { color: var(--sp2); }
.tbubble.sp3 { border-color: var(--sp3); } .tbubble.sp3 .tb-name { color: var(--sp3); }
.tbubble::after {
content: ''; position: absolute; width: 12px; height: 12px;
background: inherit; border: inherit; border-width: 0 2px 2px 0;
display: none;
}
.tbubble[class*="arrow-"]::after { display: block; }
.tbubble.arrow-top::after { top: -7px; left: var(--ax, 50%); transform: translateX(-50%) rotate(-135deg); }
.tbubble.arrow-bottom::after { bottom: -7px; left: var(--ax, 50%); transform: translateX(-50%) rotate(45deg); }
.tbubble.arrow-left::after { left: -7px; top: 50%; transform: translateY(-50%) rotate(135deg); }
.tbubble.arrow-right::after { right: -7px; top: 50%; transform: translateY(-50%) rotate(-45deg); }
.tidle { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); font-size: 13px; text-align: center; }
#viewswitch svg { display: block; }
@media (prefers-reduced-motion: reduce) {
* { transition: none !important; animation: none !important; }
}
:root[data-theme="dark"] {
--bg: #14161b;
--surface: #1d222a;
--surface-2: #262c36;
--inset: #10141a;
--line: #303845;
--fg: #e9ecf1;
--muted: #98a2b3;
--accent: #e8804c;
--accent-fg: #17130f;
--accent-soft: rgba(232, 128, 76, .14);
--ok: #55b381;
--ok-soft: rgba(85, 179, 129, .14);
--warn: #e2b93b;
--warn-soft: rgba(226, 185, 59, .14);
--danger: #e5595e;
--danger-soft: rgba(229, 89, 94, .12);
--shadow: none;
--sp0: #5bc0eb; --sp1: #ef8fc0; --sp2: #a8cc4e; --sp3: #e6b84c;
--sp0-w: #8fd6f5; --sp1-w: #f5b5d6; --sp2-w: #c5e07e; --sp3-w: #f0d08a;
}
:root[data-theme="light"] {
--bg: #f4f2ee;
--surface: #ffffff;
--surface-2: #eceae4;
--inset: #f7f6f2;
--line: #ddd8cf;
--fg: #23272e;
--muted: #6d7683;
--accent: #c05f2d;
--accent-fg: #ffffff;
--accent-soft: rgba(192, 95, 45, .1);
--ok: #2f8a5d;
--ok-soft: rgba(47, 138, 93, .12);
--warn: #9a7514;
--warn-soft: rgba(154, 117, 20, .12);
--danger: #c73e43;
--danger-soft: rgba(199, 62, 67, .09);
--shadow: 0 1px 3px rgba(35, 39, 46, .07);
--sp0: #1179a8; --sp1: #c2417f; --sp2: #5f7d16; --sp3: #a4700d;
--sp0-w: #0e6b95; --sp1-w: #ad3a72; --sp2-w: #556f14; --sp3-w: #92640b;
}
/* ==========================================================================
Suche — Google-Anmutung, aber in der Aime-Palette: viel Weißraum, eine
dominante Eingabezeile, Treffer als Textblöcke ohne Karten-Rahmen.
========================================================================== */
.search-page { max-width: 46rem; margin: 0 auto; padding: 1rem 1rem 4rem; }
/* Startzustand: Suchfeld optisch mittig, wie eine leere Suchseite. */
.search-page.landing { padding-top: 12vh; }
.search-page.has-results { padding-top: 1.25rem; }
* { box-sizing: border-box; }
.search-box {
display: flex; align-items: center; gap: .5rem;
border: 1px solid var(--line); border-radius: 2rem;
background: var(--surface); box-shadow: var(--shadow);
padding: .35rem .35rem .35rem 1rem;
transition: box-shadow .15s, border-color .15s;
}
.search-box:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.search-ico { opacity: .55; font-size: 1rem; }
.search-input {
flex: 1; border: 0; outline: 0; background: transparent; color: var(--fg);
font: inherit; font-size: 1.05rem; padding: .55rem 0;
}
.search-clear { border: 0; background: transparent; color: var(--muted); cursor: pointer; font-size: .95rem; padding: .25rem .4rem; }
.search-clear:hover { color: var(--fg); }
.search-go {
border: 0; border-radius: 1.6rem; cursor: pointer;
background: var(--accent); color: var(--accent-fg);
font: inherit; font-weight: 600; padding: .55rem 1.1rem;
}
.search-go:hover { filter: brightness(1.07); }
/* Scrollbalken im Logo-Kupfer (Firefox: scrollbar-color; Chromium/WebKit: ::-webkit-*) */
* { scrollbar-width: thin; scrollbar-color: var(--accent) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
background: var(--accent); border-radius: 5px;
border: 2px solid transparent; background-clip: padding-box;
.search-hints { margin-top: 1.5rem; display: flex; flex-direction: column; gap: .5rem; align-items: flex-start; }
.search-hints .example {
border: 1px solid var(--line); border-radius: 1rem; background: var(--surface-2);
color: var(--fg); font: inherit; font-size: .88rem; padding: .35rem .8rem; cursor: pointer; text-align: left;
}
::-webkit-scrollbar-thumb:hover { background: var(--accent); filter: brightness(1.15); }
::-webkit-scrollbar-corner { background: transparent; }
html { color-scheme: dark; }
:root[data-theme="light"] { color-scheme: light; }
body {
margin: 0; background: var(--bg); color: var(--fg);
font: 15px/1.5 system-ui, "Segoe UI", Roboto, sans-serif;
transition: background .2s, color .2s;
}
/* Volle Fensterbreite für ALLE Tabs. Früher war nur „Aufnahmen“ breit (.wide)
und der Rest auf 860px zentriert — dadurch sprang die Kopfzeile beim
Tabwechsel in die Mitte. */
#app { max-width: none; margin: 0 auto; padding: 16px 28px; }
.search-hints .example:hover { border-color: var(--accent); color: var(--accent); }
/* ---- Topbar / Brand ---- */
.topbar { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; padding: 6px 0 14px; }
.brand { display: flex; align-items: center; gap: 10px; }
.brand-wave { width: 30px; height: 18px; color: var(--accent); flex: none; }
.topbar h1 { margin: 0; font-size: 21px; font-weight: 650; letter-spacing: -.01em; }
.topbar .sub { font-size: 12px; color: var(--muted); font-weight: 400; margin-left: 6px; }
nav { display: flex; gap: 6px; flex-wrap: wrap; }
.topbar nav { margin-left: 0; }
.searching { padding: 1.5rem 0; }
.search-empty { padding: 1.5rem 0; }
/* Aufnahme-Steuerung in der Kopfzeile (zwischen Logo und Tabs) */
.rec-ctl { display: flex; align-items: center; gap: 10px; margin: 0 auto; }
.recbtn {
width: 38px; height: 38px; border-radius: 50%; border: 2px solid var(--danger);
background: var(--danger-soft); color: var(--danger); font-size: 15px; line-height: 1;
cursor: pointer; transition: background .15s, transform .1s;
/* Antwortkasten: abgesetzt wie eine KI-Übersicht, nicht wie ein Treffer. */
.answer-box {
margin: 1.25rem 0 .5rem; padding: .9rem 1rem;
background: var(--accent-soft); border: 1px solid var(--line); border-radius: .75rem;
}
.recbtn:hover { background: var(--danger); color: #fff; }
.recbtn.rec { background: var(--danger); color: #fff; animation: recpulse 1.4s ease-in-out infinite; }
@keyframes recpulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(229, 89, 94, .45); } 50% { box-shadow: 0 0 0 9px rgba(229, 89, 94, 0); } }
.recbtn:disabled { opacity: .55; cursor: default; animation: none; }
.rec-ctl .rectime { font-size: 14px; font-variant-numeric: tabular-nums; color: var(--muted); min-width: 44px; }
.answer-head { font-weight: 600; font-size: .82rem; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); margin-bottom: .5rem; }
.answer-body { line-height: 1.55; white-space: pre-wrap; }
/* Platzhalterbalken, solange das LLM rechnet (30-90 s sind hier normal). */
.answer-body.skeleton { display: flex; flex-direction: column; gap: .45rem; }
.answer-body.skeleton span {
height: .7rem; border-radius: .35rem; background: var(--line); opacity: .55;
animation: pulse 1.4s ease-in-out infinite;
}
.answer-body.skeleton span:nth-child(1) { width: 92%; }
.answer-body.skeleton span:nth-child(2) { width: 78%; animation-delay: .18s; }
.answer-body.skeleton span:nth-child(3) { width: 45%; animation-delay: .36s; }
@keyframes pulse { 0%, 100% { opacity: .3; } 50% { opacity: .7; } }
.answer-src { margin-top: .7rem; display: flex; flex-wrap: wrap; gap: .4rem; }
.src-chip {
border: 1px solid var(--line); background: var(--surface); color: var(--fg);
border-radius: 1rem; font: inherit; font-size: .8rem; padding: .2rem .6rem; cursor: pointer;
}
.src-chip:hover { border-color: var(--accent); color: var(--accent); }
/* Namensmarker während der Aufnahme (Kopfzeile) */
.rec-ctl .mkin { width: 210px; padding: 6px 10px; font-size: 13px; }
@media (max-width: 1100px) { .rec-ctl .mkin { width: 130px; } }
.result-meta { margin: .9rem 0 .3rem; }
.results { list-style: none; margin: 0; padding: 0; }
.result { padding: .85rem 0; cursor: pointer; border-bottom: 1px solid var(--line); }
.result:hover .result-title { text-decoration: underline; }
.result-src { color: var(--muted); font-size: .78rem; margin-bottom: .15rem; }
.result-title { display: block; color: var(--accent); font-size: 1.05rem; text-decoration: none; }
.result-at { color: var(--muted); font-size: .8rem; font-weight: 400; margin-left: .4rem; }
.result-snip { color: var(--fg); font-size: .9rem; line-height: 1.5; margin-top: .2rem; }
.result-snip mark { background: var(--warn-soft); color: inherit; font-weight: 600; padding: 0 .1em; border-radius: .15em; }
/* Live-Platzhalter in der Aufnahmen-Liste */
.item.rec-live { border-color: var(--danger); background: var(--danger-soft); }
.rec-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--danger); flex: none; animation: recpulse 1.4s ease-in-out infinite; }
.item.rec-live .rectime { color: var(--danger); font-weight: 650; }
.mkchips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.mkchip {
display: inline-flex; align-items: center; gap: 5px; font-size: 12px;
background: var(--surface-2); border: 1px solid var(--line); border-radius: 999px; padding: 2px 4px 2px 8px;
}
.mkchip .mono { color: var(--muted); }
.mkdel { background: none; border: none; color: var(--muted); cursor: pointer; font-size: 12px; line-height: 1; padding: 2px 4px; }
.mkdel:hover { color: var(--danger); }
/* ==========================================================================
Papierkorb + Umbenennen
========================================================================== */
.list-bar { display: flex; justify-content: flex-end; padding: .35rem .5rem; border-bottom: 1px solid var(--line); }
.btn.ghost { background: transparent; border-color: transparent; color: var(--muted); }
.btn.ghost:hover { color: var(--fg); border-color: var(--line); }
.btn.ghost.active { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
.trash-hint, .trash-empty { padding: .6rem .75rem; }
.item.rec.trashed { opacity: .8; }
.item.rec.trashed b { text-decoration: line-through; text-decoration-color: var(--muted); }
.days-left { color: var(--muted); }
.days-left.urgent { color: var(--danger); font-weight: 600; }
/* Der Stift sitzt neben der H2 des Detailkopfs, ohne die Zeile umzubrechen. */
.rename-btn { vertical-align: middle; margin-left: -.25rem; opacity: .55; }
.rename-btn:hover { opacity: 1; }
.item.rec [data-ren] { opacity: .45; }
.item.rec:hover [data-ren] { opacity: 1; }
/* Namensmarker-Liste im Detail */
.markers { list-style: none; margin: 6px 0; padding: 0; }
.markers li { display: flex; align-items: center; gap: 7px; padding: 3px 0; font-size: 13px; }
.mkseek {
background: var(--surface-2); border: 1px solid var(--line); border-radius: 7px;
color: var(--accent); cursor: pointer; font-size: 12px; padding: 2px 7px;
}
.mkseek:hover { border-color: var(--accent); }
.mkadd-row { margin-top: 8px; }
.mkadd-row .input { flex: 1; min-width: 120px; }
.tab {
background: transparent; color: var(--muted); border: 1px solid transparent;
padding: 7px 14px; border-radius: 999px; cursor: pointer; font-size: 14px;
transition: color .12s, background .12s;
}
.tab:hover { color: var(--fg); background: var(--surface-2); }
.tab.active { color: var(--accent); background: var(--accent-soft); font-weight: 600; }
.btn.icon.theme { border-radius: 999px; font-size: 15px; line-height: 1; }
/* ---- Bausteine ---- */
.card {
background: var(--surface); border: 1px solid var(--line); border-radius: 14px;
padding: 18px; margin-bottom: 16px; box-shadow: var(--shadow);
}
h2 { margin: 0 0 12px; font-size: 18px; letter-spacing: -.01em; }
.row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.grow { flex: 1; }
.btn {
background: var(--surface-2); color: var(--fg); border: 1px solid var(--line);
border-radius: 9px; padding: 8px 14px; cursor: pointer; font-size: 14px;
transition: border-color .12s, background .12s;
}
.btn:hover { border-color: var(--accent); }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); font-weight: 600; }
.btn.primary:hover { filter: brightness(1.07); }
.btn.danger { background: var(--danger-soft); border-color: transparent; color: var(--danger); }
.btn.danger:hover { border-color: var(--danger); }
.btn.big { font-size: 17px; padding: 14px 22px; border-radius: 12px; }
.btn.small { font-size: .8rem; padding: 3px 8px; }
.btn.icon { padding: 6px 9px; flex: none; }
.btn:disabled { opacity: .55; cursor: default; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.lbl { display: block; font-size: 12px; color: var(--muted); margin: 10px 0 4px; text-transform: uppercase; letter-spacing: .04em; }
.input, select {
width: 100%; background: var(--inset); color: var(--fg);
border: 1px solid var(--line); border-radius: 9px; padding: 9px 12px; font-size: 14px;
}
.input:focus, select:focus { border-color: var(--accent); outline: none; }
.list { display: flex; flex-direction: column; gap: 8px; }
.item {
display: flex; gap: 10px; align-items: center;
background: var(--inset); border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px;
}
.badge { font-size: 11px; padding: 3px 8px; border-radius: 20px; background: var(--surface-2); color: var(--muted); }
.badge.ok { background: var(--ok-soft); color: var(--ok); }
.badge.warn { background: var(--warn-soft); color: var(--warn); }
.muted { color: var(--muted); }
.mono { font-family: ui-monospace, monospace; font-size: 12px; }
.err { color: var(--danger); }
.err-inline { color: var(--danger); margin-bottom: 6px; }
.hint { font-size: .8rem; color: var(--muted); margin-bottom: 10px; }
/* ---- Aufnahme ---- */
.rec-panel { display: flex; flex-direction: column; align-items: center; gap: 14px; margin: 22px 0 8px; }
.timer { font-size: 40px; font-variant-numeric: tabular-nums; font-weight: 600; }
audio { width: 100%; margin-bottom: 10px; }
/* ---- Transkript ---- */
.transcript { line-height: 2.1; font-size: 16px; }
.transcript .w { cursor: pointer; padding: 1px 2px; border-radius: 4px; transition: background .1s; }
.transcript .w:hover { background: var(--surface-2); }
.transcript .w.active, .w.active { background: var(--accent); color: var(--accent-fg); }
.w.sp0 { color: var(--sp0-w); } .w.sp1 { color: var(--sp1-w); }
.w.sp2 { color: var(--sp2-w); } .w.sp3 { color: var(--sp3-w); }
.toast {
position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
background: var(--surface); border: 1px solid var(--accent); color: var(--fg);
padding: 10px 18px; border-radius: 10px; z-index: 99; max-width: 90%; box-shadow: var(--shadow);
}
.toast.err { border-color: var(--danger); }
/* ---- Analyse ---- */
.summary-snip {
font-size: .85rem; color: var(--muted); margin-top: 4px; overflow: hidden;
text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.analysis { margin: 10px 0; padding: 12px; background: var(--inset); border: 1px solid var(--line); border-radius: 10px; }
.analysis h3 { margin: 8px 0 4px; font-size: .9rem; color: var(--muted); }
.summary { margin: 0 0 6px; line-height: 1.55; white-space: pre-line; }
.todos { margin: 0; padding-left: 20px; }
.todos li { margin: 3px 0; }
.parts { margin: 0; padding-left: 20px; } .parts li { margin: 3px 0; }
.clean { line-height: 1.55; background: var(--inset); padding: 8px 10px; border-radius: 8px; margin: 0 0 4px; }
/* ---- Dialog-Turns / Sprecher ---- */
.dialog { display: flex; flex-direction: column; gap: 6px; margin-bottom: 4px; }
.turn { padding: 6px 10px; border-radius: 8px; background: var(--inset); line-height: 1.5; }
.turn .spk { margin-right: 6px; font-weight: 600; }
.turn.sp0 { border-left: 3px solid var(--sp0); } .turn.sp0 .spk { color: var(--sp0); }
.turn.sp1 { border-left: 3px solid var(--sp1); } .turn.sp1 .spk { color: var(--sp1); }
.turn.sp2 { border-left: 3px solid var(--sp2); } .turn.sp2 .spk { color: var(--sp2); }
.turn.sp3 { border-left: 3px solid var(--sp3); } .turn.sp3 .spk { color: var(--sp3); }
.spk-edit { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 8px; }
.spk-row { display: flex; align-items: center; gap: 8px; padding: 4px 8px; border-radius: 8px; background: var(--inset); border-left: 3px solid var(--line); }
.spk-row.sp0 { border-left-color: var(--sp0); } .spk-row.sp1 { border-left-color: var(--sp1); }
.spk-row.sp2 { border-left-color: var(--sp2); } .spk-row.sp3 { border-left-color: var(--sp3); }
.spk-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--muted); flex: none; }
.spk-row.sp0 .spk-dot { background: var(--sp0); } .spk-row.sp1 .spk-dot { background: var(--sp1); }
.spk-row.sp2 .spk-dot { background: var(--sp2); } .spk-row.sp3 .spk-dot { background: var(--sp3); }
.spk-name { flex: 1; min-width: 80px; padding: 5px 8px; border-radius: 6px; border: 1px solid var(--line); background: var(--surface); color: var(--fg); }
.spk-learn-l { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--muted); white-space: nowrap; }
.ana-time { font-variant-numeric: tabular-nums; font-weight: 600; }
.ana-time.live { color: var(--accent); }
.ana-line { margin: 2px 0 8px; }
/* ---- Aufnahmen: Liste links, Detail rechts ---- */
.rec-layout { display: flex; gap: 8px; align-items: flex-start; }
.rec-splitter {
flex: 0 0 6px; align-self: stretch; cursor: col-resize; border-radius: 3px;
background: transparent; transition: background .12s;
}
.rec-splitter:hover, body.resizing .rec-splitter { background: var(--accent-soft); }
body.resizing { cursor: col-resize; user-select: none; }
.rec-list-pane {
flex: 0 0 340px; position: sticky; top: 12px; max-height: calc(100vh - 90px);
overflow-y: auto; background: var(--surface); border: 1px solid var(--line);
border-radius: 14px; padding: 10px; box-shadow: var(--shadow);
}
.rec-detail-pane { flex: 1 1 auto; min-width: 0; }
.rec-detail-pane .card { margin-bottom: 0; }
.detail-empty { padding: 40px 16px; text-align: center; border: 1px dashed var(--line); border-radius: 14px; }
.day-group { margin-bottom: 6px; }
.day-head {
display: flex; align-items: center; gap: 6px; cursor: pointer; font-size: 12px;
color: var(--muted); text-transform: uppercase; letter-spacing: .03em; padding: 6px 4px; user-select: none;
}
.day-head:hover { color: var(--fg); }
.day-caret { display: inline-block; transition: transform .15s; font-size: 10px; }
.day-group.collapsed .day-caret { transform: rotate(-90deg); }
.day-group.collapsed .day-items { display: none; }
.day-count { margin-left: auto; background: var(--accent-soft); color: var(--accent); border-radius: 20px; padding: 1px 8px; font-size: 11px; font-weight: 650; }
.day-items { display: flex; flex-direction: column; gap: 6px; }
.item.rec { cursor: pointer; transition: border-color .1s, background .1s; }
.item.rec:hover { border-color: var(--accent); }
.item.rec.sel { border-color: var(--accent); background: var(--accent-soft); }
/* ---- Detail: Module links, Transkript als eigene Scroll-Spalte rechts ---- */
.status-line { margin: 4px 0 10px; }
.detail-cols { display: flex; gap: 12px; align-items: flex-start; }
.mod-col { min-width: 0; display: flex; flex-direction: column; gap: 10px; min-height: 60px; }
.mod-col[data-col="main"] { flex: 1 1 54%; }
.mod-col[data-col="side"] { flex: 1 1 46%; }
.mod-col.empty { flex: 0 0 26px; border: 1px dashed var(--line); border-radius: 12px; align-self: stretch; }
.transcript-panel { max-height: calc(100vh - 90px); overflow-y: auto; position: sticky; top: 12px; }
.transcript-panel .mod-head { position: sticky; top: 0; z-index: 1; }
@media (max-width: 1100px) {
.detail-cols { flex-direction: column; }
.mod-col { width: 100%; }
.mod-col.empty { display: none; }
.transcript-panel { max-height: 60vh; position: static; }
}
.module { background: var(--inset); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
.module.dragging { opacity: .45; border-style: dashed; }
.mod-head {
display: flex; align-items: center; gap: 8px; padding: 8px 12px; cursor: grab;
background: var(--surface-2); user-select: none;
}
.mod-head:active { cursor: grabbing; }
.mod-head h3 { margin: 0; font-size: .85rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.mod-grip { color: var(--muted); font-size: 14px; letter-spacing: -1px; }
.mod-body { padding: 12px; }
.mod-body h3 { margin: 10px 0 4px; font-size: .9rem; color: var(--muted); }
/* ---- Sprecher-Verlauf: fester Streifen unter dem Player, volle Breite ---- */
.viz-top {
position: relative; margin: 4px 0 10px; padding: 10px 12px; background: var(--inset);
border: 1px solid var(--line); border-radius: 12px;
}
.lane-label { cursor: pointer; }
.lane-label:hover { color: var(--fg); text-decoration: underline dotted; }
.spkpop {
position: absolute; left: 12px; z-index: 5; width: 270px;
background: var(--surface); border: 1px solid var(--accent); border-radius: 10px;
padding: 10px 12px; box-shadow: 0 6px 22px rgba(0, 0, 0, .35);
}
.spkpop .input { margin-bottom: 6px; }
.viz-wrap { display: flex; gap: 14px; align-items: stretch; }
.lanes { flex: 1 1 58%; min-width: 0; position: relative; display: flex; flex-direction: column; gap: 6px; cursor: pointer; }
.lane-row { display: flex; align-items: center; gap: 8px; }
.lane-label {
flex: 0 0 92px; display: flex; align-items: center; gap: 6px; font-size: 12px;
color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.lane { position: relative; flex: 1; height: 22px; background: var(--surface-2); border-radius: 6px; overflow: hidden; }
.seg { position: absolute; top: 3px; bottom: 3px; border-radius: 3px; opacity: .75; transition: opacity .15s; }
.lane-row.sp0 .seg { background: var(--sp0); } .lane-row.sp1 .seg { background: var(--sp1); }
.lane-row.sp2 .seg { background: var(--sp2); } .lane-row.sp3 .seg { background: var(--sp3); }
.lane-row.sp0 .spk-dot { background: var(--sp0); } .lane-row.sp1 .spk-dot { background: var(--sp1); }
.lane-row.sp2 .spk-dot { background: var(--sp2); } .lane-row.sp3 .spk-dot { background: var(--sp3); }
.lane-row.speaking .seg { opacity: 1; }
.lane-row.speaking .lane-label { color: var(--fg); font-weight: 600; }
.lane-row.speaking .spk-dot { animation: pulse 1s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.55); } }
.playhead {
display: none; position: absolute; top: 0; bottom: 0; width: 2px;
background: var(--accent); border-radius: 1px; pointer-events: none; z-index: 2;
transition: left .25s linear;
}
.lanes { flex: 1 1 100%; }
@media (max-width: 800px) {
#app { padding: 16px; }
.rec-layout { flex-direction: column; }
.rec-list-pane { flex: none !important; width: 100%; position: static; max-height: 340px; }
.rec-splitter { display: none; }
.topbar nav { margin-left: 0; width: 100%; order: 3; }
}
/* ---- Konferenztisch-Ansicht ---- */
.hidden { display: none !important; }
.tablescene { position: relative; width: 100%; aspect-ratio: 4 / 3; max-height: 420px; }
.ttable {
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
width: 58%; height: 44%; border-radius: 18px;
background: var(--surface-2); border: 2px solid var(--line);
box-shadow: inset 0 0 0 6px var(--inset);
}
.tperson { position: absolute; transform: translate(-50%, -50%); display: flex; flex-direction: column; align-items: center; gap: 2px; width: 76px; }
.thead {
position: relative; width: 30px; height: 30px; border-radius: 50%;
background: var(--muted); z-index: 2;
}
.tperson.sp0 .thead, .tperson.sp0 .tbody { background: var(--sp0); }
.tperson.sp1 .thead, .tperson.sp1 .tbody { background: var(--sp1); }
.tperson.sp2 .thead, .tperson.sp2 .tbody { background: var(--sp2); }
.tperson.sp3 .thead, .tperson.sp3 .tbody { background: var(--sp3); }
.teyes { position: absolute; left: 0; right: 0; top: 9px; display: flex; justify-content: center; gap: 7px; transition: transform .25s; }
.teye { width: 5px; height: 5px; border-radius: 50%; background: #17130f; }
:root[data-theme="light"] .teye { background: #fff; }
.tmouth {
position: absolute; left: 50%; bottom: 6px; transform: translateX(-50%);
width: 9px; height: 3px; border-radius: 2px; background: rgba(0, 0, 0, .55); opacity: 0;
}
:root[data-theme="light"] .tmouth { background: rgba(255, 255, 255, .8); }
.tperson.talking .tmouth { opacity: 1; animation: talkmouth .35s ease-in-out infinite; }
@keyframes talkmouth { 0%, 100% { height: 2px; } 50% { height: 7px; } }
.tperson.talking .thead { box-shadow: 0 0 0 3px var(--accent-soft), 0 0 10px var(--accent-soft); }
.tbody { width: 46px; height: 20px; border-radius: 12px 12px 5px 5px; background: var(--muted); margin-top: -6px; opacity: .85; }
.tname { font-size: 11px; color: var(--muted); max-width: 76px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tperson.talking .tname { color: var(--fg); font-weight: 650; }
.tbubble {
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
width: 54%; z-index: 3;
background: var(--surface); border: 2px solid var(--line); border-radius: 14px;
padding: 8px 10px; font-size: 13px; line-height: 1.55;
transition: border-color .2s;
}
.tb-scroll { max-height: 150px; overflow-y: auto; }
.tbubble .tb-name { display: block; font-weight: 650; font-size: 11px; margin-bottom: 2px; position: sticky; top: 0; background: var(--surface); padding: 2px 0; z-index: 1; }
.tbubble .bw { cursor: pointer; padding: 1px 2px; border-radius: 4px; }
.tbubble .bw:hover { background: var(--surface-2); }
.tbubble .bw.active { background: var(--accent); color: var(--accent-fg); }
.tbubble.sp0 { border-color: var(--sp0); } .tbubble.sp0 .tb-name { color: var(--sp0); }
.tbubble.sp1 { border-color: var(--sp1); } .tbubble.sp1 .tb-name { color: var(--sp1); }
.tbubble.sp2 { border-color: var(--sp2); } .tbubble.sp2 .tb-name { color: var(--sp2); }
.tbubble.sp3 { border-color: var(--sp3); } .tbubble.sp3 .tb-name { color: var(--sp3); }
.tbubble::after {
content: ''; position: absolute; width: 12px; height: 12px;
background: inherit; border: inherit; border-width: 0 2px 2px 0;
display: none;
}
.tbubble[class*="arrow-"]::after { display: block; }
.tbubble.arrow-top::after { top: -7px; left: var(--ax, 50%); transform: translateX(-50%) rotate(-135deg); }
.tbubble.arrow-bottom::after { bottom: -7px; left: var(--ax, 50%); transform: translateX(-50%) rotate(45deg); }
.tbubble.arrow-left::after { left: -7px; top: 50%; transform: translateY(-50%) rotate(135deg); }
.tbubble.arrow-right::after { right: -7px; top: 50%; transform: translateY(-50%) rotate(-45deg); }
.tidle { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); font-size: 13px; text-align: center; }
#viewswitch svg { display: block; }
@media (prefers-reduced-motion: reduce) {
* { transition: none !important; animation: none !important; }
}
@media (max-width: 640px) {
.search-page.landing { padding-top: 6vh; }
.search-go { padding: .55rem .8rem; }
}