Certain rechnet den Saldo bisher ausschliesslich selbst aus: Anfangsbestand plus alle Buchungen. Stimmt eine Buchung nicht, faellt das erst auf, wenn man von Hand mit dem Auszug vergleicht. Der von der Bank gemeldete Saldo wird jetzt mitgefuehrt und danebengestellt. Weicht er ab, fehlt oder doppelt eine Buchung – und man sieht es auf der Kontenseite und in den Einstellungen sofort, statt es zu suchen. Banken melden mehrere Saldi nebeneinander; ausgewaehlt wird der gebuchte, denn nur der ist mit Certains Rechnung vergleichbar. Ein verfuegbarer Saldo enthaelt Vorgemerktes oder einen Ueberziehungsrahmen. Die Saldi kommen auch fuer noch nicht zugeordnete Konten: Bei mehreren Revolut-Pockets ist der Betrag oft das Einzige, woran man sie unterscheidet. Ein Fehler bei einem Konto beendet den Durchlauf nicht. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1156 lines
24 KiB
CSS
1156 lines
24 KiB
CSS
/* Eigene Stil-Ergänzungen für Certain Finance – bewusst getrennt von der aus
|
||
der Vorlage übernommenen styles.css, damit Basis und Ergänzung sauber bleiben. */
|
||
|
||
.brand-mark {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
}
|
||
.brand-logo {
|
||
display: block;
|
||
flex: 0 0 auto;
|
||
}
|
||
.brand-name {
|
||
font-weight: 650;
|
||
font-size: 1rem;
|
||
letter-spacing: -0.01em;
|
||
color: var(--text);
|
||
}
|
||
|
||
/* Beträge: Vorzeichenfarbe. Grün = Einnahme/im Budget, Rot = Ausgabe/über Budget. */
|
||
.amount.pos { color: var(--ok); }
|
||
.amount.neg { color: var(--danger); }
|
||
.amount.muted { color: var(--muted); }
|
||
|
||
/* Umgerechneter Zweitbetrag (Originalwährung -> Hauptwährung) klein und gedämpft. */
|
||
.amount-converted {
|
||
color: var(--muted);
|
||
font-size: 0.82em;
|
||
margin-left: 4px;
|
||
}
|
||
|
||
/* Schlichtes zentriertes Layout für Login / Passwort ändern. */
|
||
.auth-screen {
|
||
min-height: 100vh;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: var(--sp-5);
|
||
background: var(--bg);
|
||
}
|
||
.auth-card {
|
||
width: 100%;
|
||
max-width: 380px;
|
||
}
|
||
.auth-card .brand-mark {
|
||
justify-content: center;
|
||
margin-bottom: var(--sp-4);
|
||
}
|
||
|
||
/* Eingabefeld mit angehängtem Icon-Knopf (z. B. Passwort ein-/ausblenden). */
|
||
.input-with-button {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
}
|
||
.input-with-button > input {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
}
|
||
|
||
/* Flex-Kinder dürfen schrumpfen – sonst drängt ein 100%-breites select/input
|
||
ein danebenliegendes Feld auf 0 (das war der Grund für das winzige IBAN-Feld). */
|
||
.row > * {
|
||
min-width: 0;
|
||
}
|
||
|
||
/* Leerer Zustand (z. B. Dashboard ohne Karten). */
|
||
.empty-state {
|
||
text-align: center;
|
||
color: var(--muted);
|
||
padding: var(--sp-6) var(--sp-4);
|
||
}
|
||
.empty-state .icon {
|
||
opacity: 0.5;
|
||
margin-bottom: var(--sp-3);
|
||
}
|
||
|
||
/* Farbiges Icon-Plättchen (Konten/Kategorien). */
|
||
.icon-chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: var(--radius-sm);
|
||
flex: 0 0 auto;
|
||
}
|
||
|
||
/* Betrag + umgerechneter Wert (rechtsbündig gestapelt). */
|
||
.money {
|
||
display: inline-flex;
|
||
flex-direction: column;
|
||
align-items: flex-end;
|
||
line-height: 1.3;
|
||
}
|
||
.money .amount {
|
||
font-variant-numeric: tabular-nums;
|
||
font-weight: 560;
|
||
}
|
||
|
||
/* Icon-Auswahlgitter. */
|
||
.icon-picker {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: var(--sp-2);
|
||
margin-top: 5px;
|
||
}
|
||
.icon-picker-item {
|
||
width: 38px;
|
||
height: 38px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: var(--radius-sm);
|
||
background: var(--surface);
|
||
color: var(--muted);
|
||
cursor: pointer;
|
||
transition: border-color .12s, color .12s, background .12s;
|
||
}
|
||
.icon-picker-item:hover {
|
||
color: var(--text);
|
||
background: var(--surface-2);
|
||
}
|
||
.icon-picker-item.selected {
|
||
background: var(--accent-soft);
|
||
}
|
||
|
||
/* Farbwähler-Zeile. */
|
||
.color-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
margin-top: 5px;
|
||
}
|
||
.color-row input[type="color"] {
|
||
width: 44px;
|
||
height: 34px;
|
||
padding: 2px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* Name + Icon nebeneinander (Listen). */
|
||
.named {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
min-width: 0;
|
||
}
|
||
.named .named-text {
|
||
min-width: 0;
|
||
}
|
||
.named .named-sub {
|
||
color: var(--muted);
|
||
font-size: 0.8rem;
|
||
}
|
||
|
||
/* Kategorien-Verwaltung: durchgehende Liste in EINER Karte, die Gruppen nur als
|
||
dezente Zwischenüberschrift dazwischen (keine abgesetzten Gruppen-Karten). */
|
||
.cat-section + .cat-section {
|
||
margin-top: var(--sp-5);
|
||
}
|
||
.cat-section-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
padding-bottom: var(--sp-2);
|
||
margin-bottom: 2px;
|
||
border-bottom: 2px solid var(--border-strong);
|
||
}
|
||
.cat-section-name {
|
||
flex: 1;
|
||
min-width: 0;
|
||
font-size: 1.05rem;
|
||
font-weight: 650;
|
||
}
|
||
.cat-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
padding: 9px 4px;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.cat-section .cat-row:last-child {
|
||
border-bottom: none;
|
||
}
|
||
.cat-row:hover {
|
||
background: var(--surface-2);
|
||
}
|
||
.cat-row .grow {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
/* Seitenkopf mit Titel + Aktion rechts. */
|
||
.page-head {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: var(--sp-3);
|
||
margin-bottom: var(--sp-4);
|
||
}
|
||
.page-head h1 {
|
||
margin: 0;
|
||
}
|
||
|
||
/* Konten. */
|
||
.account-total {
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: space-between;
|
||
padding-bottom: var(--sp-3);
|
||
margin-bottom: var(--sp-2);
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.account-total strong {
|
||
font-size: 1.2rem;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.account-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.account-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
padding: var(--sp-3) 0;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.account-row:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
/* Die Kategorie-Auswahl ist `width: 100%` – als Flex-Element heisst das „ich
|
||
brauche die ganze Zeile", und alles daneben wird bis zum Umbruch
|
||
zusammengedrückt. In einer Listenzeile bekommt sie deshalb eine eigene
|
||
Breite, die nur noch schrumpfen darf. */
|
||
.account-row .catsel {
|
||
flex: 0 1 clamp(180px, 26%, 300px);
|
||
width: auto;
|
||
/* Ohne das schrumpft ein Flex-Element nie unter die Breite seines längsten
|
||
Wortes – die Auswahl bliebe breiter als vorgesehen und schöbe die Knöpfe
|
||
aus der Zeile. Der Text darin kürzt sich selbst mit „…". */
|
||
min-width: 0;
|
||
}
|
||
/* Der Abstand darüber ist für Formulare gedacht, wo die Auswahl unter ihrer
|
||
Beschriftung steht. In einer mittig ausgerichteten Zeile verrutscht sie nur. */
|
||
.account-row .catsel-btn {
|
||
margin-top: 0;
|
||
}
|
||
/* Beträge in einer Spalte statt ausgefranst. */
|
||
.account-row .money {
|
||
flex: 0 0 auto;
|
||
min-width: 104px;
|
||
white-space: nowrap;
|
||
}
|
||
/* Der Name nimmt den übrigen Platz. Nicht mit „…" abschneiden: Die Texte
|
||
sollen sich weiterhin vollständig markieren und kopieren lassen. */
|
||
.account-row .named-text {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
}
|
||
|
||
@media (max-width: 820px) {
|
||
/* Auf dem Telefon passt das nicht in eine Zeile. Zwei Zeilen: oben Name und
|
||
Betrag, darunter Auswahl und Knöpfe. Die Prozentwerte sind die Bruchstelle
|
||
– sie sind gross genug, dass unten kein Platz mehr für Name und Betrag
|
||
bleibt, und klein genug, dass die Knöpfe neben der Auswahl bleiben. */
|
||
.account-row {
|
||
flex-wrap: wrap;
|
||
row-gap: var(--sp-2);
|
||
}
|
||
.account-row .named-text {
|
||
flex: 1 1 50%;
|
||
}
|
||
.account-row .money {
|
||
min-width: 0;
|
||
}
|
||
.account-row .catsel {
|
||
flex: 1 1 40%;
|
||
}
|
||
}
|
||
.row-actions {
|
||
display: flex;
|
||
gap: 4px;
|
||
flex: 0 0 auto;
|
||
}
|
||
|
||
/* Filterleiste. */
|
||
.filter-bar {
|
||
display: flex;
|
||
gap: var(--sp-3);
|
||
flex-wrap: wrap;
|
||
margin-bottom: var(--sp-4);
|
||
}
|
||
.filter-bar select {
|
||
width: auto;
|
||
min-width: 160px;
|
||
margin-top: 0;
|
||
}
|
||
|
||
/* Segmentierte Umschaltung (Ausgabe/Einnahme/Transfer). */
|
||
.seg {
|
||
display: flex;
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: var(--radius-sm);
|
||
overflow: hidden;
|
||
margin-bottom: var(--sp-4);
|
||
}
|
||
.seg-btn {
|
||
flex: 1;
|
||
padding: 8px 10px;
|
||
border: none;
|
||
background: var(--surface);
|
||
color: var(--muted);
|
||
font-weight: 560;
|
||
cursor: pointer;
|
||
border-right: 1px solid var(--border);
|
||
}
|
||
.seg-btn:last-child {
|
||
border-right: none;
|
||
}
|
||
.seg-btn.active {
|
||
background: var(--accent);
|
||
color: #fff;
|
||
}
|
||
|
||
.nowrap {
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* Nimmt den freien Platz in einer Flex-Zeile und darf schrumpfen (Ellipsis). */
|
||
.grow {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
}
|
||
|
||
/* Nutzer-Chip in der Seitenleisten-Fußzeile ist ein Link auf „Mein Konto". */
|
||
a.sidebar-user {
|
||
text-decoration: none;
|
||
border-radius: var(--radius-sm);
|
||
padding: 2px 4px;
|
||
margin: -2px -4px;
|
||
transition: background .12s;
|
||
}
|
||
a.sidebar-user:hover {
|
||
background: var(--surface-2);
|
||
}
|
||
a.sidebar-user:hover .name {
|
||
color: var(--accent);
|
||
}
|
||
|
||
/* Budget: mehrere Monate nebeneinander. */
|
||
.budget-months {
|
||
display: grid;
|
||
gap: var(--sp-4);
|
||
grid-template-columns: repeat(var(--cols, 1), minmax(0, 1fr));
|
||
align-items: start;
|
||
}
|
||
@media (max-width: 820px) {
|
||
.budget-months {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
.budget-col {
|
||
min-width: 0;
|
||
}
|
||
.budget-col-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
padding-bottom: var(--sp-2);
|
||
margin-bottom: var(--sp-2);
|
||
border-bottom: 2px solid var(--border-strong);
|
||
}
|
||
.budget-col-head strong {
|
||
font-size: 1.05rem;
|
||
}
|
||
.budget-col-income {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
padding: 4px 0 var(--sp-2);
|
||
margin-bottom: var(--sp-2);
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.budget-crow {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
padding: 6px 0;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.budget-crow:last-child {
|
||
border-bottom: none;
|
||
}
|
||
.budget-crow .amount.small {
|
||
flex: 0 0 auto;
|
||
}
|
||
|
||
/* Budget. */
|
||
.budget-navbar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
}
|
||
.budget-month {
|
||
font-size: 1.05rem;
|
||
min-width: 170px;
|
||
text-align: center;
|
||
}
|
||
.flip {
|
||
transform: rotate(180deg);
|
||
}
|
||
.budget-group + .budget-group {
|
||
margin-top: var(--sp-4);
|
||
}
|
||
.budget-group-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
padding: var(--sp-2) 0;
|
||
margin-bottom: 4px;
|
||
border-bottom: 2px solid var(--border-strong);
|
||
}
|
||
.budget-cat {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
padding: 7px 0;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.budget-cat:last-child {
|
||
border-bottom: none;
|
||
}
|
||
.budget-cat-name {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
min-width: 0;
|
||
}
|
||
.budget-cat-bar {
|
||
flex: 1;
|
||
}
|
||
.amount-input {
|
||
width: 96px;
|
||
text-align: right;
|
||
margin-top: 0;
|
||
padding: 5px 8px;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.budget-actual {
|
||
width: 96px;
|
||
text-align: right;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.budget-slash {
|
||
margin: 0 -4px;
|
||
}
|
||
.budget-bar {
|
||
height: 7px;
|
||
background: var(--surface-2);
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
}
|
||
.budget-bar-fill {
|
||
height: 100%;
|
||
background: var(--ok);
|
||
border-radius: 4px;
|
||
transition: width 0.2s;
|
||
}
|
||
.budget-bar-fill.over {
|
||
background: var(--danger);
|
||
}
|
||
.budget-total-row.card-total {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
padding-top: var(--sp-3);
|
||
margin-top: var(--sp-2);
|
||
border-top: 2px solid var(--border-strong);
|
||
}
|
||
tr.budget-total-row td {
|
||
border-top: 2px solid var(--border-strong);
|
||
padding-top: var(--sp-2);
|
||
}
|
||
|
||
/* Sparen-Option im Transfer-Formular. */
|
||
.savings-opt {
|
||
padding: var(--sp-3);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius-sm);
|
||
background: var(--surface-2);
|
||
margin-bottom: var(--sp-4);
|
||
}
|
||
.checkline {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
margin-bottom: var(--sp-2);
|
||
}
|
||
.checkline span {
|
||
color: var(--text);
|
||
font-weight: 560;
|
||
}
|
||
|
||
/* ---------- Dashboard-Grid ---------- */
|
||
.dash-toolbar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
position: relative;
|
||
}
|
||
.dash-add {
|
||
position: relative;
|
||
}
|
||
.dash-add-menu {
|
||
position: absolute;
|
||
right: 0;
|
||
top: calc(100% + 4px);
|
||
z-index: 20;
|
||
background: var(--surface);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: var(--radius-sm);
|
||
box-shadow: var(--shadow);
|
||
min-width: 200px;
|
||
overflow: hidden;
|
||
}
|
||
.dash-add-item {
|
||
display: block;
|
||
width: 100%;
|
||
text-align: left;
|
||
padding: 8px 12px;
|
||
border: none;
|
||
background: none;
|
||
color: var(--text);
|
||
cursor: pointer;
|
||
}
|
||
.dash-add-item:hover {
|
||
background: var(--surface-2);
|
||
}
|
||
|
||
/* Karten-Optionen im Anordnen-Modus (z. B. Gruppen vs. Kategorien). */
|
||
.dash-config-bar {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: var(--sp-3);
|
||
padding-bottom: var(--sp-2);
|
||
margin-bottom: var(--sp-2);
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.dash-config-opt {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
gap: 4px;
|
||
min-width: 0;
|
||
flex: 1 1 140px;
|
||
}
|
||
.seg.sm {
|
||
margin-bottom: 0;
|
||
width: 100%;
|
||
}
|
||
.seg.sm .seg-btn {
|
||
padding: 4px 9px;
|
||
font-size: 0.78rem;
|
||
}
|
||
|
||
.dash-grid {
|
||
position: relative;
|
||
}
|
||
.dash-card {
|
||
background: var(--surface);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius);
|
||
box-shadow: var(--shadow);
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
}
|
||
.dash-card-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
padding: 8px 12px;
|
||
border-bottom: 1px solid var(--border);
|
||
flex: 0 0 auto;
|
||
}
|
||
.dash-card-title {
|
||
font-size: 0.7rem;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
color: var(--muted);
|
||
font-weight: 650;
|
||
flex: 1;
|
||
}
|
||
.dash-grid.editing .dash-card-head {
|
||
cursor: move;
|
||
background: var(--surface-2);
|
||
}
|
||
.dash-remove {
|
||
color: var(--danger);
|
||
}
|
||
.dash-card-body {
|
||
flex: 1;
|
||
min-height: 0;
|
||
overflow: auto;
|
||
padding: var(--sp-3);
|
||
}
|
||
|
||
.dash-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
}
|
||
.dash-line {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
padding: 6px 0;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.dash-line:last-child {
|
||
border-bottom: none;
|
||
}
|
||
.dash-total {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: var(--sp-2);
|
||
padding-top: var(--sp-2);
|
||
margin-top: 2px;
|
||
border-top: 2px solid var(--border-strong);
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.ellipsis {
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.dash-loading, .dash-empty {
|
||
padding: var(--sp-4);
|
||
text-align: center;
|
||
}
|
||
.dash-progress {
|
||
padding: 6px 0;
|
||
}
|
||
.dash-progress-top {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
margin-bottom: 4px;
|
||
}
|
||
.dash-kpis {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
gap: var(--sp-2);
|
||
height: 100%;
|
||
}
|
||
.dash-kpi {
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: space-between;
|
||
}
|
||
.dash-kpi strong {
|
||
font-size: 1.1rem;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
/* react-grid-layout: Platzhalter + Resize-Griff an das Theme angleichen. */
|
||
.react-grid-item.react-grid-placeholder {
|
||
background: var(--accent);
|
||
opacity: 0.15;
|
||
border-radius: var(--radius);
|
||
}
|
||
.react-grid-item > .react-resizable-handle::after {
|
||
border-right-color: var(--border-strong);
|
||
border-bottom-color: var(--border-strong);
|
||
}
|
||
|
||
/* ---------- CSV-Import ---------- */
|
||
.table-scroll {
|
||
overflow-x: auto;
|
||
}
|
||
.raw-table {
|
||
font-size: 0.8rem;
|
||
}
|
||
.raw-table th,
|
||
.raw-table td {
|
||
white-space: nowrap;
|
||
max-width: 220px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.raw-table th {
|
||
color: var(--muted);
|
||
font-weight: 600;
|
||
}
|
||
.map-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
|
||
gap: var(--sp-3);
|
||
margin-top: var(--sp-3);
|
||
}
|
||
.map-grid label {
|
||
margin-bottom: 0;
|
||
}
|
||
tr.row-invalid {
|
||
opacity: 0.45;
|
||
}
|
||
tr.row-dup {
|
||
opacity: 0.55;
|
||
}
|
||
.doc-text {
|
||
max-height: 240px;
|
||
overflow: auto;
|
||
font-size: 0.78rem;
|
||
white-space: pre-wrap;
|
||
color: var(--muted);
|
||
background: var(--surface-2);
|
||
padding: var(--sp-3);
|
||
border-radius: var(--radius-sm);
|
||
margin-top: var(--sp-2);
|
||
}
|
||
/* Beleg-Seiten als Bildstreifen (horizontal scrollbar). */
|
||
.doc-pages {
|
||
display: flex;
|
||
gap: var(--sp-3);
|
||
overflow-x: auto;
|
||
padding-bottom: var(--sp-2);
|
||
}
|
||
.doc-page {
|
||
height: 440px;
|
||
width: auto;
|
||
flex: 0 0 auto;
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius-sm);
|
||
background: #fff;
|
||
}
|
||
.import-more {
|
||
margin-top: var(--sp-3);
|
||
}
|
||
.import-more > summary {
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* DataTable-Zellen einzeilig halten, damit „…" greift (Excel-artig). */
|
||
.datatable td { white-space: nowrap; vertical-align: middle; }
|
||
.datatable td .named-text { min-width: 0; }
|
||
/* Interaktive Zellen (Import-Prüfansicht) füllen ihre Spalte, statt auf 60% zu
|
||
schrumpfen (globale .table td select/input-Regel überschreiben). */
|
||
.datatable td select,
|
||
.datatable td input[inputmode],
|
||
.datatable td input[type="text"] { max-width: none; width: 100%; }
|
||
.datatable td .catsel { width: 100%; }
|
||
|
||
/* ---------- Kategorie-Auswahl (auf-/zuklappbarer Baum, Ersatz für <select>) ---------- */
|
||
.catsel {
|
||
width: 100%;
|
||
}
|
||
.catsel-btn {
|
||
width: 100%;
|
||
margin-top: 5px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
padding: 7px 10px;
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: var(--radius-sm);
|
||
background: var(--surface);
|
||
color: var(--text);
|
||
font: inherit;
|
||
text-align: left;
|
||
cursor: pointer;
|
||
transition: border-color .12s, box-shadow .12s;
|
||
}
|
||
.catsel-btn:hover:not(:disabled) {
|
||
border-color: var(--accent);
|
||
}
|
||
.catsel-btn:disabled {
|
||
opacity: 0.6;
|
||
cursor: default;
|
||
}
|
||
.catsel-btn.open {
|
||
border-color: var(--accent);
|
||
box-shadow: 0 0 0 3px var(--accent-soft);
|
||
}
|
||
.catsel-value {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
}
|
||
.catsel-caret {
|
||
flex: 0 0 auto;
|
||
color: var(--muted);
|
||
}
|
||
|
||
.catsel-panel {
|
||
position: fixed;
|
||
z-index: 1000;
|
||
display: flex;
|
||
flex-direction: column;
|
||
background: var(--surface);
|
||
border: 1px solid var(--border-strong);
|
||
border-radius: var(--radius);
|
||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
|
||
overflow: hidden;
|
||
}
|
||
.catsel-search {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
padding: 8px 10px;
|
||
border-bottom: 1px solid var(--border);
|
||
color: var(--muted);
|
||
flex: 0 0 auto;
|
||
}
|
||
.catsel-search input {
|
||
width: 100%;
|
||
border: none;
|
||
background: none;
|
||
outline: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
color: var(--text);
|
||
font: inherit;
|
||
}
|
||
.catsel-list {
|
||
overflow-y: auto;
|
||
padding: 5px;
|
||
}
|
||
.catsel-group + .catsel-group {
|
||
margin-top: 2px;
|
||
}
|
||
.catsel-group-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
width: 100%;
|
||
padding: 6px;
|
||
border: none;
|
||
background: none;
|
||
cursor: pointer;
|
||
color: var(--muted);
|
||
font-weight: 650;
|
||
font-size: 0.72rem;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.03em;
|
||
}
|
||
.catsel-group-head:hover {
|
||
color: var(--text);
|
||
}
|
||
.catsel-gchev {
|
||
flex: 0 0 auto;
|
||
transition: transform .12s ease;
|
||
}
|
||
.catsel-gchev.collapsed {
|
||
transform: rotate(-90deg);
|
||
}
|
||
.catsel-opt {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
width: 100%;
|
||
padding: 6px 8px 6px 22px;
|
||
border: none;
|
||
border-radius: var(--radius-sm);
|
||
background: none;
|
||
color: var(--text);
|
||
font: inherit;
|
||
text-align: left;
|
||
cursor: pointer;
|
||
}
|
||
.catsel-opt:hover {
|
||
background: var(--surface-2);
|
||
}
|
||
.catsel-opt.selected {
|
||
background: var(--accent-soft);
|
||
color: var(--accent);
|
||
font-weight: 600;
|
||
}
|
||
.catsel-check {
|
||
margin-left: auto;
|
||
flex: 0 0 auto;
|
||
color: var(--accent);
|
||
}
|
||
.catsel-empty {
|
||
padding: 10px;
|
||
}
|
||
|
||
/* ---------- Schlagwörter (Kategorie-Popup + Prüfmeldungen) ---------- */
|
||
.kw-add {
|
||
display: flex;
|
||
gap: var(--sp-2);
|
||
margin-bottom: var(--sp-3);
|
||
}
|
||
.kw-add input {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
margin-top: 0;
|
||
}
|
||
.kw-chips {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: var(--sp-2);
|
||
}
|
||
.kw-chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 4px 6px 4px 11px;
|
||
border-radius: 20px;
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border);
|
||
font-size: 0.85rem;
|
||
}
|
||
.kw-amount {
|
||
color: var(--muted);
|
||
font-variant-numeric: tabular-nums;
|
||
font-size: 0.8em;
|
||
}
|
||
.kw-chip button {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 18px;
|
||
height: 18px;
|
||
padding: 0;
|
||
margin: 0;
|
||
border: none;
|
||
border-radius: 50%;
|
||
background: none;
|
||
color: var(--muted);
|
||
cursor: pointer;
|
||
}
|
||
.kw-chip button:hover {
|
||
background: var(--danger);
|
||
color: #fff;
|
||
}
|
||
/* Schlagwort-Knopf in der Kategorienliste. Belegte Kategorien tragen die
|
||
Akzentfarbe und die Anzahl – so sieht man der Liste an, wo schon etwas
|
||
hinterlegt ist, ohne jede Kategorie einzeln zu öffnen. */
|
||
.kw-btn {
|
||
gap: 5px;
|
||
}
|
||
.kw-btn.belegt {
|
||
color: var(--accent);
|
||
border-color: var(--accent-soft);
|
||
background: var(--accent-soft);
|
||
}
|
||
.kw-btn-zahl {
|
||
font-size: 0.72rem;
|
||
font-weight: 640;
|
||
font-variant-numeric: tabular-nums;
|
||
line-height: 1;
|
||
}
|
||
|
||
/* Das Schlagwort, das gerade oben in der Zeile liegt. Bearbeitet wird dort,
|
||
nicht im Chip – im Chip war für den Betrag kein Platz. */
|
||
.kw-chip.aktiv {
|
||
border-color: var(--accent);
|
||
box-shadow: 0 0 0 2px var(--accent-soft);
|
||
}
|
||
/* Passend dazu die Eingabezeile hervorheben, damit klar ist, dass dort gerade
|
||
ein vorhandenes Wort liegt und nicht ein neues entsteht. */
|
||
.kw-add.bearbeitet input {
|
||
border-color: var(--accent);
|
||
}
|
||
|
||
.kw-flag {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-3);
|
||
padding: var(--sp-3) 0;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.kw-flag:last-child {
|
||
border-bottom: none;
|
||
}
|
||
.kw-flag-main {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
.kw-flag-word {
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* ---------- Passwort: Anforderungsliste, Zwei-Faktor ---------- */
|
||
|
||
/* Nur für Screenreader – optisch weg, aber vorlesbar. */
|
||
.sr-only {
|
||
position: absolute;
|
||
width: 1px;
|
||
height: 1px;
|
||
padding: 0;
|
||
margin: -1px;
|
||
overflow: hidden;
|
||
clip: rect(0 0 0 0);
|
||
white-space: nowrap;
|
||
border: 0;
|
||
}
|
||
|
||
/* Hakt sich beim Tippen ab. Farben ausschliesslich aus Tokens, sonst bricht
|
||
das dunkle Thema. */
|
||
.pw-rules {
|
||
list-style: none;
|
||
margin: var(--sp-2) 0 0;
|
||
padding: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 3px;
|
||
font-size: 0.82rem;
|
||
}
|
||
.pw-rule {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--sp-2);
|
||
color: var(--muted);
|
||
transition: color 0.16s ease;
|
||
}
|
||
.pw-rule.erfuellt {
|
||
color: var(--ok);
|
||
}
|
||
.pw-rule-mark {
|
||
flex: 0 0 auto;
|
||
width: 16px;
|
||
height: 16px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
/* Offener Punkt, solange die Regel nicht erfüllt ist. */
|
||
.pw-rule-dot {
|
||
width: 7px;
|
||
height: 7px;
|
||
border-radius: 50%;
|
||
border: 1.5px solid currentColor;
|
||
opacity: 0.55;
|
||
}
|
||
/* Kurzer Impuls, wenn das Häkchen erscheint. */
|
||
.pw-rule.erfuellt .pw-rule-mark {
|
||
animation: pw-rule-pop 0.18s ease-out;
|
||
}
|
||
|
||
@keyframes pw-rule-pop {
|
||
from { transform: scale(0.4); opacity: 0; }
|
||
60% { transform: scale(1.18); opacity: 1; }
|
||
to { transform: scale(1); opacity: 1; }
|
||
}
|
||
|
||
/* styles.css hat keinen globalen Bewegungs-Reset, nur eine Aufzählung –
|
||
deshalb hier noch einmal ausdrücklich für die neuen Bewegungen. */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.pw-rule { transition: none; }
|
||
.pw-rule.erfuellt .pw-rule-mark { animation: none; }
|
||
}
|
||
|
||
/* Zwei-Faktor: QR-Code, Secret und Wiederherstellungscodes. */
|
||
.tfa-qr {
|
||
display: block;
|
||
width: 190px;
|
||
height: 190px;
|
||
/* Der QR-Code braucht weissen Grund, damit er auch im dunklen Thema
|
||
zuverlässig gescannt wird. */
|
||
background: #fff;
|
||
padding: var(--sp-2);
|
||
border-radius: var(--radius-sm);
|
||
border: 1px solid var(--border);
|
||
}
|
||
.tfa-secret {
|
||
display: inline-block;
|
||
padding: 6px var(--sp-2);
|
||
border-radius: var(--radius-sm);
|
||
background: var(--surface-2);
|
||
border: 1px solid var(--border);
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||
font-size: 0.9rem;
|
||
letter-spacing: 0.06em;
|
||
/* styles.css setzt body { user-select: none } – ohne das hier liesse sich
|
||
das Secret nicht markieren und kopieren. */
|
||
user-select: text;
|
||
}
|
||
.tfa-codes {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||
gap: var(--sp-2);
|
||
margin: var(--sp-3) 0;
|
||
padding: var(--sp-3);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius-sm);
|
||
background: var(--surface-2);
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||
font-size: 0.9rem;
|
||
user-select: text;
|
||
}
|
||
.tfa-codes .verbraucht {
|
||
color: var(--muted);
|
||
text-decoration: line-through;
|
||
}
|
||
|
||
/* Zusammenfassung der Prüfansicht beim Wiederherstellen. */
|
||
.restore-summary {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin-top: var(--sp-2);
|
||
font-size: 0.88rem;
|
||
}
|
||
.restore-summary td {
|
||
padding: 3px 0;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.restore-summary td:last-child {
|
||
text-align: right;
|
||
font-variant-numeric: tabular-nums;
|
||
color: var(--muted);
|
||
}
|
||
.restore-summary tr:last-child td {
|
||
border-bottom: none;
|
||
}
|
||
|
||
/* Zweiter Buchen-Knopf unter der Prüfliste – bei vielen Zeilen ist der obere
|
||
längst aus dem Bild gescrollt. */
|
||
.commit-footer {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
padding-top: var(--sp-3);
|
||
margin-top: var(--sp-2);
|
||
border-top: 1px solid var(--border);
|
||
}
|
||
|
||
/* Ausgaben-Anteile: der Prozentwert ist die Kernaussage der Karte und steht
|
||
deshalb vor dem Betrag, mit gleichbleibender Ziffernbreite. */
|
||
.share-pct {
|
||
font-variant-numeric: tabular-nums;
|
||
white-space: nowrap;
|
||
}
|
||
/* Wird die Karte schmal, gibt zuerst der Betrag nach – ohne den Kategorienamen
|
||
sagt die Zeile nichts mehr aus, ohne den Betrag bleibt der Anteil lesbar. */
|
||
.share-row .share-name {
|
||
min-width: 4.5rem;
|
||
}
|
||
.share-row .share-amount {
|
||
flex: 0 1 auto;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* Abgleich des selbst gerechneten Saldos mit dem, den die Bank fuehrt.
|
||
Eine Abweichung ist kein Fehler der Bank, sondern ein Hinweis auf eine
|
||
fehlende oder doppelte Buchung – deshalb warnend, nicht rot. */
|
||
.saldo-ok { color: var(--ok); }
|
||
.saldo-abweichung { color: var(--warn); font-weight: 560; }
|