/* ── CHART AREA ── */
.chart-wrap {
	padding-block: 12px;
	padding-inline: 14px;
	position: relative;
}
.chart-wrap canvas {
	width: 100% !important;
}
.chart-wrap-lg {
	height: 220px;
}
.chart-wrap-md {
	height: 200px;
}
.chart-wrap-sm {
	height: 160px;
}

.timefilter {
	display: flex;
	gap: 2px;
}
.tf {
	font-family: var(--fm);
	font-size: var(--fs-hint);
	padding-block: 3px;
	padding-inline: 7px;
	border-radius: var(--radius);
	color: var(--t3);
	background: transparent;
	border: none;
}
.tf.active {
	background: var(--bg3);
	color: var(--t1);
}
@media (hover: hover) and (pointer: fine) {
	.tf:hover:not(.active) {
		color: var(--t2);
	}
}

/* ── TRANSACTIONS ── */
.txn-table-wrap {
	overflow-x: auto;
	overflow-y: auto;
	/* Kopfleisten-Gruppe (Topbar inkl. Safe-Area-Top-Inset + Navtabs) +
	   page-padding (24px) + Kartenüberschrift (~48px). dvh statt vh, wie
	   #main-app/.device-root: mit eingeblendeter URL-Leiste auf iOS-Safari
	   ist vh grösser als die tatsächlich sichtbare Fläche, das Budget wäre
	   dort zu grosszügig. */
	max-height: calc(100dvh - var(--chrome-h) - 72px);
}
table.txns {
	width: 100%;
	/* design-system.md Fallstrick #1: separate + border-spacing:0, never
	   collapse — collapse renders a visibly different line color/weight than
	   every other redesigned table in the app. */
	border-collapse: separate;
	border-spacing: 0;
}
table.txns th {
	/* Klebt an der Oberkante von .txn-table-wrap — das ist ein eigener,
	   verschachtelter Scroller, der Haftpunkt ist also dessen Kante und nicht
	   die Kopfleiste (deshalb top:0, anders als bei .card-h).
	   Spiegelbild des bereits bestehenden sticky tfoot weiter unten. */
	position: sticky;
	top: 0;
	z-index: var(--z-raised);
	/* Muss decken: ohne eigenen Hintergrund scheinen die darunter
	   wegscrollenden Zeilen durch den fixierten Kopf. Gehört auf die th, nicht
	   auf thead — unter border-collapse:separate ist ein Hintergrund auf der
	   Zeilengruppe in Safari unzuverlässig (gleicher Grund wie beim tfoot). */
	background: var(--bg);
	/* 0.875rem = the app's own existing 14px inline value (design-system.md's
	   "app-specific for 3/5-column layouts" rationale applies here too, this
	   table has 11), just expressed in rem; vertical rhythm unified with every
	   other redesigned table (0.5rem body / 0.6rem header-bottom) instead of
	   this table's own one-off 7/8px (Nutzerwunsch 10.08.2026). */
	padding: 0 0.875rem 0.6rem;
	font-size: var(--fs-table-head);
	font-weight: 400;
	letter-spacing: 0.07em;
	text-transform: uppercase;
	color: var(--t3);
	/* design-system.md: ONE line value app-wide for tables — 1px var(--line),
	   never --line2 or 0.5px (that split was the actual source of the
	   perceived-inconsistent-line-color bug the redesign fixed elsewhere). */
	border-bottom: 1px solid var(--line);
	text-align: left;
	white-space: nowrap;
}
table.txns th:last-child {
	text-align: right;
}
/* Flush against the card's own edges, matching every other redesigned table
   (Nutzerwunsch 10.08.2026: "noch padding" on this table) — the 0.875rem
   inline padding above stays for the gaps BETWEEN columns (this table has 11,
   unlike the few wide 25%-columns tables that need none at all), it's only
   the outer edges that shouldn't be inset from the card. */
table.txns th:first-child,
table.txns td:first-child {
	padding-left: 0;
}
table.txns th:last-child,
table.txns td:last-child {
	padding-right: 0;
}
table.txns th.txn-sortable {
	cursor: pointer;
	user-select: none;
}
@media (hover: hover) and (pointer: fine) {
	table.txns th.txn-sortable:hover {
		color: var(--t2);
	}
}
table.txns td {
	padding: 0.5rem 0.875rem;
	font-size: var(--fs-table-body);
	border-bottom: 1px solid var(--line);
}
table.txns tr:last-child td {
	border-bottom: none;
}
@media (hover: hover) and (pointer: fine) {
	table.txns tr:hover td {
		background: var(--bg-hover);
	}
}
table.txns td.mono {
	font-family: var(--fm);
	font-variant-numeric: tabular-nums;
}
table.txns td.r {
	text-align: right;
	font-family: var(--fm);
	font-variant-numeric: tabular-nums;
}
table.txns th.r {
	text-align: right;
}
.txn-empty {
	text-align: center;
	padding: 24px;
	color: var(--t3);
}
/* R240: placeholder shown when a tab has no position data to render (e.g. a
   non-admin/kiosk view before any position has been created). */
.empty-state {
	text-align: center;
	padding: 40px 24px;
	color: var(--t3);
}
.txn-note {
	color: var(--t3);
	font-size: 11px;
}
.txn-actions {
	text-align: right;
	white-space: nowrap;
}
.txn-ca-label {
	margin-left: 8px;
	color: var(--t2);
	font-size: 12px;
}
.txn-closed-banner {
	padding-block: 0.5rem;
	padding-inline: 0.875rem;
	font-size: 12px;
	color: var(--t2);
	background: var(--bg2);
	border-bottom: 0.5px solid var(--line2);
}

/* ── FORMS ── */
.form-grid {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 10px;
	padding: 16px;
}
.span-all {
	grid-column: 1 / -1;
}
.form-grid label {
	font-size: var(--fs-hint);
	font-weight: 400;
	letter-spacing: 0.05em;
	text-transform: uppercase;
	color: var(--t3);
	display: block;
	margin-bottom: 4px;
}
input[type='text'],
input[type='number'],
input[type='date'],
input[type='password'],
select {
	width: 100%;
	padding-block: 8px;
	padding-inline: 10px;
	background: var(--bg2);
	border: 0.5px solid var(--line);
	border-radius: var(--radius);
	color: var(--t1);
	/* min 16px, sonst löst iOS Safari beim Fokussieren Auto-Zoom aus
	   (--fs-prominent kann seit der --fs-5-Verkleinerung 21.09.2026 auf
	   schmalen Viewports bis 15.6px sinken). */
	font-size: max(16px, var(--fs-prominent));
	font-family: var(--ff);
	line-height: 1.3;
	height: calc(1.3em + 17px);
	transition: border-color var(--dur-fast), box-shadow var(--dur-fast);
}
@media (hover: hover) and (pointer: fine) {
	input[type='text']:hover,
	input[type='number']:hover,
	input[type='date']:hover,
	input[type='password']:hover,
	select:hover {
		border-color: var(--t3);
	}
}
input::placeholder {
	color: var(--t3);
	opacity: 1;
}
select {
	appearance: none;
	-webkit-appearance: none;
	-moz-appearance: none;
	cursor: pointer;
	padding-right: 26px;
	background-repeat: no-repeat;
	background-image:
		linear-gradient(45deg, transparent 50%, var(--t3) 50%),
		linear-gradient(135deg, var(--t3) 50%, transparent 50%);
	background-position:
		right 16px center,
		right 12px center;
	background-size: 5px 5px, 5px 5px;
	transition: border-color var(--dur-fast), background-color var(--dur-fast);
}
input:focus {
	outline: none;
	border-color: var(--blue);
	box-shadow: 0 0 0 3px var(--blue-bg);
}
/* Select-Feld + Chevron sollen bei Fokus/offenem Zustand als EIN
   zusammengehöriges Element erkennbar sein (Nutzerwunsch 18.08.2026,
   präzisiert 18.08.2026: kein Ring, keine Border-Farbänderung — nur der
   ganze Hintergrund, inkl. Chevron-Fläche, wird heller). Border bleibt
   unverändert auf --line, wie im Ruhezustand. */
select:focus {
	outline: none;
	background-color: var(--bg-hover);
}
input:disabled,
select:disabled {
	opacity: 0.5;
	cursor: not-allowed;
	background: var(--bg3);
}
input[type='checkbox'] {
	accent-color: var(--t1);
	width: 14px;
	height: 14px;
	cursor: pointer;
}
/* Ein Button-Charakter für die ganze App (Nutzerwunsch 18.08.2026, finale
   Variante B von 3 vorgeschlagenen Optionen — siehe design-system.md
   "Buttons"): die ursprüngliche Vollinversion (--t1-Hintergrund) wurde im
   Dark Mode zu einer grellen, fast weissen Fläche; eine erste Korrektur mit
   --blue/--blue-bg wurde verworfen, weil Blau im Projekt sonst nirgends als
   Button-Farbe vorkommt (nur Link-/Info-Akzent). Jetzt eine getönte Fläche
   in Graustufe (--bg3) statt Farbe — bleibt in beiden Themes dezent, ohne
   eine neue Akzentfarbe einzuführen. */
.btn-primary {
	width: 100%;
	padding: 10px;
	background: var(--bg3);
	color: var(--t1);
	border: none;
	border-radius: var(--radius);
	font-size: var(--fs-prominent);
	font-weight: 400;
	margin-top: 4px;
}
/* filter statt eines dedizierten Hover-Tokens: verdunkelt die Fläche in
   BEIDEN Themes einheitlich (in Dark Mode ist --bg3 bereits sehr dunkel,
   ein hellerer Hover-Token wie --bg-hover würde die Fläche fälschlich
   Richtung Seitenhintergrund auflösen statt sie hervorzuheben). */
@media (hover: hover) and (pointer: fine) {
	.btn-primary:hover {
		filter: brightness(0.94);
	}
}
.btn-sm {
	padding-block: 5px;
	padding-inline: 12px;
	font-size: var(--fs-hint);
	border: 0.5px solid var(--line);
	border-radius: var(--radius);
	background: transparent;
	color: var(--t2);
}
@media (hover: hover) and (pointer: fine) {
	.btn-sm:hover {
		background: var(--bg2);
		color: var(--t1);
	}
}
/* Eigener Hover statt des generischen .btn-sm:hover (der auf --t1/--bg2
   umschaltet und damit die Warnfarbe beim Hovern verlieren würde) — bleibt
   in der Warnfarbe getönt, analog zu .btn-primary. */
.btn-danger {
	color: var(--neg);
	border-color: var(--neg-bg);
}
@media (hover: hover) and (pointer: fine) {
	.btn-danger:hover {
		background: var(--neg-bg);
		color: var(--neg);
	}
}

/* ── LOGIN ── */
.login-screen {
	height: 100dvh; /* R249: same iOS Safari URL-bar reasoning as #app/#main-app in layout.css */
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--bg2);
}
.login-card {
	width: 320px;
	display: flex;
	flex-direction: column;
	gap: 12px;
}
.login-card input {
	background: var(--bg);
}
.login-error {
	font-size: var(--fs-form-label);
	color: var(--neg);
	background: var(--neg-bg);
	padding-block: 8px;
	padding-inline: 10px;
	border-radius: var(--radius);
}
/* ── MODAL ── */
.modal-overlay {
	position: fixed;
	inset: 0;
	background: var(--overlay);
	z-index: var(--z-overlay);
}
.modal {
	position: fixed;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	background: var(--bg);
	border: 0.5px solid var(--line);
	border-radius: var(--radius);
	width: min(480px, 95vw);
	max-height: 80vh;
	overflow-y: auto;
	z-index: var(--z-modal);
}
.modal-h {
	padding-block: 14px;
	padding-inline: 18px;
	border-bottom: 0.5px solid var(--line);
	display: flex;
	align-items: center;
	justify-content: space-between;
}
.modal-title {
	font-size: var(--fs-modal-title);
	font-weight: 400;
}
.modal-close {
	width: 24px;
	height: 24px;
	background: none;
	border: none;
	color: var(--t3);
	font-size: 16px;
	display: flex;
	align-items: center;
	justify-content: center;
	cursor: pointer;
}
/* Bisher kein Hover-Feedback — angeglichen an .icon-btn:hover (reine
   Textfarbe, kein Hintergrund/Rahmen, da beide dasselbe flache
   Icon-Button-Prinzip ohne eigene Fläche verfolgen). */
@media (hover: hover) and (pointer: fine) {
	.modal-close:hover {
		color: var(--t1);
	}
}

/* ── HEADER-DROPDOWN ANCHOR POSITIONING (alle vier Trigger-Dropdowns) ──
   Rein deklarativ in CSS (Baseline seit Safari 26, siehe AGENTS.md-Browser-
   Baseline / "CSS vor JS wo möglich") — kein JS mehr nötig, weder für die
   Anchor-Zuordnung noch fürs Nachführen bei Resize (Vorgänger bis
   02.09.2026: JS-Funktion anchorDropdown() setzte einmalig px-Werte per
   getBoundingClientRect(), hing danach bei offenem Dropdown + Fenster-Resize
   nicht mehr am richtigen Icon). Jeder Trigger bekommt einen festen
   `anchor-name`; da immer nur einer der beiden Trigger (normal/Geräte-View)
   sichtbar ist (#main-app vs #device-app), gibt es nie zwei gleichzeitig
   gerenderte Elemente mit demselben Namen - der unsichtbare Zwilling hat
   schlicht keine Box und zählt nicht als Anchor. `top: anchor(bottom);
   left: anchor(left);` dockt bündig unter die linke untere Trigger-Ecke an
   (kein Offset, keine Animation — Nutzerwunsch 18.08.2026);
   `position-try-fallbacks: flip-inline` übernimmt den Rechtsbündig-Flip bei
   zu wenig Platz rechts (vorher manuell in JS berechnet). */
#position-selector,
#device-position-selector {
	anchor-name: --position-dropdown-anchor;
}
#holidays-btn,
#device-holidays-btn {
	anchor-name: --holidays-dropdown-anchor;
}
/* R330: sechster Trigger (Glocke-Dropdown, js/app.js) - gleiches Muster wie
   die vier obigen. */
#push-btn,
#device-push-btn {
	anchor-name: --push-dropdown-anchor;
}
.view-switch-trigger {
	anchor-name: --view-switch-dropdown-anchor;
}
.hamburger-btn {
	anchor-name: --hamburger-dropdown-anchor;
}
/* Fünfter Trigger (10.09.2026, Chart-Einstellungen), einziger nicht in der
   Topbar - liegt in .intraday-module (js/views/overview.js), sonst gleiches
   Muster wie die vier obigen: fester anchor-name, kein JS-Nachführen nötig. */
.intraday-controls-trigger {
	anchor-name: --intraday-controls-dropdown-anchor;
}
.position-dropdown {
	position-anchor: --position-dropdown-anchor;
}
.holidays-dropdown {
	position-anchor: --holidays-dropdown-anchor;
}
.push-dropdown {
	position-anchor: --push-dropdown-anchor;
}
.view-switch-dropdown {
	position-anchor: --view-switch-dropdown-anchor;
}
.hamburger-dropdown {
	position-anchor: --hamburger-dropdown-anchor;
}
.intraday-controls-dropdown {
	position-anchor: --intraday-controls-dropdown-anchor;
}
/* Zusätzliche @position-try-Fallbacks (07.09.2026, Nutzerfund: Holidays-
   Dropdown ging auf dem iPhone über den rechten Rand hinaus) - flip-inline
   allein spiegelt nur die Ausrichtung relativ zum Anchor (rechtsbündig statt
   linksbündig zum Trigger), garantiert aber keine absolute Position
   innerhalb des Viewports. Der Holidays-Button sitzt (responsiver
   Header-Kollaps) bewusst immer sichtbar ganz rechts in der Topbar, nah am
   Viewport-Rand - dort kann auch die geflippte Variante noch überlaufen.
   Die beiden --dropdown-*-edge-Tries pinnen die Box explizit mit festem
   Abstand an den Viewport-Rand statt relativ zum Anchor und greifen nur,
   wenn auch flip-inline noch überläuft - normales Verhalten der drei
   anderen, bereits unauffälligen Dropdowns bleibt unverändert. */
@position-try --dropdown-right-edge {
	left: auto;
	right: 8px;
}
@position-try --dropdown-left-edge {
	left: 8px;
}
.position-dropdown,
.holidays-dropdown,
.push-dropdown,
.view-switch-dropdown,
.hamburger-dropdown {
	top: anchor(bottom);
	left: anchor(left);
	position-try-fallbacks: flip-inline, --dropdown-right-edge, --dropdown-left-edge;
}
/* Öffnet nach OBEN statt nach unten (Nutzerwunsch 10.09.2026: Trigger sitzt
   unter dem Chart, nicht in der Topbar - ein nach unten öffnendes Panel
   hätte dort ins Leere/unter den sichtbaren Inhalt gezeigt). bottom statt
   top als Anker-Kante, sonst identisches Muster (kein Lückenabstand,
   linksbündig, dieselbe Fallback-Kette für den Fall, dass auch die
   Horizontal-Ausrichtung überläuft). */
.intraday-controls-dropdown {
	bottom: anchor(top);
	left: anchor(left);
	position-try-fallbacks: flip-inline, --dropdown-right-edge, --dropdown-left-edge;
}

/* ── POSITION DROPDOWN ── */
/* Dockt flush unter der Topbar an (kein Lückenabstand), linksbündig zum
   Trigger. Keine Border/Outline — Zusammengehörigkeit mit dem geöffneten
   .position-selector entsteht allein über den helleren Hintergrund
   (Nutzerwunsch 18.08.2026, gilt als Prinzip für alle Dropdowns/Selects). */
/* Verankerung (top/left, Rechtsbündig-Flip bei Platzmangel) läuft komplett
   über die gemeinsame Anchor-Positioning-Regel oben, rein CSS (Nutzerwunsch
   18.08.2026: linke obere Ecke des Dropdowns = linke untere
   Ecke des Triggers, ohne Abstand, ohne Animation). */
.position-dropdown {
	position: fixed;
	background: var(--bg-hover);
	border-radius: var(--radius);
	width: max-content;
	max-width: calc(100vw - 32px);
	z-index: var(--z-dropdown);
	overflow: auto;
}
/* Gleiches Tabellen-Prinzip wie .holidays-dropdown (s.u.): EIN gemeinsames
   Grid für die Zeilen statt eines eigenen Grids pro Zeile — sonst sizt
   jede .pos-item-Zeile ihre Spalten unabhängig von den anderen
   (Nutzerfeedback 18.08.2026: dadurch wanderte die zweite Spalte pro Zeile
   unterschiedlich breit, sah aus wie zentriert statt linksbündig). Zeilen
   (.pos-item) werden dafür zu `display:contents` — ihre Kinder (.sym/.nm)
   werden direkte Grid-Items dieses EINEN Grids, Spaltenbreiten bleiben
   dadurch über alle Zeilen hinweg exakt gleich. EIGENER Grid-Container statt
   direkt auf .position-dropdown: .pos-share spannt beide Spalten und sein
   langer, nicht umbrechender Text hätte sonst als spannendes Grid-Item die
   "auto"-Spaltenbreiten mit aufgebläht — die Namensspalte landete dadurch
   sichtbar weiter rechts als im Trigger-Button darüber (Nutzerfeedback
   19.08.2026: Text im Dropdown soll wie im Button selbst ausgerichtet
   sein). Aussen vor gelassen, sizt das Grid nur noch nach den tatsächlichen
   Zeileninhalten. */
.pos-list {
	display: grid;
	/* auto statt minmax(4em, ...): der Trigger (.position-selector) hat
	   keine Mindestbreite fürs Symbol, Name folgt direkt im festen 6px-
	   Flex-Gap danach. column-gap 6px matcht exakt den Trigger-Gap. */
	grid-template-columns: auto auto;
	column-gap: 6px;
	/* Ohne eigene width strecken sich die "auto"-Spalten sonst auf die
	   volle Breite von .position-dropdown (dessen width:max-content sich am
	   breiteren .pos-share-Text orientiert) — Spalte 1 würde dadurch über
	   ihren tatsächlichen Inhalt hinaus aufgebläht, die Namensspalte landet
	   sichtbar weiter rechts als im Trigger. width:max-content hier sorgt
	   dafür, dass sich das Grid nur an seinem eigenen Inhalt orientiert. */
	width: max-content;
}
.pos-item {
	display: contents;
	cursor: pointer;
	white-space: nowrap;
}
.pos-item .sym {
	padding-block: 10px;
	padding-inline-start: 14px;
	font-size: var(--fs-prominent);
	/* 700 statt 500 - matcht .position-symbol im Trigger (Nutzerfeedback
	   19.08.2026, s.o.). */
	font-weight: 400;
	font-family: var(--fm);
}
.pos-item .nm {
	padding-block: 10px;
	padding-inline-end: 14px;
	font-size: var(--fs-hint);
	color: var(--t3);
}
/* Hover innerhalb des Dropdowns ausschliesslich über Schriftfarbe, kein
   Hintergrund (Nutzerwunsch 18.08.2026) — das Dropdown-Panel selbst hebt
   sich bereits über --bg-hover vom Rest der Seite ab, ein zusätzlicher
   Zeilen-Hintergrund würde das doppeln. */
@media (hover: hover) and (pointer: fine) {
	.pos-item:hover .nm {
		color: var(--t2);
	}
}
/* Nur relevant, wenn die aktuell gewählte Position die einzige verfügbare
   ist (siehe renderPositionDropdown()-Filter in js/app.js, Nutzerwunsch
   19.08.2026: gewählte Position nicht nochmal im Dropdown auflisten) —
   sonst stünde ein Nicht-Admin vor einem leeren Panel. */
.pos-empty {
	padding-block: 10px;
	padding-inline: 14px;
	color: var(--t3);
}
/* "Link teilen" (Read-only Share-Link, Nutzerwunsch 24.07.2026) - sieht aus
   wie ein echter Button (--t2/--t1, font-weight 600, wie .btn-primary), nicht
   mehr wie ein Link (kein --blue mehr, siehe design-system.md "Buttons").
   Hover bleibt aber Farbe-only wie der Rest dieses Dropdowns. Kein Grid-Item
   mehr (s.o. .pos-list) — eigene volle Zeile per Block-Layout, ohne die
   Spaltenbreiten der Items zu beeinflussen. Der frühere Nachbar-Eintrag
   "+ Position hinzufügen" (.pos-add) wurde 03.09.2026 entfernt (redundant
   zum "+ Position"-Button im Admin-Bereich) — border-top hier übernommen,
   da .pos-share jetzt direkt auf .pos-list folgt. */
.pos-share {
	padding-block: 10px;
	padding-inline: 14px;
	font-size: var(--fs-hint);
	font-weight: 400;
	color: var(--t2);
	cursor: pointer;
	white-space: nowrap;
	border-top: 0.5px solid var(--line2);
	display: flex;
	align-items: center;
	gap: 6px;
}
@media (hover: hover) and (pointer: fine) {
	.pos-share:hover {
		color: var(--t1);
	}
}
/* Share-Link-Liste im "Link teilen"-Modal (js/app.js openShareLinkModal()) */
/* Zwei feste Zeilen statt einer umbrechenden Flex-Zeile: Notizfeld oben über
   die volle Breite, darunter Datum links und die beiden Buttons rechts. Alle
   vier Elemente einzeilig nebeneinander wäre im Modal (max-width) immer zu
   eng geworden — mit `flex-wrap` sprang dann je nach Notizlänge mal der eine,
   mal der andere Button in die nächste Zeile. Das Grid legt den Umbruch fest,
   statt ihn vom Inhalt abhängen zu lassen. */
.share-link-row {
	display: grid;
	grid-template-columns: 1fr auto auto;
	align-items: center;
	gap: 6px 8px;
	padding-block: 10px;
	border-bottom: 0.5px solid var(--line2);
}
.share-link-row:last-child {
	border-bottom: none;
}
/* Notiz pro Link ("für wen ist der Link?"). Basis-Styling (Rahmen/Radius/
   Farben) kommt vom globalen input[type='text'] oben. */
.share-link-note {
	grid-column: 1 / -1;
}
.share-link-date {
	font-size: var(--fs-hint);
	color: var(--t3);
	white-space: nowrap;
}

/* ── HOLIDAYS DROPDOWN ── */
/* Gleiches Prinzip wie .position-dropdown (s.o.): EIN gemeinsames Grid auf
   dem Panel selbst statt eines eigenen Grids pro .holiday-item-Zeile
   (Nutzerfeedback 18.08.2026: unabhängige Zeilen-Grids sizten ihre
   Spalten unterschiedlich breit, je nach Zeileninhalt — sah aus wie
   zentriert statt linksbündig, weil v.a. die Datumsspalte pro Zeile
   wanderte). Zeilen (.holiday-item) werden dafür zu `display:contents`. */
.holidays-dropdown {
	position: fixed;
	background: var(--bg-hover);
	border-radius: var(--radius);
	display: grid;
	grid-template-columns: 2.2em minmax(5.5em, auto) auto;
	/* Fixer 5px-Spaltenabstand (Nutzerwunsch 02.09.2026) - das vorherige
	   0.3em (Nutzerwunsch 18.08.2026, "so schmal wie ein einzelnes
	   Leerzeichen") fiel auf dem Grid-Containers eigener Font-Size fast
	   ganz zusammen, deutlich schmaler als beabsichtigt. */
	column-gap: 5px;
	row-gap: 0;
	align-items: baseline;
	/* Breite folgt dem Inhalt statt eines festen max-width (Nutzerwunsch
	   18.08.2026: keine Zeile darf umbrechen) — .holiday-item erzwingt
	   unten white-space:nowrap, das Panel muss also so breit werden
	   dürfen, wie die längste Zeile es braucht, gedeckelt nur vom
	   Viewport-Rand. */
	width: max-content;
	max-width: calc(100vw - 32px);
	/* Kein fixer px-Deckel mehr (Nutzerwunsch 19.08.2026: bei genug
	   vertikalem Platz keinen Scrollbalken erzwingen) — max-height ist
	   ohnehin nur eine Obergrenze, die Liste wächst dadurch nicht künstlich,
	   scrollt aber erst, wenn der Viewport wirklich zu klein ist. */
	max-height: calc(100dvh - var(--topbar-h-total) - 24px);
	overflow-y: auto;
	overflow-x: auto;
	z-index: var(--z-dropdown);
}
.holiday-item {
	display: contents;
	white-space: nowrap;
}
.holiday-weekday,
.holiday-date,
.holiday-name {
	padding-block: 8px;
	border-bottom: 0.5px solid var(--line2);
}
.holiday-weekday {
	padding-inline-start: 14px;
}
.holiday-name {
	padding-inline-end: 14px;
}
.holiday-item:last-child .holiday-weekday,
.holiday-item:last-child .holiday-date,
.holiday-item:last-child .holiday-name {
	border-bottom: none;
}
.holiday-weekday,
.holiday-date {
	/* Basisgrösse wie .holiday-name (Nutzerwunsch 19.08.2026) - font-weight
	   bleibt bewusst 400 (vs. .holiday-name 500), sonst wären Datum und Name
	   optisch nicht mehr unterscheidbar. R331 (18.09.2026, Nutzerwunsch
	   "einfach die body font grösse nehmen"): --fs-body statt --fs-form-label. */
	font-size: var(--fs-body);
	font-variant-numeric: tabular-nums;
}
.holiday-weekday {
	/* Wochentag bleibt gedämpft (Nutzerkorrektur 19.08.2026: nur das Datum
	   selbst sollte auf --t1 angehoben werden, der Wochentag nicht). Eine
	   Stufe kleiner als Datum/Name (Nutzerwunsch 21.09.2026). */
	font-size: var(--fs-2);
	color: var(--t3);
}
.holiday-date {
	color: var(--t1);
}
.holiday-name {
	font-size: var(--fs-body);
	font-weight: 400;
}
/* Ersetzt die vorherige Amber-Pille — Halbtag-Hinweis steht jetzt als
   normaler, gedimmter Text direkt in derselben Zeile hinter dem Namen
   (Nutzerwunsch 18.08.2026: "einfach 'bis 19 Uhr' anschreiben", MEZ/MESZ
   auf erneuten Nutzerwunsch wieder entfernt; 21.09.2026: Text auf "Close
   19 Uhr" gekürzt, s. js/app.js renderHolidaysDropdown()). R331/21.09.2026:
   als "Zusatz" zum Haupttext eine Stufe kleiner als --fs-body - ohne eigene
   font-size würde er sonst .holiday-name's --fs-body erben.
*/
.holiday-halfday {
	font-size: var(--fs-2);
	color: var(--t3);
	font-weight: 400;
}
/* Direktes Kind des Grids (kein .holiday-item-Wrapper) — Einzeiliger
   Hinweistext, kein Datum → über alle drei Grid-Spalten spannen. */
.holiday-empty {
	grid-column: 1 / -1;
	padding-block: 8px;
	padding-inline: 14px;
	color: var(--t3);
}
/* Schmale Viewports (Nutzerwunsch 21.09.2026: iPhone-Dropdown nutzte nicht
   die volle Breite, musste aber horizontal gescrollt werden, weil die
   Namensspalte per white-space:nowrap jede Zeile in eine Linie zwang; dazu
   wirkte der 5px-column-gap dort zu knapp). Ersetzt die Anchor-Kante durch
   feste Insets (volle Breite statt max-content), lässt die Namensspalte
   umbrechen statt zu scrollen. Wochentag-/Datumsspalte bleiben unverändert
   (bereits über das gemeinsame Grid auf .holiday-item ausgerichtet). Muss
   NACH dem obigen Basis-Block stehen, sonst greift der Override bei
   gleicher Spezifität nie (s. AGENTS.md-Gotcha zu @media-Reihenfolge).
   left/right auf 0 (statt 16px, Nutzerwunsch 21.09.2026: "darf gern bis zum
   Rand gehen") - der Innenabstand zum Text bleibt trotzdem über
   .holiday-weekday/.holiday-name's padding-inline erhalten. */
@media (max-width: 600px) {
	.holidays-dropdown {
		left: 0;
		right: 0;
		width: auto;
		max-width: none;
		position-try-fallbacks: none;
		grid-template-columns: 2.2em minmax(5.5em, auto) minmax(0, 1fr);
		column-gap: 10px;
		overflow-x: hidden;
	}
	.holiday-name {
		white-space: normal;
	}
}

/* ── PUSH DROPDOWN (R330, Glocke - js/app.js) ──
   Anders als .holidays-dropdown (Grid, gleich lange Zeilen) hier eine
   vertikale Liste wie .hamburger-dropdown, da Push-Historie-Einträge
   mehrzeilig und unterschiedlich lang sind (Titel + optionaler Body +
   Zeitstempel) - eine feste Spaltenbreite würde lange Close-Frankfurt-/
   Close-US-Bodies unlesbar stauchen. Feste `width` statt `max-content`
   (anders als .holidays-dropdown), damit lange Bodies umbrechen statt das
   Panel in die Breite zu ziehen.
   R330 Nachbesserung (18.09.2026, Nutzerwunsch): Flex-Column mit ZWEI
   Kindern - `.push-dropdown-scroll` (Historie, scrollt) und
   `.push-dropdown-footer` (Ab-/Anmelden + Push-Einstellungen, bleibt IMMER
   sichtbar am unteren Rand). Kein eigenes `position: sticky` nötig - die
   Historie ist der einzige wachsende/scrollende Teil, der Footer liegt als
   normales Flex-Geschwister ausserhalb ihres Scroll-Containers und wird vom
   Scrollen darin nie berührt. `overflow: hidden` auf dem Panel selbst
   schneidet die Ecken beider Kinder sauber auf `border-radius` zu.

   R335 Breite: das Panel misst sich selbst am FOOTER - es ist immer genau so
   breit, wie die längste Aktionszeile ("Push-Benachrichtigungen
   deaktivieren") sie braucht, ohne feste Zahl. Die trägt `white-space:
   nowrap` und wurde bei den vorherigen 320px vom `overflow: hidden`
   abgeschnitten; eine grössere Zahl (R334: 380px) hätte bei jeder Änderung
   an Text, Schriftgrösse oder Icon-Grösse erneut nachjustiert werden müssen.
   Der Trick steckt in `.push-dropdown-scroll` unten: `width: 0` nimmt die
   Historie aus der Breitenberechnung des Panels (ihre umbrechenden
   Close-Bodies hätten es sonst über `max-content` viewportbreit aufgezogen),
   `min-width: 100%` lässt sie danach trotzdem die volle Panelbreite füllen -
   sonst endeten ihre `border-bottom`-Trennlinien vor dem Panelrand.
   Prozentuale `min-width` wird bei der intrinsischen Messung ignoriert und
   erst nach dem Feststehen der Panelbreite aufgelöst, deshalb ist das nicht
   zirkulär. `min-width` am Panel hält die Historie lesbar breit, falls der
   Footer mal schmaler ist (Readonly-Betrachter sehen nur eine Zeile). */
.push-dropdown {
	position: fixed;
	background: var(--bg-hover);
	border-radius: var(--radius);
	width: max-content;
	min-width: min(320px, calc(100vw - 32px));
	max-width: calc(100vw - 32px);
	max-height: calc(100dvh - var(--topbar-h-total) - 24px);
	z-index: var(--z-dropdown);
	display: flex;
	flex-direction: column;
	overflow: hidden;
}
.push-dropdown-scroll {
	/* Siehe Kommentar oben - hält die Historie aus der Panelbreiten-Messung
	   heraus, füllt sie aber danach voll aus. */
	width: 0;
	min-width: 100%;
	overflow-y: auto;
	overflow-x: hidden;
	/* min-height:0 - ohne das erzwingt der Inhalt die Flex-Item-Höhe und
	   "overflow-y:auto" greift nie, klassischer Flexbox-Fallstrick. */
	min-height: 0;
	flex: 1 1 auto;
}
.push-dropdown-footer {
	flex: 0 0 auto;
}
/* "Push-Benachrichtigungen aktivieren/deaktivieren"/"Push-Einstellungen" -
   gleiches Klick-Zeilen-Muster wie .hamburger-item. R331 (18.09.2026,
   Nutzerwunsch "einfach die body font grösse nehmen"): --fs-body statt
   --fs-hint/--fs-label-sm. */
.push-action {
	display: flex;
	align-items: center;
	gap: 10px;
	padding-block: 10px;
	padding-inline: 14px;
	font-size: var(--fs-body);
	color: var(--t3);
	white-space: nowrap;
	cursor: pointer;
	border-top: 0.5px solid var(--line2);
}
.push-action i {
	font-size: var(--fs-icon);
}
@media (hover: hover) and (pointer: fine) {
	.push-action:hover {
		color: var(--t2);
	}
}
.push-history-item {
	padding-block: 8px;
	padding-inline: 14px;
	border-bottom: 0.5px solid var(--line2);
}
.push-history-item:last-child {
	border-bottom: none;
}
.push-history-title {
	font-size: var(--fs-body);
	font-weight: 500;
	color: var(--t1);
}
/* R333: Meldungstext eine Stufe unter dem Titel, Zeitstempel nochmal eine -
   dreistufige Hierarchie im Eintrag. Die Tokens liegen in tokens.css, weil
   --fs-form-label hier nicht mehr passte: es steht auf --fs-4 und war damit
   GRÖSSER als der Titel auf --fs-body/--fs-3. */
/* R341: pre-line statt normal - der Meldungstext behält die Zeilenumbrüche,
   mit denen er verschickt wurde, statt sie zu " · " zusammenzuziehen. Die
   Liste zeigt damit exakt das, was auf dem Gerät ankam. `pre-line` (nicht
   `pre-wrap`) kollabiert weiterhin Einrückungen und doppelte Leerzeichen und
   bricht lange Zeilen normal um - nur das \n selbst wird respektiert. */
.push-history-body {
	font-size: var(--fs-push-body);
	color: var(--t2);
	white-space: pre-line;
	word-break: break-word;
	margin-top: 2px;
}
.push-history-ts {
	font-size: var(--fs-push-ts);
	color: var(--t3);
	margin-top: 2px;
}
/* Platzhalter für die leere Liste - auf Meldungstext-Stufe, nicht grösser als
   die Einträge, die er vertritt. */
.push-empty {
	padding-block: 8px;
	padding-inline: 14px;
	color: var(--t3);
	font-size: var(--fs-push-body);
}

/* ── PUSH-EINSTELLUNGEN-SEITE (R330/R331, js/views/push_settings.js) ──
   Eine Zeile PRO Push-Typ, untereinander gestapelt (Nutzerwunsch 18.09.2026,
   ersetzt eine frühere flex-wrap-Zeile) - Checkbox + Label + Beispiel-
   Vorschau(en). Die Vorschau nutzt bewusst dieselben .push-history-title/-body-
   Klassen wie die echte Historie im Glocke-Dropdown, damit ein Beispiel
   optisch identisch zu einer echten Pushmeldung aussieht. R331: thematisch
   gruppiert (.push-settings-group-label), ein Typ (Börsenschluss) kann
   mehrere Beispiele zeigen (Close Frankfurt + Close US). */
.push-settings-list {
	display: flex;
	flex-direction: column;
}
.push-settings-group-label {
	font-size: var(--fs-form-label);
	font-weight: 500;
	color: var(--t3);
	text-transform: uppercase;
	letter-spacing: 0.02em;
	padding-top: 16px;
}
.push-settings-group-label:first-child {
	padding-top: 0;
}
.push-settings-item {
	display: flex;
	align-items: flex-start;
	gap: 12px;
	padding-block: 14px;
	border-bottom: 0.5px solid var(--line2);
	cursor: pointer;
}
.push-settings-item:last-child {
	border-bottom: none;
}
.push-settings-item input[type="checkbox"] {
	flex: 0 0 auto;
	margin-top: 3px;
}
.push-settings-item-body {
	flex: 1 1 auto;
	min-width: 0;
}
.push-settings-item-label {
	font-size: var(--fs-form-label);
	font-weight: 500;
	color: var(--t1);
	margin-bottom: 6px;
}
.push-settings-item-example {
	background: var(--bg-hover);
	border-radius: var(--radius);
	padding: 8px 10px;
	/* Überschreibt die Border-Konvention der echten Historie-Zeile im
	   Dropdown (dort trennt der Rand mehrere Einträge in einer Liste) -
	   hier ist jede Vorschau ein eigenständiger, umrandeter Kasten. */
	border-bottom: none;
}
.push-settings-item-example + .push-settings-item-example {
	margin-top: 6px;
}

/* ── VIEW-SWITCH / HAMBURGER DROPDOWN (responsive Header-Kollaps, js/app.js) ──
   Gleiches Prinzip wie .position-dropdown/.holidays-dropdown oben (position
   per anchorDropdown() gesetzt, kein CSS-Offset/Animation, Hintergrund statt
   Border) - hier als einfache vertikale Icon+Label-Liste statt Grid, da nur
   eine Spalte gebraucht wird. */
.view-switch-dropdown,
.hamburger-dropdown {
	position: fixed;
	background: var(--bg-hover);
	border-radius: var(--radius);
	width: max-content;
	max-width: calc(100vw - 32px);
	z-index: var(--z-dropdown);
	overflow: auto;
}
.view-switch-item,
.hamburger-item {
	display: flex;
	align-items: center;
	gap: 10px;
	padding-block: 10px;
	padding-inline: 14px;
	font-size: var(--fs-hint);
	color: var(--t3);
	white-space: nowrap;
	cursor: pointer;
}
/* Startet bündig mit dem Icon im Trigger darüber (.view-switch-trigger,
   css/device-views.css, padding-inline: 6px) - anchorDropdown() (js/app.js)
   setzt die Dropdown-Linkskante exakt auf die Trigger-Linkskante, das
   gemeinsame 14px-Padding oben würde die Icons sonst sichtbar nach rechts
   versetzt gegenüber dem Trigger-Icon zeigen (Nutzerfeedback 02.09.2026). */
.view-switch-item {
	padding-inline-start: 6px;
}
.view-switch-item i,
.hamburger-item i {
	font-size: var(--fs-icon);
}
@media (hover: hover) and (pointer: fine) {
	.view-switch-item:hover,
	.hamburger-item:hover {
		color: var(--t2);
	}
}
.view-switch-item.active,
.hamburger-item.active {
	color: var(--t1);
}

/* ── LOADING ── */
.loading {
	display: flex;
	align-items: center;
	justify-content: center;
	padding-block: 60px;
	padding-inline: 20px;
	color: var(--t3);
	font-size: var(--fs-form-label);
	gap: 8px;
}
.spinner {
	width: 16px;
	height: 16px;
	border: 1.5px solid var(--line);
	border-top-color: var(--t2);
	border-radius: 50%;
	animation: spin 0.7s linear infinite;
}
@keyframes spin {
	to {
		transform: rotate(360deg);
	}
}

/* ── TRANSACTION TOTAL ROW ── */
/* Sticky on the whole tfoot (not per-row bottom:0), so all footer rows
   stack and stay visible together instead of overlapping at the same offset. */
table.txns tfoot {
	position: sticky;
	bottom: 0;
	z-index: var(--z-raised);
}
tfoot tr.txn-total td {
	border-top: 1px solid var(--line);
	/* Nutzerwunsch 10.08.2026: keine eigene Hintergrundfarbe (war --bg2) — aber
	   nicht transparent: dieser Footer ist `position: sticky` (siehe oben), muss
	   also weiterhin die darunter wegscrollenden Zeilen abdecken, sonst
	   scheinen sie beim Scrollen sichtbar durch den fixierten Footer-Text. */
	background: var(--bg);
	font-family: var(--fm);
	font-variant-numeric: tabular-nums;
}
.txn-total-label {
	padding-block: 4px;
	padding-inline: 0.875rem;
	color: var(--t2);
	font-size: 11px;
}
.txn-total-label-first {
	padding-block: 8px 4px;
	padding-inline: 0.875rem;
}
.txn-total-label-last {
	padding-block: 4px 10px;
	padding-inline: 0.875rem;
}
.txn-total-value {
	padding-block: 4px;
	padding-inline: 0.875rem;
	font-weight: 400;
	color: var(--t1);
	text-align: right;
}
.txn-total-value-first {
	padding-block: 8px 4px;
	padding-inline: 0.875rem;
}
.txn-total-value-last {
	padding-block: 4px 10px;
	padding-inline: 0.875rem;
	font-weight: 400;
}
.txn-total-value-pos {
	color: var(--pos);
}

/* ── RUNNING SHARES COLUMN ── */
td.shares-running {
	color: var(--blue);
}

