/*
   Depotblick: a figure sheet. One figure per screen is set at specimen size; everything else steps down a short type
   ramp on hairline rules. Dark first (ink sheet, paper figures), light as warm paper with ink figures; the theme follows
   html[data-theme], which js/theme.js sets from the Darstellung (System follows prefers-color-scheme). Colour does jobs only: blue for what is selected or pressable, green and coral for signed gains and losses.
   Charts read their colours from the --chart-* tokens (js/value-chart.js, js/forecast-chart.js).
*/

:root {
    color-scheme: dark;
    --ground: #121316;
    --raised: #1b1d21;
    --sheet: #1a1b1f;
    --figure: #f1efe9;
    --text: #d9d7d1;
    --muted: #9c9ea4;
    --rule: #2a2c31;
    --rule-strong: #3d4047;
    /* The edge of a control (buttons, fields, segment groups): 3:1 against the ground, as WCAG asks of a boundary. */
    --control-edge: #686b73;
    --accent: #6b93ff;
    --accent-ink: #0b1530;
    --selection: rgba(107, 147, 255, 0.16);
    --positive: #7fd4a0;
    --negative: #ff8a7c;
    --warning-ink: #f2c879;
    --warning-bg: rgba(242, 200, 121, 0.1);
    --warning-rule: rgba(242, 200, 121, 0.35);
    --scrim: rgba(6, 7, 9, 0.5);
    --shadow-color: rgba(0, 0, 0, 0.5);
    --brand: #2457c5;
    --hatch-ground: #2a2c31;
    --chart-value: var(--figure);
    --chart-history: var(--figure);
    --chart-median: var(--accent);
    --chart-invested: #747880;
    --chart-band-inner: rgba(107, 147, 255, 0.32);
    --chart-band-outer: rgba(107, 147, 255, 0.14);
    /* The value chart's area: own money still in the depot in a grey wash, the gains above it in an accent wash. */
    --chart-own: rgba(156, 158, 164, 0.2);
    --chart-gains: rgba(107, 147, 255, 0.26);
    --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%239c9ea4' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

    /* Apple's system font first: a Mac with only some faces of Segoe UI or Consolas (Office installs them one at a time)
       would otherwise render every text in the one it has, e.g. italic. These names resolve on Apple devices only. */
    --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text", "Segoe UI", system-ui, sans-serif;
    --font-figure: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Display", "Segoe UI", system-ui, sans-serif;
    --font-code: ui-monospace, Menlo, "Cascadia Mono", Consolas, monospace;
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    /* A softer start than ease-out, for whole pages and layouts: they ease away instead of lurching. */
    --ease-settle: cubic-bezier(0.32, 0.72, 0, 1);
}

:root[data-theme="light"] {
    color-scheme: light;
    --ground: #f4f2ed;
    --raised: #e9e6df;
    --sheet: #fbfaf7;
    --figure: #141518;
    --text: #2b2c30;
    --muted: #5f6066;
    --rule: #dcd8cf;
    --rule-strong: #c2bdb2;
    --control-edge: #85837d;
    --accent: #2f5bd8;
    --accent-ink: #ffffff;
    --selection: rgba(47, 91, 216, 0.1);
    --positive: #1f7a4c;
    --negative: #b8392b;
    --warning-ink: #6e4b00;
    --warning-bg: #f5e6c3;
    --warning-rule: #e2c98a;
    --scrim: rgba(30, 28, 24, 0.26);
    --shadow-color: rgba(40, 35, 25, 0.18);
    --hatch-ground: #e3dfd6;
    --chart-invested: #858580;
    --chart-band-inner: rgba(47, 91, 216, 0.26);
    --chart-band-outer: rgba(47, 91, 216, 0.11);
    --chart-own: rgba(95, 96, 102, 0.14);
    --chart-gains: rgba(47, 91, 216, 0.2);
    --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%235f6066' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* ---------- Base and the browser's own surfaces ---------- */

* {
    box-sizing: border-box;
}

html, body {
    margin: 0;
    background: var(--ground);
    color: var(--text);
    font-family: var(--font-ui);
    font-size: 14px;
    line-height: 1.5;
    accent-color: var(--accent);
    scrollbar-color: var(--rule-strong) transparent;
}

/* The web host covers the views while the page is hidden, and until the check on return passes, so no figures show
   after a sleep or a long absence (lock-signals.js). Dialogs in the top layer inherit the hidden visibility too. */
html.covered #app {
    visibility: hidden;
}

/* A switch of the Darstellung fades: while js/theme.js holds .theme-switching, every surface, text and rule eases to
   the new theme's colour instead of cutting to it. It stands in for the element's own transitions for that moment. */
.theme-switching,
.theme-switching *,
.theme-switching *::before,
.theme-switching *::after {
    transition: background-color 320ms var(--ease-out), border-color 320ms var(--ease-out), color 320ms var(--ease-out),
        fill 320ms var(--ease-out), stroke 320ms var(--ease-out), outline-color 320ms var(--ease-out),
        box-shadow 320ms var(--ease-out), text-decoration-color 320ms var(--ease-out), scrollbar-color 320ms var(--ease-out) !important;
}

::selection {
    background: var(--selection);
    color: var(--figure);
}

h1, h2, h3 {
    color: var(--figure);
    text-wrap: balance;
}

p {
    text-wrap: pretty;
}

table {
    font-variant-numeric: tabular-nums;
}

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.positive {
    color: var(--positive);
}

.negative {
    color: var(--negative);
}

/* ---------- Controls ---------- */

button,
a.button {
    padding: 7px 14px;
    border: 1px solid var(--control-edge);
    border-radius: 6px;
    background: transparent;
    color: var(--text);
    font: inherit;
    cursor: pointer;
    transition: background-color 120ms ease-out, border-color 120ms ease-out, color 120ms ease-out;
}

button:hover:not(:disabled),
a.button:hover {
    border-color: var(--muted);
    background: var(--raised);
}

button.primary,
a.button.primary {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-ink);
    font-weight: 600;
}

button.primary:hover:not(:disabled),
a.button.primary:hover {
    background: color-mix(in srgb, var(--accent) 88%, var(--figure));
    border-color: transparent;
}

/* A link that looks like a button: the Demo's "Zugang anfordern", a mailto. */
a.button {
    display: inline-block;
    text-decoration: none;
}

button:disabled {
    opacity: 0.5;
    cursor: default;
}

button.link {
    padding: 0;
    border: none;
    background: none;
    color: var(--accent);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
}

button.link:hover:not(:disabled) {
    background: none;
    text-decoration-thickness: 2px;
}

/* The Kontakt links, which leave the app: drawn like button.link. */
a {
    color: var(--accent);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
}

a:hover {
    text-decoration-thickness: 2px;
}

/* Erklären is small text: keep its target at least 24px tall (WCAG 2.2 target size) without moving the text. */
button.link.explain {
    min-height: 24px;
    padding: 3px 0;
}

button.danger {
    border-color: color-mix(in srgb, var(--negative) 60%, transparent);
    color: var(--negative);
}

/* The one close control (CloseButton.razor): a quiet ✕ in a 30px target. The negative margin puts the drawn cross, not
   its target, on the header's right edge, and centres it on the first line of an 18px heading beside it. */
button.close-button {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 30px;
    height: 30px;
    margin: -1px -9px 0 0;
    padding: 0;
    border-color: transparent;
    color: var(--muted);
}

button.close-button:hover:not(:disabled) {
    color: var(--text);
}

button.close-button svg {
    width: 12px;
    height: 12px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
}

input[type="text"],
input[type="password"],
input[type="search"],
input[type="date"],
select {
    padding: 7px 10px;
    font: inherit;
    color: var(--text);
    background: var(--sheet);
    border: 1px solid var(--control-edge);
    border-radius: 6px;
}

input::placeholder {
    color: var(--muted);
}

input:hover:not(:disabled),
select:hover:not(:disabled) {
    border-color: var(--muted);
}

input:focus-visible,
select:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 0;
    border-color: transparent;
}

/* The native arrow ignores padding and sits on the border; draw our own in the theme's muted colour. */
select {
    appearance: none;
    padding-right: 36px;
    background-image: var(--chevron);
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 12px 8px;
    cursor: pointer;
}

/* Segmented controls: the period quick picks, the tax and value toggles, the chart's two views. */
.range-pills > div,
.tax-toggle > div,
.chart-toolbar {
    display: inline-flex;
    padding: 2px;
    border: 1px solid var(--control-edge);
    border-radius: 7px;
}

/* A toggle with a visible name (Prognose: "Steuern", "Werte") sets it beside the segments. */
.tax-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--muted);
}

.range-pills button,
.tax-toggle button,
.chart-toolbar button {
    padding: 5px 12px;
    border: none;
    border-radius: 5px;
    color: var(--muted);
    background: none;
    transition: background-color 120ms ease-out, color 120ms ease-out, box-shadow 120ms ease-out;
}

.range-pills button:hover:not(:disabled),
.tax-toggle button:hover:not(:disabled),
.chart-toolbar button:hover:not(:disabled) {
    color: var(--text);
    background: var(--raised);
}

.range-pills button.active,
.tax-toggle button.active,
.chart-toolbar button.active,
.range-pills button.active:hover,
.tax-toggle button.active:hover,
.chart-toolbar button.active:hover {
    color: var(--figure);
    background: var(--raised);
    box-shadow: inset 0 0 0 1px var(--control-edge);
    font-weight: 600;
}

.badge {
    display: inline-block;
    min-width: 18px;
    margin-left: 6px;
    padding: 0 6px;
    font-size: 12px;
    font-weight: 600;
    line-height: 18px;
    text-align: center;
    color: var(--warning-ink);
    background: var(--warning-bg);
    border-radius: 999px;
}

.badge.quiet {
    color: var(--muted);
    background: var(--raised);
}

.error {
    margin: 0;
    color: var(--negative);
}

.notice {
    color: var(--muted);
}

.warning,
.info {
    padding: 10px 14px;
    color: var(--text);
    border-radius: 6px;
}

.warning {
    background: var(--warning-bg);
    border: 1px solid var(--warning-rule);
}

/* Something to know rather than to fix, e.g. a security valued at its Execution prices because no source has any. */
.info {
    background: var(--selection);
    border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
}

.setting {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--muted);
}

.setting select {
    min-width: 130px;
}

.file-name {
    margin-left: 8px;
    color: var(--muted);
    font-family: var(--font-code);
    font-size: 12px;
}

.app-version {
    margin: 32px 0 0;
    font-size: 12px;
    color: var(--muted);
}

#blazor-error-ui {
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    z-index: 100;
    width: 100%;
    padding: 10px 24px;
    color: var(--text);
    background: var(--warning-bg);
    border-top: 1px solid var(--warning-rule);
}

/* ---------- Loading: the host page's mark until the app has rendered (it replaces #app's content) ---------- */

/* The viewfinder focuses on its line, which draws itself, again and again. It shows only after a moment, so a fast
   start doesn't flash it. */
.app-loading {
    display: grid;
    place-content: center;
    justify-items: center;
    gap: 16px;
    min-height: 100vh;
    color: var(--muted);
    font-size: 13px;
    opacity: 0;
    animation: loading-appear 320ms var(--ease-out) 240ms forwards;
}

.loading-mark {
    width: 64px;
    height: 64px;
}

.loading-mark rect {
    fill: var(--brand);
}

.loading-mark path,
.loading-mark polyline {
    fill: none;
    stroke: #ffffff;
    stroke-width: 7;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.loading-mark path {
    transform-origin: 50px 50px;
    animation: loading-focus 1600ms var(--ease-settle) infinite;
}

.loading-mark polyline {
    stroke-dasharray: 1;
    animation: loading-draw 1600ms var(--ease-settle) infinite;
}

@keyframes loading-appear {
    to { opacity: 1; }
}

@keyframes loading-focus {
    0%, 100% { transform: scale(1); }
    45%, 60% { transform: scale(0.9); }
}

@keyframes loading-draw {
    0% { stroke-dashoffset: 1; }
    55%, 80% { stroke-dashoffset: 0; }
    100% { stroke-dashoffset: -1; }
}

/* The Vault's documents loading after the unlock: like the host page's mark, it shows only after a moment. */
.documents-loading {
    opacity: 0;
    animation: loading-appear 320ms var(--ease-out) 240ms forwards;
}

@media (prefers-reduced-motion: reduce) {
    .app-loading,
    .documents-loading {
        animation-duration: 1ms;
    }

    .loading-mark path,
    .loading-mark polyline {
        animation: none;
    }
}

/* ---------- Shell: the rail on the left, the work on the right ---------- */

.app-shell {
    display: grid;
    grid-template-columns: 208px minmax(0, 1fr);
    min-height: 100vh;
}

.rail {
    position: sticky;
    top: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
    height: 100vh;
    padding: 22px 14px 18px;
    border-right: 1px solid var(--rule);
}

.brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 10px 26px;
    color: var(--figure);
    font-family: var(--font-figure);
    font-size: 17px;
    font-weight: 600;
    letter-spacing: -0.01em;
}

.brand-mark {
    width: 22px;
    height: 22px;
    flex: none;
}

.brand-mark rect {
    fill: var(--brand);
}

.brand-mark path,
.brand-mark polyline {
    fill: none;
    stroke: #ffffff;
    stroke-width: 8;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.rail-item {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1px;
    width: 100%;
    padding: 8px 10px;
    border: none;
    border-radius: 6px;
    text-align: left;
    color: var(--muted);
    transition: background-color 160ms ease-out, color 160ms ease-out;
}

.rail-item:hover:not(:disabled) {
    color: var(--text);
    background: var(--raised);
}

.rail-item.active {
    color: var(--figure);
    background: var(--raised);
    font-weight: 600;
}

.rail-item .hint {
    font-size: 12px;
    font-weight: 400;
    color: var(--muted);
}

/* Refresh progress under the Kursdaten entry, so the entry keeps its size. */
.rail-progress {
    width: 100%;
    height: 3px;
    margin-top: 6px;
    appearance: none;
    border: none;
    border-radius: 2px;
    background: var(--rule);
}

.rail-progress::-webkit-progress-bar {
    border-radius: 2px;
    background: var(--rule);
}

.rail-progress::-webkit-progress-value {
    border-radius: 2px;
    background: var(--accent);
    transition: width 300ms ease-out;
}

/* The rail's groups (VaultPages.Rail) are set apart by space alone: the figures, then the inputs they are built from.
   The last group, Einstellungen, leads the foot, just above Sperren (or Beispiel beenden). */
.rail-item.rail-group-start {
    margin-top: 16px;
}

.rail-item.rail-foot-start {
    margin-top: auto;
}

.rail-item + .rail-foot {
    margin-top: 10px;
}

.rail-foot {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
    margin-top: auto;
    padding: 0 10px;
}

.rail-foot .vault-state {
    font-size: 12px;
    color: var(--muted);
}

.rail-foot button,
.rail-foot a.button {
    padding: 5px 12px;
    font-size: 13px;
}

/* The Demo's two actions stacked: one width, their labels centred. */
.rail-foot.demo > a.button,
.rail-foot.demo > button {
    align-self: stretch;
    text-align: center;
}

.workspace {
    min-width: 0;
}

/* A page chosen in the rail settles in rather than cutting in: it rises 8px into place while it fades up. It is a
   transition, so switching again before it has settled picks it up where it is. */
.workspace > main {
    transition: opacity 400ms var(--ease-settle), translate 560ms var(--ease-settle);
}

/* The page it replaces doesn't vanish in one frame either (js/page-switch.js): it fades out first, and only then does
   the new page settle in, so the two are never seen together. */
html .workspace > main[data-leaving] {
    opacity: 0;
    pointer-events: none;
    transition: opacity 140ms cubic-bezier(0.4, 0, 1, 1);
}

@starting-style {
    .workspace > main {
        opacity: 0;
        translate: 0 8px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .workspace > main {
        transition: opacity 200ms ease-out;
    }

    @starting-style {
        .workspace > main {
            translate: none;
        }
    }
}

/* Keine Verbindung (web only): a warning above whatever the screen shows, gone once the connection is back. */
.connection-banner {
    margin: 20px 48px 0;
    padding: 8px 14px;
    color: var(--warning-ink);
    background: var(--warning-bg);
    border: 1px solid var(--warning-rule);
    border-radius: 6px;
}

.start > .connection-banner {
    margin: 0 0 24px;
}

/* The narrow-screen hint (web only): an info notice, only in a window narrower than 720px, until closed. */
.narrow-screen-banner {
    display: none;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin: 20px 48px 0;
    padding: 8px 8px 8px 14px;
    color: var(--text);
    background: var(--selection);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
    border-radius: 6px;
}

@media (width < 720px) {
    .narrow-screen-banner {
        display: flex;
    }
}

.start > .narrow-screen-banner {
    margin: 0 0 24px;
}

/* The unlock opened the generation before a damaged one: told once, until closed. */
.fallback-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin: 20px 48px 0;
    padding: 8px 8px 8px 14px;
    color: var(--warning-ink);
    background: var(--warning-bg);
    border: 1px solid var(--warning-rule);
    border-radius: 6px;
}

/* The Backup-Erinnerung on the Übersicht: an info notice, after the connection and fallback banners. "Später" at the end. */
.backup-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin: 20px 48px 0;
    padding: 8px 8px 8px 14px;
    color: var(--text);
    background: var(--selection);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
    border-radius: 6px;
}

.backup-banner-lines {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.backup-banner .error {
    color: var(--negative);
}

/* Right after "Vault anlegen" on the web: the first Backup, offered once (#399). Info, not a warning. */
.backup-offer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px 12px;
    margin: 20px 48px 0;
    padding: 8px 8px 8px 14px;
    color: var(--text);
    background: var(--selection);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
    border-radius: 6px;
}

.backup-offer .actions {
    display: flex;
    gap: 8px;
}

.backup-offer .error {
    display: block;
    margin-top: 4px;
}

/* Why a Run mode can't be chosen now, below its disabled button. */
.run-mode-unavailable {
    margin: 8px 0 0;
    color: var(--warning-ink);
}

/* Sample mode: impossible to miss, but not louder than the figures. */
.sample-banner {
    display: flex;
    gap: 6px;
    align-items: baseline;
    margin: 20px 48px 0;
    padding: 8px 14px;
    color: var(--text);
    background: var(--selection);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
    border-radius: 6px;
}

.sample-banner strong {
    color: var(--accent);
}

/* The Sample Stichtag: two chips at the banner's end, the active one like an active document filter. */
.sample-banner {
    flex-wrap: wrap;
}

.sample-stichtag {
    display: flex;
    align-items: baseline;
    gap: 6px;
    margin-left: auto;
    color: var(--muted);
}

.sample-stichtag button {
    padding: 2px 10px;
    border-radius: 999px;
    color: var(--muted);
}

.sample-stichtag button[aria-pressed="true"] {
    color: var(--figure);
    background: var(--raised);
    border-color: var(--muted);
    font-weight: 600;
}

/* A later refresh (e.g. after an import): a quiet strip on top of the dashboard. */
.refresh-status.strip {
    margin: 0;
    padding: 10px 48px;
    border-bottom: 1px solid var(--rule);
    background: var(--raised);
}

/* ---------- Figures: re-set in place (RollingFigure) ---------- */

/*
   A changed figure is re-set, not replaced: each changed character rolls one place, up when the amount rose and down
   when it fell, through a window the height of its line; unchanged characters, points and the unit stay still. The
   changes run left to right, 30ms apart. Numerals are tabular, so a character's place keeps its width; only a place
   that opens (one more digit) or closes glides, and the text beside the figure moves with it instead of jumping.
*/
.roll {
    interpolate-size: allow-keywords;
}

.roll-slot {
    display: inline-block;
    position: relative;
    white-space: pre;
    transition: width 360ms var(--ease-out);
}

/*
   While a place changes, it shows its characters through a window the height of the numerals, with soft edges: the
   window is centred on the line box (Segoe's numerals sit from 0.33em above its middle to 0.43em below it at any line
   height) and fades out over 0.14em beyond them. Only moving places are masked; a resting comma keeps its tail.
*/
.roll-slot.up,
.roll-slot.down {
    mask-image: linear-gradient(transparent calc(50% - 0.5em), #000 calc(50% - 0.36em), #000 calc(50% + 0.46em), transparent calc(50% + 0.6em));
    mask-repeat: no-repeat;
}

.roll-new,
.roll-old {
    display: inline-block;
}

.roll-old {
    position: absolute;
    top: 0;
    left: 0;
    user-select: none;
    pointer-events: none;
}

/* A place that closes keeps its leaving character in the flow while its width runs to nothing. */
.roll-slot.leaving {
    width: 0;
}

.roll-slot.leaving .roll-old {
    position: static;
}

@starting-style {
    .roll-slot.entering {
        width: 0;
    }
}

.roll-new.up {
    animation: roll-in-up 420ms var(--ease-out) both;
}

.roll-new.down {
    animation: roll-in-down 420ms var(--ease-out) both;
}

/* The leaving character rides 1.1em ahead of the arriving one, like the next figure on a counter wheel. */
.roll-old.up {
    animation: roll-out-up 420ms var(--ease-out) both;
}

.roll-old.down {
    animation: roll-out-down 420ms var(--ease-out) both;
}

.roll .d1 { animation-delay: 30ms; }
.roll .d2 { animation-delay: 60ms; }
.roll .d3 { animation-delay: 90ms; }
.roll .d4 { animation-delay: 120ms; }
.roll .d5 { animation-delay: 150ms; }

/* 1.1em of travel: a character starts and ends wholly outside the window, and the two never overlap in it. */
@keyframes roll-in-up {
    from {
        transform: translateY(1.1em);
    }
}

@keyframes roll-in-down {
    from {
        transform: translateY(-1.1em);
    }
}

@keyframes roll-out-up {
    to {
        transform: translateY(-1.1em);
        opacity: 0;
    }
}

@keyframes roll-out-down {
    to {
        transform: translateY(1.1em);
        opacity: 0;
    }
}

/* Without movement a changed character crossfades in its place, and places open and close at once. */
@media (prefers-reduced-motion: reduce) {
    .roll-slot {
        transition: none;
    }

    .roll-new.up,
    .roll-new.down {
        animation: roll-fade-in 160ms ease-out both;
    }

    .roll-old.up,
    .roll-old.down {
        animation: roll-fade-out 120ms ease-in both;
    }

    .roll .d1, .roll .d2, .roll .d3, .roll .d4, .roll .d5 {
        animation-delay: 0ms;
    }
}

@keyframes roll-fade-in {
    from { opacity: 0; }
}

@keyframes roll-fade-out {
    to { opacity: 0; }
}

/* ---------- Dashboard ---------- */

.dashboard {
    max-width: 1440px;
    padding: 26px 48px 64px;
}

.dashboard-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 14px 24px;
}

.dashboard-head .title {
    display: flex;
    align-items: baseline;
    gap: 14px;
}

.dashboard-head h1 {
    margin: 0;
    font-size: 15px;
    font-weight: 600;
}

.dashboard-head .range {
    margin: 0;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}

.dashboard-head .range-error {
    margin: 0;
    color: var(--negative);
}

.range-picker {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-left: auto;
}

.range-picker label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    color: var(--muted);
}

.range-select select {
    min-width: 180px;
    color: var(--muted);
}

/* The dropdown holds the active range: it reads as selected, like an active segment's text. */
.range-select.active select {
    color: var(--figure);
    font-weight: 600;
    border-color: var(--muted);
}

.range-select option {
    color: var(--text);
    font-weight: 400;
}

.custom-range {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 12px;
}

.custom-range label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    color: var(--muted);
}

/* Spans the column like the head and the figure row. */
.dashboard > .warning,
.dashboard > .info {
    margin: 20px 0 0;
}

/* The specimen: the figure at full size, on the Prognose and Steuern pages one sentence beside it. */
.hero {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 16px 56px;
    margin-top: 40px;
}

.hero h2 {
    margin: 0;
    font-size: 13px;
    font-weight: 400;
    color: var(--muted);
}

.hero-value {
    margin: 6px 0 0;
    color: var(--figure);
    font-family: var(--font-figure);
    font-size: clamp(56px, 7.2vw, 104px);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    letter-spacing: -0.035em;
}

.hero-value .cents {
    font-size: 0.5em;
    font-weight: 300;
    color: var(--muted);
    letter-spacing: -0.01em;
}

.hero-figure .explain {
    margin-top: 12px;
    font-size: 13px;
}

/* The end value split into own money and the gains in it, quiet under the figure. */
.hero-split {
    margin: 10px 0 0;
    font-size: 14px;
    color: var(--muted);
}

.hero-split strong {
    font-family: var(--font-figure);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--text);
}

.hero-split button.link {
    margin-left: 6px;
    font-size: 13px;
}

.hero-summary {
    margin: 0 0 6px;
    max-width: 42ch;
    font-size: 21px;
    line-height: 1.5;
    color: var(--text);
}

.hero-summary strong {
    font-family: var(--font-figure);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--figure);
}

.hero-summary strong.positive {
    color: var(--positive);
}

.hero-summary strong.negative {
    color: var(--negative);
}

.hero-summary button.link {
    margin-top: 6px;
    font-size: 13px;
}

/* Steuern: the year's Quellensteuer under the sentence, which the figure leaves out. */
.hero-summary .withholding {
    display: block;
    margin-top: 8px;
    font-size: 14px;
    color: var(--muted);
}

/* The other figures: one hairline row, each with its Erklären. */
.headlines {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    margin-top: 36px;
    border-top: 1px solid var(--rule);
    border-bottom: 1px solid var(--rule);
}

/* Each figure spans the row's shared tracks (label, value, Erklären), so a label over two lines moves no value. */
.headline {
    display: grid;
    grid-row: span 3;
    grid-template-rows: subgrid;
    row-gap: 0;
    align-items: start;
    justify-items: start;
    padding: 16px 20px 16px 0;
}

.forecast-figures .headline {
    grid-row: span 4;
}

.headline + .headline {
    padding-left: 20px;
    border-left: 1px solid var(--rule);
}

.headline h2 {
    margin: 0;
    font-size: 13px;
    font-weight: 400;
    color: var(--muted);
}

.headline .value {
    margin: 4px 0 0;
    color: var(--figure);
    font-family: var(--font-figure);
    font-size: 26px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.02em;
}

/* Set back like the hero's cents; two thirds rather than half, so they stay legible at figure size. */
.headline .value .cents {
    font-size: 0.65em;
    font-weight: 300;
    color: var(--muted);
    letter-spacing: -0.01em;
}

.headline.positive .value {
    color: var(--positive);
}

.headline.negative .value {
    color: var(--negative);
}

/* A signed amount's cents keep its sign colour; only an unsigned amount's are muted. */
.headline.positive .value .cents,
.headline.negative .value .cents {
    color: inherit;
}

/* On the last track, so the buttons line up across the row however the values wrap. */
.headline .explain {
    align-self: end;
    margin-top: 10px;
    font-size: 12px;
}

/* What the Gewinn holds, the row's footing: one line per group on the row's own four columns, each pair a label
   left and its amount right within the column, as on a statement. No vertical rules, so they read as lines rather
   than more figures; a hairline closes the block. The group names are for screen readers: a line is its group. */
.ledger {
    display: grid;
    gap: 8px;
    padding: 12px 0 14px;
    border-bottom: 1px solid var(--rule);
}

.ledger-line dl {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    align-items: baseline;
    margin: 0;
}

/* The figure row's padding, so a label starts where the label above it does. */
.ledger-item {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: baseline;
    column-gap: 12px;
    padding-right: 20px;
}

.ledger-item + .ledger-item {
    padding-left: 20px;
}

.ledger-item dt {
    font-size: 13px;
    color: var(--muted);
}

.ledger-item dd {
    margin: 0;
    white-space: nowrap;
}

.ledger-item dd.ledger-note {
    grid-column: 1 / -1;
    white-space: normal;
}

/* The amount is its own Erklären, drawn as the app's link (button.link) so it reads as one: accent, a 1px underline
   thickening to 2px on hover. A figure that only turned into a link on hover read as a second way in. */
.ledger-figure {
    padding: 0;
    border: none;
    background: none;
    color: var(--accent);
    font-family: var(--font-figure);
    font-size: 17px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
    cursor: pointer;
}

.ledger-figure:hover {
    text-decoration-thickness: 2px;
}

/* Set back less than the figure row's: at this size light cents were barely readable. */
.ledger-figure .cents {
    font-size: 14px;
    font-weight: 400;
}

.ledger-note {
    font-size: 12px;
    color: var(--warning-ink);
}

.chart-scope {
    display: flex;
    align-items: baseline;
    gap: 12px;
    margin-top: 40px;
    color: var(--muted);
}

.chart-scope strong {
    color: var(--figure);
}

.chart-card {
    margin-top: 12px;
}

.chart-toolbar {
    margin-bottom: 14px;
}

.chart {
    width: 100%;
    height: 420px;
}

/* In the Erklären sheet: the range at a glance, its payments marked in the lane under the line (taller than 200px by
   the lane, so the plot keeps its room). */
.chart-card.compact {
    margin-top: 4px;
}

.chart-card.compact .chart {
    height: 236px;
}

/* The value chart's tooltip (js/value-chart.js, tipContent): the day over a hairline, then groups of rows split by
   hairlines. A row is its line's or area's swatch as in the legend, the label, and the figure right-aligned in tabular
   numerals so the figures line up; a part of the value is indented with a muted label, the value itself is set in
   figure weight 600, and a signed gain takes the gain or loss tone. Muted hints close it. */
.chart-tip {
    pointer-events: none;
    line-height: 1.55;
}

.chart-tip-day {
    color: var(--figure);
    font-weight: 600;
    padding-bottom: 6px;
    margin-bottom: 6px;
    border-bottom: 1px solid var(--rule);
}

.chart-tip-rows {
    display: grid;
    grid-template-columns: auto auto;
    column-gap: 24px;
    row-gap: 1px;
}

.chart-tip-rule {
    grid-column: 1 / -1;
    border-top: 1px solid var(--rule);
    margin: 5px 0;
}

.chart-tip-label {
    display: flex;
    align-items: center;
    gap: 8px;
    white-space: nowrap;
}

.chart-tip-label.strong {
    color: var(--figure);
    font-weight: 600;
}

.chart-tip-label.part {
    color: var(--muted);
    padding-left: 22px;
}

/* 14px wide like the legend's marks, so every label starts at the same place; a row without a line keeps the room. */
.chart-tip-swatch {
    flex: none;
    width: 14px;
    height: 2px;
}

.chart-tip-label.part .chart-tip-swatch {
    width: 10px;
    height: 10px;
    border-radius: 2px;
}

.chart-tip-swatch.line-value {
    background: var(--chart-value);
}

.chart-tip-swatch.line-invested {
    background: var(--chart-invested);
}

.chart-tip-swatch.area-gains {
    background: var(--chart-gains);
    box-shadow: inset 0 0 0 1px var(--chart-gains);
}

.chart-tip-swatch.area-own {
    background: var(--chart-own);
    box-shadow: inset 0 0 0 1px var(--chart-own);
}

.chart-tip-figure {
    color: var(--figure);
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.chart-tip-figure.strong {
    font-weight: 600;
}

.chart-tip-figure.positive {
    color: var(--positive);
}

.chart-tip-figure.negative {
    color: var(--negative);
}

.chart-tip-hint {
    color: var(--muted);
    margin-top: 8px;
    padding-top: 6px;
    border-top: 1px solid var(--rule);
    max-width: 300px;
}

.chart-tip-hint + .chart-tip-hint {
    margin-top: 2px;
    padding-top: 0;
    border-top: 0;
}

.securities {
    margin-top: 40px;
}

/* On narrow windows the table scrolls sideways inside its section, not the whole page. */
/* Scroll shadows: a soft edge appears on the side where more columns are hidden, and only there. */
.securities-scroll {
    overflow-x: auto;
    background:
        linear-gradient(to right, var(--ground) 40%, transparent) left / 48px 100% no-repeat local,
        linear-gradient(to left, var(--ground) 40%, transparent) right / 48px 100% no-repeat local,
        linear-gradient(to right, var(--rule-strong), transparent) left / 20px 100% no-repeat scroll,
        linear-gradient(to left, var(--rule-strong), transparent) right / 20px 100% no-repeat scroll;
    background-color: var(--ground);
}

.securities h2 {
    margin: 0 0 4px;
    font-size: 15px;
}

/* Which figures are the range's and which its last day's: Gewinn is not (Kurs − Einstandskurs) × Stück. */
.securities-range {
    margin: 0 0 10px;
    font-size: 13px;
    color: var(--muted);
}

/* The Dokumente filter's field, as wide as the table, with the sold-rows toggle after it; the toggle wraps below
   when the Security pane leaves too little room. */
.security-filter {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 16px;
    margin-bottom: 10px;
}

.security-filter .setting {
    font-size: 13px;
    white-space: nowrap;
}

.security-filter input[type="search"] {
    flex: 1 1 260px;
    min-width: 0;
}

.securities .empty,
.market-data .empty {
    margin: 0;
    color: var(--muted);
}

.securities table,
.document-table,
.market-table {
    width: 100%;
    border-collapse: collapse;
}

.securities th,
.securities td {
    padding: 10px 12px;
    border-bottom: 1px solid var(--rule);
}

.securities th {
    border-bottom-color: var(--rule-strong);
}

.securities th:first-child,
.securities td:first-child {
    padding-left: 0;
}

.securities .text {
    min-width: 220px;
    text-align: left;
}

.securities .number {
    text-align: right;
    white-space: nowrap;
}

.securities th button.sort,
.tax-table th button.sort {
    padding: 0;
    border: none;
    background: none;
    font-size: 12px;
    font-weight: 400;
    color: var(--muted);
}

.securities th button.sort.active,
.tax-table th button.sort.active {
    color: var(--figure);
    font-weight: 600;
}

/* The sort direction: the same chevron as the selects, flipped for ascending. */
.securities th .arrow,
.tax-table th .arrow {
    width: 9px;
    height: 6px;
    margin-left: 5px;
    vertical-align: 1px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
    transition: transform 160ms var(--ease-out);
}

/* Sorting the same column again turns the chevron over, so the change of direction is seen, not just its result. */
.securities th .arrow.up,
.tax-table th .arrow.up {
    transform: rotate(180deg);
}

.securities tbody tr,
.document-table tbody tr {
    cursor: pointer;
    transition: background-color 120ms ease-out;
}

.securities tbody tr:hover,
.document-table tbody tr:hover {
    background: var(--raised);
}

.securities tbody tr.selected,
.document-table tbody tr.selected {
    background: var(--selection);
}

.securities tbody tr.selected .name,
.document-table tbody tr.selected td:first-child {
    color: var(--accent);
}

/* The name cell's button: reads as text, the row carries hover and selection; focus rings the whole row. */
.securities .row-toggle,
.document-table .row-toggle {
    display: block;
    width: 100%;
    padding: 0;
    border: none;
    border-radius: 0;
    background: none;
    color: inherit;
    text-align: left;
}

.securities .row-toggle:hover:not(:disabled),
.document-table .row-toggle:hover:not(:disabled) {
    background: none;
}

.securities .row-toggle:focus-visible,
.document-table .row-toggle:focus-visible {
    outline: none;
}

.securities tbody tr:has(.row-toggle:focus-visible),
.document-table tbody tr:has(.row-toggle:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

.securities .name,
.harvest-table .name {
    display: block;
    font-weight: 600;
    color: var(--figure);
}

.securities .isin,
.market-table .isin,
.harvest-table .isin {
    font-family: var(--font-code);
    font-size: 12px;
    color: var(--muted);
}

.securities td.positive {
    color: var(--positive);
}

.securities td.negative {
    color: var(--negative);
}

/* Sold off by the end of the range: still there, set back; a realised gain or loss keeps its sign colour. */
.securities tbody tr.sold td:not(.positive):not(.negative),
.securities tbody tr.sold .name {
    color: var(--muted);
    font-weight: 400;
}

.securities .no-price {
    margin-left: 8px;
    padding: 1px 6px;
    font-size: 12px;
    font-weight: 600;
    color: var(--warning-ink);
    background: var(--warning-bg);
    border-radius: 4px;
}

.securities .period-note {
    margin-left: 2px;
    cursor: help;
}

/*
   With a Security selected, only the table splits: it keeps the left, the Security pane takes the right. Closed, the
   pane's track is 0fr, like the document viewer's.
*/
.security-workspace {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 0fr);
    column-gap: 0;
    align-items: start;
    margin-top: 40px;
}

.security-workspace .securities {
    margin-top: 0;
}

/* 1 : 1.2 like the document viewer: the table needs three columns, the pane's list six. */
.security-workspace.open {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
    column-gap: 24px;
}

/* Beside the pane, long names wrap rather than pushing the figures out of the table. */
.security-workspace.open .securities .text,
.security-workspace.closing .securities .text {
    min-width: 0;
}

/*
   On a narrow dashboard (the workspace, not the window, so the rail and gutters count) the pane goes under the table
   at full width instead of squeezing both. The dashboard is the size container for that.
*/
.dashboard {
    container: dashboard / inline-size;
}

@container dashboard (max-width: 1059px) {
    .dashboard .security-workspace.open {
        display: block;
    }

    .dashboard .security-workspace.open .securities table,
    .dashboard .security-workspace.closing .securities table {
        width: 100%;
    }

    .dashboard .security-pane {
        position: static;
        max-height: none;
        margin-top: 24px;
    }

    .dashboard .security-workspace .security-pane {
        width: auto;
    }
}

/* Beside the pane the rows have inner room on both sides, so a selected row's wash doesn't end flush with its text. */
.security-workspace .securities th:first-child,
.security-workspace .securities td:first-child {
    padding-left: 12px;
}

.security-workspace .securities th:last-child,
.security-workspace .securities td:last-child {
    padding-right: 12px;
}

/* Focus rings the name, the app's usual ring, rather than the whole row. */
.security-workspace .securities tbody tr:has(.row-toggle:focus-visible) {
    outline: none;
}

.security-workspace .securities .row-toggle {
    display: inline-block;
    width: auto;
}

.security-workspace .securities .row-toggle:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 2px;
}

/*
   Opening, the figure columns fade first (.hiding, 120ms; HidingTime in Dashboard waits for it), so none is squeezed or seen to jump
   when the columns the pane leaves out go; then the track narrows over 460ms while the pane slides in 24px and fades up
   just behind it, as the document viewer does, and Wert am Ende and Gewinn fade back in at their new places. The table
   is set at its split width from the start (100cqw is the workspace), so the names stay put while the track moves.
   Closing is quicker and backwards (.closing, 320ms, ClosingTime): the pane and the figures fade out first, the track
   widens after it with the table on it, its rows and hairlines following the track rather than reaching the full width
   in one frame at the end, and then every column fades in at full width.
*/
.security-workspace {
    container-type: inline-size;
    transition: grid-template-columns 460ms var(--ease-settle), column-gap 460ms var(--ease-settle);
}

.security-workspace.open .securities table {
    width: calc((100cqw - 24px) / 2.2);
}

/*
   The pane, too, is laid out at its open width from the start, so its list never reflows while the track grows (it
   would change from five columns to six on the way, and its height with it): it slides in from the workspace's right
   edge, which clips it there instead of letting the page scroll sideways.
*/
.security-workspace {
    overflow-x: clip;
}

.security-workspace .security-pane {
    width: calc((100cqw - 24px) * 1.2 / 2.2);
}

.security-workspace .securities .number {
    transition: opacity 200ms ease-out;
}

@starting-style {
    .security-workspace .securities .wide {
        opacity: 0;
    }
}

.security-workspace.hiding .securities .number,
.security-workspace.closing .securities .number {
    opacity: 0;
    transition: opacity 120ms cubic-bezier(0.4, 0, 1, 1);
}

.security-pane {
    transition: opacity 260ms ease-out 60ms, transform 420ms var(--ease-out);
}

@starting-style {
    .security-pane {
        opacity: 0;
        transform: translateX(24px);
    }

    /* Back from Dokumente the pane is simply there again, as the page fades in. */
    .security-pane[data-restored] {
        opacity: 1;
        transform: none;
    }
}

.security-workspace.closing {
    transition: grid-template-columns 300ms cubic-bezier(0.4, 0, 0.2, 1) 20ms, column-gap 300ms cubic-bezier(0.4, 0, 0.2, 1) 20ms;
}

.security-workspace.closing .security-pane {
    opacity: 0;
    transform: translateX(24px);
    pointer-events: none;
    transition: opacity 160ms cubic-bezier(0.4, 0, 1, 1), transform 240ms cubic-bezier(0.4, 0, 1, 1);
}

/* Without movement the layout changes at once; the pane and the columns only fade. */
@media (prefers-reduced-motion: reduce) {
    .security-workspace,
    .security-workspace.closing {
        transition: none;
    }

    .security-pane {
        transition: opacity 240ms ease-out;
    }

    .security-workspace.closing .security-pane {
        transform: none;
        transition: opacity 160ms ease-out;
    }

    @starting-style {
        .security-pane {
            transform: none;
        }
    }
}

/*
   Another Security while the pane is open: the pane keeps its place and width and fades through, never showing both
   lists at once. An open preview collapses first (.collapsing, 300ms, CollapsingTime in Dashboard), then the old
   contents fade out (.swapping, 140ms, SwappingTime), then the new ones fade up 6px, header first and row by row
   20ms apart, the last no later than 200ms (.entering). js/event-preview.js eases the pane's height from the old list
   to the new one, so a shorter list doesn't make the page jump when it is scrolled to the end. The list playing out
   answers no clicks.
*/
.pane-content {
    display: contents;
}

.security-workspace.collapsing .security-pane,
.security-workspace.swapping .security-pane {
    pointer-events: none;
}

.security-workspace.swapping .pane-content > * {
    opacity: 0;
    transition: opacity 140ms cubic-bezier(0.4, 0, 1, 1);
}

.pane-content.entering > header,
.pane-content.entering > .standing,
.pane-content.entering > .gain-breakdown,
.pane-content.entering > .gain-note,
.pane-content.entering > .empty,
.pane-content.entering .event-table > thead > tr,
.pane-content.entering .event-table > tbody > tr {
    animation: pane-fade-up 280ms var(--ease-out) both;
}

.pane-content.entering > .standing,
.pane-content.entering > .gain-breakdown,
.pane-content.entering > .gain-note,
.pane-content.entering > .empty {
    animation-delay: 20ms;
}

.pane-content.entering .event-table > thead > tr {
    animation-delay: 40ms;
}

.pane-content.entering .event-table > tbody > tr {
    animation-delay: min(calc(40ms + sibling-index() * 20ms), 200ms);
}

/* The rows below their place would make the list scroll for a moment and show its scrollbar. */
.pane-content.entering .event-scroll {
    overflow: hidden;
}

@keyframes pane-fade-up {
    from {
        opacity: 0;
        translate: 0 6px;
    }
}

@media (prefers-reduced-motion: reduce) {
    @keyframes pane-fade-up {
        from {
            opacity: 0;
        }
    }
}

.security-pane {
    position: sticky;
    top: 16px;
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 32px);
    padding: 18px 20px;
    background: var(--sheet);
    border: 1px solid var(--rule);
    border-radius: 8px;
}

.security-pane > *,
.pane-content > * {
    flex-shrink: 0;
}

.security-pane header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
}

.security-pane h2 {
    margin: 0 0 2px;
    font-size: 18px;
    color: var(--figure);
}

.security-pane .isin {
    font-family: var(--font-code);
    font-size: 12px;
    color: var(--muted);
}

/* The Einstandskurs and the close beside it: each a label over its figure, as in the figure row; the close carries
   its signed gain or loss per piece. A narrow pane stacks them. */
.security-pane .standing {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 40px;
    margin: 14px 0 12px;
    padding-bottom: 14px;
    border-bottom: 1px solid var(--rule);
    color: var(--muted);
    font-size: 13px;
}

.security-pane .standing dd {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 2px 10px;
    margin: 4px 0 0;
}

.security-pane .standing .difference {
    font-size: 14px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    color: var(--text);
}

/* Its hint on hover: how the rate is figured and why it isn't the XIRR or TWR. The ⓘ stays muted beside the tone. */
.security-pane .standing .difference[title] {
    cursor: help;
}

.security-pane .standing .hint-icon {
    width: 13px;
    height: 13px;
    margin-left: 3px;
    vertical-align: -2px;
    fill: none;
    stroke: var(--muted);
    stroke-width: 1.3;
    stroke-linecap: round;
    opacity: 0.7;
    /* Not a target itself: an outlined SVG is hit only on its stroke and keeps the span's title from showing. The
       pointer falls through to the span, so the whole figure and its ⓘ show the hint alike. */
    pointer-events: none;
}

.security-pane .standing .difference:hover .hint-icon,
.security-pane .standing .figure-hint:hover .hint-icon {
    opacity: 1;
}

/* A figure that says on hover how it is counted, its ⓘ beside it like the close's difference. */
.security-pane .standing .figure-hint {
    cursor: help;
    white-space: nowrap;
}

/* The Rückflussquote's bar under its figure: filled as far as the stake has come back, 100 % marked. It reaches past
   100 % (up to 200 %), so a quote above it shows where the stake was back. */
.security-pane .payback .return-ratio dd {
    flex-direction: column;
    align-items: stretch;
}

.security-pane .ratio-bar {
    display: block;
    width: 140px;
    height: 10px;
    overflow: visible;
}

.security-pane .ratio-track {
    fill: var(--rule);
}

.security-pane .ratio-fill {
    fill: var(--accent);
}

.security-pane .ratio-mark {
    stroke: var(--figure);
    stroke-width: 1.5;
}

.security-pane .standing .difference.positive {
    color: var(--positive);
}

.security-pane .standing .difference.negative {
    color: var(--negative);
}

.security-pane .standing strong {
    font-family: var(--font-figure);
    font-size: 26px;
    font-weight: 600;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
    color: var(--figure);
}

.security-pane .empty {
    margin: 0;
    color: var(--muted);
}

/* The Gewinn, line by line: each label beside what it adds, the sum under a rule. */
.security-pane .gain-breakdown {
    display: grid;
    gap: 3px;
    margin: 0 0 12px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--rule);
    font-size: 13px;
}

.security-pane .gain-breakdown > div {
    display: flex;
    justify-content: space-between;
    gap: 16px;
}

.security-pane .gain-breakdown dt {
    color: var(--muted);
}

/* A line whose label doesn't say how it is figured says so on hover. */
.security-pane .gain-breakdown > div[title] {
    cursor: help;
}

.security-pane .gain-breakdown dd {
    margin: 0;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    color: var(--text);
}

.security-pane .gain-breakdown dd.positive {
    color: var(--positive);
}

.security-pane .gain-breakdown dd.negative {
    color: var(--negative);
}

/* A line that only makes up for another (the OeKB offset) is no gain of its own: grey, like its label. */
.security-pane .gain-breakdown > div:not(.total) dd.neutral {
    color: var(--muted);
}

.security-pane .gain-breakdown .total {
    margin-top: 3px;
    padding-top: 6px;
    border-top: 1px solid var(--rule);
    font-weight: 600;
}

.security-pane .gain-breakdown .total dt {
    color: var(--text);
}

/* The Kosten block: a missing TER is no amount, so it stays grey. */
.security-pane .costs dd.missing {
    color: var(--muted);
}

.security-pane .costs .ter {
    align-items: center;
}

.security-pane .costs .ter input {
    width: 8em;
    padding: 2px 6px;
    font-size: 13px;
    text-align: right;
}

/* A year the Kosteninformation states: the TER estimate to compare, in grey, left of its amount to correct, so the
   field lines up with the TER field above. */
.security-pane .costs .cost-year .estimate {
    margin-right: 8px;
    font-size: 13px;
    color: var(--muted);
}

.security-pane .ter-error {
    margin: -6px 0 12px;
    font-size: 13px;
    color: var(--negative);
}

.security-pane .gain-note {
    margin: 0 0 12px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--rule);
    font-size: 13px;
    color: var(--muted);
}

/*
   Only the list scrolls; the header and the Einstandskurs stay put. Its scrollbar has its room from the start, so the
   columns don't move when a growing preview makes the list scroll.
*/
.security-pane .event-scroll {
    flex-shrink: 1;
    min-height: 0;
    overflow: auto;
    overscroll-behavior: contain;
    scrollbar-gutter: stable;
}

.event-table {
    width: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
}

/*
   The list fits the pane rather than scrolling sideways: values never break, but a header may take two lines and an
   OeKB correction's "before → after" breaks at the arrow (.entry-change).
*/
.event-table > thead > tr > th,
.event-table > tbody > tr > td {
    padding: 8px;
    border-bottom: 1px solid var(--rule);
    white-space: nowrap;
}

.event-table > thead > tr > th,
.event-table > tbody > tr > td.entry-change {
    white-space: normal;
}

.event-table > thead > tr > th {
    vertical-align: bottom;
}

.event-table .entry-change span {
    white-space: nowrap;
}

/* In a narrow pane Art goes under the date, as an ISIN goes under a name, and six columns become five. */
.security-pane {
    container: security-pane / inline-size;
}

.event-table .kind-inline {
    display: none;
}

/* The event's symbol on the chart (EventMark.razor) before its Art in the Security pane and its Vorgang in the Erklären
   tables, so a buy tells itself from a sell without colour: a filled accent triangle up, an open one down, an open
   diamond in the figure colour for a payout. */
.event-mark {
    width: 11px;
    height: 11px;
    margin-right: 8px;
    vertical-align: -1px;
    overflow: visible;
}

/* A Vorgang that wraps (a narrow sheet) carries on under its text, not under the mark: a hanging indent of the mark's
   width and gap, on top of the cell's own 8px. */
.explain-table td:has(> .event-mark) {
    padding-left: 27px;
    text-indent: -19px;
}

/* The filled triangle takes the open one's stroke too, so both are the same size. */
.event-mark.buy path {
    fill: var(--accent);
    stroke: var(--accent);
    stroke-width: 1.8;
    stroke-linejoin: round;
}

.event-mark.sell path {
    fill: none;
    stroke: var(--accent);
    stroke-width: 1.8;
    stroke-linejoin: round;
}

.event-mark.payout path {
    fill: none;
    stroke: var(--figure);
    stroke-width: 1.6;
    stroke-linejoin: round;
}

@container security-pane (max-width: 619px) {
    .event-table .kind {
        display: none;
    }

    .event-table .kind-inline {
        display: block;
        font-size: 12px;
        color: var(--muted);
    }
}

.event-table > thead > tr > th {
    position: sticky;
    top: 0;
    /* Above the rows: a dimmed row (opacity) would otherwise paint over it while scrolling. */
    z-index: 1;
    font-size: 12px;
    font-weight: 400;
    color: var(--muted);
    background: var(--sheet);
    border-bottom-color: var(--rule-strong);
}

/* 12px of inner room on both sides, as in the Wertpapiere table, so a wash (an opened or linked row) doesn't end flush with the text. */
.event-table > thead > tr > th:first-child,
.event-table > tbody > tr > td:first-child {
    padding-left: 12px;
}

.event-table > thead > tr > th:last-child,
.event-table > tbody > tr > td:last-child {
    padding-right: 12px;
}

.event-table .text {
    text-align: left;
}

.event-table .number {
    text-align: right;
}

/* An OeKB correction moves no cash: a quiet row. */
.event-table > tbody > tr.correction > td {
    color: var(--muted);
}

.event-table > tbody > tr.range-divider > td {
    padding-top: 18px;
    font-size: 12px;
    color: var(--muted);
    border-bottom-color: var(--rule-strong);
}

/* Before the range: kept for the running Einstandskurs, set back in muted; a quiet correction there fades further. */
.event-table > tbody > tr.before-range > td {
    color: var(--muted);
}

.event-table > tbody > tr.before-range.correction > td {
    opacity: 0.7;
}

/* The rows of the ring clicked on the chart (js/value-chart.js), in the selection wash like a selected table row. */
.event-table > tbody > tr.linked > td {
    background: var(--selection);
}

/*
   A Steuern row whose KESt flatex booked differently: tinted like the year's warning (.booked-kest shows flatex's
   figure). Ahead of the rules below, so hovering and an open preview still show.
*/
.tax-table > tbody > tr.differs > td {
    background: var(--warning-bg);
}

/*
   A row with a document opens it below itself, like a row of the Wertpapiere table; an OeKB correction has none and
   stays a plain row. The date is the button for the keyboard, and focus rings it, as it rings the name in the table.
*/
.event-table > tbody > tr.previewable,
.tax-table > tbody > tr.previewable {
    cursor: pointer;
}

.event-table > tbody > tr.previewable > td,
.tax-table > tbody > tr.previewable > td {
    transition: background-color 120ms ease-out;
}

.event-table > tbody > tr.previewable:hover > td,
.tax-table > tbody > tr.previewable:hover > td {
    background: var(--raised);
}

.event-table > tbody > tr.previewing > td,
.event-table > tbody > tr.previewing:hover > td,
.tax-table > tbody > tr.previewing > td,
.tax-table > tbody > tr.previewing:hover > td {
    background: var(--selection);
    border-bottom-color: transparent;
}

.event-table > tbody > tr.previewing > td:first-child,
.tax-table > tbody > tr.previewing > td:first-child {
    color: var(--accent);
}

.event-table .row-toggle,
.tax-table .row-toggle {
    padding: 0;
    border: none;
    border-radius: 0;
    background: none;
    color: inherit;
    text-align: left;
}

.event-table .row-toggle:hover:not(:disabled),
.tax-table .row-toggle:hover:not(:disabled) {
    background: none;
}

.event-table .row-toggle:focus-visible,
.tax-table .row-toggle:focus-visible {
    border-radius: 2px;
}

/*
   The preview below its row, in the list: what was read, then the pages, which scroll inside its fixed height so the
   list around it stays where it is.
*/
/* Right under its row, as wide as the list's columns. */
.event-table > tbody > tr.preview-row > td,
.tax-table > tbody > tr.preview-row > td {
    padding: 0;
    border-bottom: none;
    white-space: normal;
}

/*
   Opening, the row expands downwards (grid rows 0fr to 1fr, 560ms, eased in and out evenly) while the preview fades up
   inside it, what was read already there, so a box this tall settles in rather than sweeping in fully drawn; its pages
   fade up once rendered, in place of "Wird geladen …". js/event-preview.js keeps it in view in the list as it grows.
   Closing, a copy of it fades and collapses in 380ms where it was (.leaving; 300ms when another Security comes next,
   CollapsingTime in Dashboard), so another row's preview opening at the same time moves with it. Back from Dokumente
   it is simply there.
*/
.preview-reveal {
    display: grid;
    grid-template-rows: 1fr;
    /* Even, not the soft-start curve: that lays most of so tall a box down in its first 150ms. */
    transition: grid-template-rows 560ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* The room around the preview and the rule under it are inside what collapses, so nothing of it is left at 0fr. */
.preview-reveal > div {
    min-height: 0;
    overflow: hidden;
}

.preview-reveal .event-preview {
    margin: 8px 0 16px;
    transition: opacity 360ms ease-out 80ms;
}

.preview-reveal > div::after {
    content: "";
    display: block;
    border-bottom: 1px solid var(--rule);
}

.preview-reveal.leaving {
    grid-template-rows: 0fr;
    transition: grid-template-rows 380ms cubic-bezier(0.4, 0, 0.2, 1);
}

.preview-reveal.leaving .event-preview {
    opacity: 0;
    transition: opacity 220ms cubic-bezier(0.4, 0, 1, 1);
}

.security-workspace.collapsing .preview-reveal.leaving {
    transition-duration: 300ms;
}

.security-workspace.collapsing .preview-reveal.leaving .event-preview {
    transition-duration: 180ms;
}

.event-preview .zoom-bar,
.event-preview .document-pages {
    transition: opacity 200ms ease-out;
}

@starting-style {
    .preview-reveal {
        grid-template-rows: 0fr;
    }

    .preview-reveal .event-preview,
    .event-preview .zoom-bar,
    .event-preview .document-pages {
        opacity: 0;
    }

    .preview-reveal.leaving,
    .security-pane[data-restored] .preview-reveal,
    .tax-page[data-restored] .preview-reveal {
        grid-template-rows: 1fr;
    }

    .preview-reveal.leaving .event-preview,
    .security-pane[data-restored] .preview-reveal .event-preview,
    .security-pane[data-restored] .event-preview .zoom-bar,
    .security-pane[data-restored] .event-preview .document-pages,
    .tax-page[data-restored] .preview-reveal .event-preview,
    .tax-page[data-restored] .event-preview .zoom-bar,
    .tax-page[data-restored] .event-preview .document-pages,
    .preview-reveal.leaving .event-preview .zoom-bar,
    .preview-reveal.leaving .event-preview .document-pages {
        opacity: 1;
    }
}

/* Without movement the preview fades in and out where it opens; its row takes the room at once. */
@media (prefers-reduced-motion: reduce) {
    .preview-reveal,
    .preview-reveal.leaving {
        grid-template-rows: 1fr;
        transition: opacity 200ms ease-out;
    }

    .preview-reveal.leaving {
        opacity: 0;
        transition-duration: 160ms;
    }

    /* The row fades as one. */
    .preview-reveal .event-preview,
    .preview-reveal.leaving .event-preview {
        opacity: 1;
        transition: none;
    }

    @starting-style {
        .preview-reveal {
            grid-template-rows: 1fr;
            opacity: 0;
        }

        .preview-reveal.leaving,
        .security-pane[data-restored] .preview-reveal,
        .tax-page[data-restored] .preview-reveal {
            opacity: 1;
        }
    }
}

.event-preview {
    display: flex;
    flex-direction: column;
    height: max(360px, 60vh);
    padding: 14px 16px 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    background: var(--sheet);
    border: 1px solid var(--rule);
    border-radius: 8px;
}

.event-preview > * {
    flex-shrink: 0;
}

.event-preview header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 4px;
}

.event-preview .file-name {
    margin: 0;
    min-width: 0;
    overflow-wrap: anywhere;
    font-family: var(--font-code);
    font-size: 12px;
    color: var(--muted);
}

.event-preview .preview-actions {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-shrink: 0;
}

.event-preview .document-pages {
    flex: 1 1 auto;
    min-height: 200px;
    overflow: auto;
    overscroll-behavior: contain;
}

/* ---------- Sheets: Erklären and the side panels ---------- */

.explain-backdrop {
    position: fixed;
    inset: 0;
    z-index: 20;
    background: var(--scrim);
}

.explain-panel {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 21;
    width: min(680px, 100%);
    overflow-y: auto;
    padding: 28px 40px 40px;
    background: var(--sheet);
    border-left: 1px solid var(--rule);
    box-shadow: -16px 0 48px var(--shadow-color);
    line-height: 1.55;
}

.explain-panel:focus {
    outline: none;
}

/*
   The sheet slides in from the right while the dashboard dims behind it, and slides back out faster. Transitions, not
   keyframes: closing while it still arrives turns it around where it is. Off screen it also moves its shadow's reach
   (16px offset + 48px blur), so no shadow edge is left showing at the window's right.
*/
.explain-backdrop {
    transition: opacity 240ms ease-out;
}

.explain-panel {
    --sheet-away: translateX(calc(100% + 64px));
    transition: transform 380ms var(--ease-out);
}

/* The content fades in once the panel has nearly arrived; it does not move on its own. */
.explain-panel > * {
    transition: opacity 300ms 120ms ease-out;
}

.explain-backdrop.closing {
    opacity: 0;
    transition: opacity 200ms ease-in;
}

.explain-panel.closing {
    transform: var(--sheet-away);
    transition: transform 200ms cubic-bezier(0.4, 0, 1, 1);
}

@starting-style {
    .explain-backdrop,
    .explain-panel > * {
        opacity: 0;
    }

    .explain-panel {
        transform: var(--sheet-away);
    }
}

/* Without movement the sheet fades in and out in place, so opening and closing stay visible. */
@media (prefers-reduced-motion: reduce) {
    .explain-panel {
        --sheet-away: none;
        transition: opacity 200ms ease-out;
    }

    .explain-panel > * {
        transition: none;
    }

    .explain-panel.closing {
        opacity: 0;
        transition: opacity 200ms ease-in;
    }

    @starting-style {
        .explain-panel {
            opacity: 0;
        }
    }
}

.explain-panel header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
}

.explain-panel h2 {
    margin: 0;
    font-size: 18px;
    font-weight: 600;
}

.explain-panel h3 {
    margin: 28px 0 8px;
    font-size: 14px;
    font-weight: 600;
}

.explain-panel p {
    max-width: 70ch;
}

/* The explained figure, at the sheet's own specimen size. */
.explain-result {
    margin: 18px 0 14px;
}

.explain-result span {
    display: block;
    font-size: 13px;
    color: var(--muted);
}

.explain-result strong {
    display: block;
    margin-top: 2px;
    color: var(--figure);
    font-family: var(--font-figure);
    font-size: 56px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 1.05;
    letter-spacing: -0.03em;
}

/* "p.a." or "in diesem Zeitraum" after a rate: set in text, not in the gain colour. */
/* Set back like the hero's cents. */
.explain-result .cents {
    display: inline;
    margin: 0;
    font-size: 0.5em;
    font-weight: 300;
    color: var(--muted);
    letter-spacing: -0.01em;
}

/* A signed amount's cents keep its sign colour, as in the figure row. */
.explain-result strong.positive .cents,
.explain-result strong.negative .cents {
    color: inherit;
}

.explain-result .result-suffix {
    display: block;
    margin-top: 2px;
    font-size: 15px;
    color: var(--muted);
}

.explain-panel strong.positive,
.explain-panel .positive {
    color: var(--positive);
}

.explain-panel strong.negative,
.explain-panel .negative {
    color: var(--negative);
}

.explain-table {
    width: 100%;
    border-collapse: collapse;
}

.explain-table th,
.explain-table td {
    padding: 7px 8px;
    border-bottom: 1px solid var(--rule);
    text-align: left;
}

.explain-table th:first-child,
.explain-table td:first-child {
    padding-left: 0;
}

.explain-table th {
    font-size: 12px;
    font-weight: 400;
    color: var(--muted);
    border-bottom-color: var(--rule-strong);
}

.explain-table .number {
    text-align: right;
    white-space: nowrap;
}

/*
   Amounts stay on one line, their headers may wrap: in the narrower side sheet (Prognose) six columns only fit that way.
   Headers sit on the rule, so a two-line one lines up with its neighbours.
*/
.explain-table th.number {
    white-space: normal;
}

.explain-table th {
    vertical-align: bottom;
}

.explain-table tfoot td {
    font-weight: 600;
    color: var(--figure);
    border-top: 1px solid var(--figure);
    border-bottom: none;
}

.side-panel {
    width: min(560px, 100%);
}

.side-panel .card {
    padding: 20px 0 0;
}

.side-panel .setting {
    margin: 20px 0 8px;
}

/* Settings stacked in one section share the label column, so their fields line up. */
:is(.side-panel, .settings-page) .setting-list {
    display: grid;
    grid-template-columns: max-content max-content;
    gap: 12px 8px;
    align-items: center;
    margin: 20px 0 8px;
}

.settings-page .setting-list {
    margin-top: 12px;
}

:is(.side-panel, .settings-page) .setting-list > .setting {
    display: grid;
    grid-column: 1 / -1;
    grid-template-columns: subgrid;
    margin: 0;
}

/* Centred on its capitals, not its line box: Segoe UI's line box has more room above the letters than below, so a
   label centred on it looks sunk next to the taller select. */
:is(.side-panel, .settings-page) .setting-list > .setting > span {
    text-box: trim-both cap alphabetic;
}

/* The question before every Export, over the whole page. */
.confirm-backdrop {
    position: fixed;
    inset: 0;
    z-index: 30;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background: var(--scrim);
}

.confirm-dialog {
    max-width: 480px;
    padding: 24px 28px;
    background: var(--sheet);
    border: 1px solid var(--rule);
    border-radius: 10px;
    box-shadow: 0 16px 48px var(--shadow-color);
}

/* The question arrives quickly and from where it stands: the page dims, the dialog fades up from just below full size. */
.confirm-backdrop {
    transition: opacity 160ms ease-out;
}

.confirm-dialog {
    transition: opacity 200ms ease-out, transform 200ms var(--ease-out);
}

@starting-style {
    .confirm-backdrop,
    .confirm-dialog {
        opacity: 0;
    }

    .confirm-dialog {
        transform: scale(0.97);
    }
}

@media (prefers-reduced-motion: reduce) {
    .confirm-dialog {
        transition: opacity 200ms ease-out;
    }
}

.confirm-dialog h2 {
    margin: 0 0 8px;
    font-size: 18px;
}

.confirm-dialog .actions {
    display: flex;
    gap: 10px;
    margin-top: 18px;
}

/* ---------- Start: unlock, create, run mode, first steps ---------- */

.start {
    max-width: 540px;
    margin: 0 auto;
    padding: 72px 24px 48px;
}

.start-head {
    display: flex;
    align-items: center;
    gap: 14px;
}

.start-head .brand-mark {
    width: 40px;
    height: 40px;
}

.start-head h1 {
    margin: 0;
    font-family: var(--font-figure);
    font-size: 40px;
    font-weight: 600;
    letter-spacing: -0.025em;
    line-height: 1.1;
}

.start-lead {
    margin: 14px 0 28px;
    max-width: 46ch;
    font-size: 15px;
    color: var(--text);
}

/* The trial, on one ruled line above the forms. */
.start-sample {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px 20px;
    margin: 0;
    padding: 14px 0;
    border-top: 1px solid var(--rule);
    color: var(--muted);
}

/* One decision per section, divided by rules, not boxed. */
.card {
    padding: 22px 0;
    border-top: 1px solid var(--rule);
}

.card h1,
.card h2 {
    margin: 0 0 10px;
    font-size: 18px;
    font-weight: 600;
}

.card h3 {
    margin: 20px 0 8px;
    font-size: 14px;
}

.card.secondary h2 {
    font-size: 15px;
}

/* "Erweiterte Optionen" on the lock screen: folded away, its heading the toggle, with the chevron of the selects. */
.advanced-options summary {
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    list-style: none;
}

.advanced-options summary::-webkit-details-marker {
    display: none;
}

.advanced-options summary::before {
    content: "";
    width: 12px;
    height: 8px;
    background: var(--chevron) no-repeat center / 12px 8px;
    transform: rotate(-90deg);
    transition: transform 120ms ease-out;
}

.advanced-options[open] summary::before {
    transform: none;
}

.advanced-options summary h2 {
    margin: 0;
}

.advanced-options summary:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 3px;
}

.advanced-options[open] summary {
    margin-bottom: 14px;
}

.card form {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.card label {
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
}

.card input[type="password"],
.card input[type="text"] {
    padding: 10px 12px;
}

.card form button {
    align-self: flex-start;
    margin-top: 8px;
}

/* "Vault anlegen" on the web: what deletes a Vault that lives only in the browser, confirmed before it exists (#399). */
.card .creation-notice {
    margin-top: 8px;
    padding: 10px 14px;
    color: var(--text);
    background: var(--warning-bg);
    border: 1px solid var(--warning-rule);
    border-radius: 6px;
}

.creation-notice p {
    margin: 0;
}

.creation-notice .actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 12px;
}

.card .creation-notice button {
    margin-top: 0;
}

/* The web's Backup card: clearing the browser data takes the Vault with it (#365). */
.browser-data-hint {
    margin: 10px 0 0;
    font-size: 13px;
    color: var(--muted);
}

.steps {
    padding-left: 20px;
}

.steps li {
    margin-bottom: 14px;
}

.steps li.done {
    color: var(--muted);
    text-decoration: line-through;
}

.steps button {
    margin-top: 8px;
}

.run-modes .third-parties {
    margin: 0 0 14px;
    padding-left: 20px;
}

.run-modes .third-parties li {
    margin-bottom: 6px;
}

.run-modes .learns,
.run-modes .third-parties-none {
    display: block;
    color: var(--muted);
}

/* ---------- Dokumente, Prognose, Kursdaten and Einstellungen pages ---------- */

.documents-page,
.forecast-page,
.market-page,
.tax-page {
    max-width: 1280px;
    padding: 26px 48px 64px;
}

/* Einstellungen is read, not worked in: one narrow column of 640px, not a form stretched across the window. */
.settings-page {
    max-width: calc(640px + 2 * 48px);
    padding: 26px 48px 64px;
}

.market-page > header,
.settings-page > header {
    margin-bottom: 20px;
}

:is(.documents-page, .forecast-page, .market-page, .settings-page, .tax-page) > header .title {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 8px 20px;
}

:is(.documents-page, .forecast-page, .market-page, .settings-page, .tax-page) h1 {
    margin: 0;
    font-family: var(--font-figure);
    font-size: 34px;
    font-weight: 600;
    letter-spacing: -0.025em;
}

/* On the Prognose page the Basis figure leads; the title steps down to the dashboard's. */
.forecast-page h1 {
    font-family: var(--font-ui);
    font-size: 15px;
    letter-spacing: 0;
}

.documents-page > header .actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 20px;
}

.documents-page > header .setting {
    display: flex;
    margin-top: 10px;
    font-size: 13px;
}

.drop-zone {
    margin: 16px 0;
    padding: 18px;
    text-align: center;
    color: var(--muted);
    border: 1px dashed var(--rule-strong);
    border-radius: 8px;
    transition: border-color 150ms ease-out, background-color 150ms ease-out, color 150ms ease-out;
}

body.dragging-files .drop-zone {
    color: var(--accent);
    border-color: var(--accent);
    background: var(--selection);
}

.import-results {
    margin: 0 0 12px;
    padding-left: 20px;
}

.import-results li {
    margin-bottom: 6px;
}

.import-results .file-name {
    display: block;
    margin-left: 0;
    word-break: break-all;
}

.import-results li.negative {
    color: var(--negative);
}

/* After an import with new documents: "N neue Dokumente – Backup speichern". */
.import-backup-hint {
    margin: 0 0 12px;
}

.document-browser {
    margin-top: 16px;
}

/* Only the table scrolls sideways: a scroller clips whatever is drawn past its edge, the filter's focus ring included. */
.document-browser .table-scroll {
    overflow-x: auto;
}

.document-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}

.document-filters input[type="search"] {
    flex: 1 1 260px;
}

.document-filters .chip {
    padding: 6px 12px;
    border-radius: 999px;
    color: var(--muted);
}

.document-filters .chip.active {
    color: var(--figure);
    background: var(--raised);
    border-color: var(--muted);
    font-weight: 600;
}

.document-filters .chip .count {
    margin-left: 4px;
    font-weight: 600;
}

.document-table th,
.document-table td {
    padding: 9px 12px;
    text-align: left;
    border-bottom: 1px solid var(--rule);
}

/* 12px of inner room on both sides, as in the Wertpapiere table beside the pane, so the wash of the opened document
   (and the hover fill) doesn't end flush with the date. */
.document-table th:first-child,
.document-table td:first-child {
    padding-left: 12px;
}

.document-table th {
    font-size: 12px;
    font-weight: 400;
    color: var(--muted);
    border-bottom-color: var(--rule-strong);
}

.document-table .date,
.document-table .isin {
    white-space: nowrap;
}

.document-table .isin {
    font-family: var(--font-code);
    font-size: 12px;
    color: var(--muted);
}

/* A row's problem badge: the shared .badge, with its label in full. */
.document-table .badge {
    margin-left: 8px;
    padding: 0 8px;
    font-weight: 400;
    white-space: nowrap;
}

.document-table .badge:not(.quiet) {
    color: var(--negative);
    background: color-mix(in srgb, var(--negative) 14%, transparent);
}

/*
   With a document open, the list stays on the left and the viewer takes the right. Closed, the viewer's track is
   0fr, so opening grows it and the list narrows beside it instead of jumping; the ✕ plays it back (.closing).
*/
.document-workspace {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 0fr);
    column-gap: 0;
    align-items: start;
}

.documents-page.viewing:not(.closing) {
    max-width: 1640px;
}

.documents-page.viewing:not(.closing) .document-workspace {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
    column-gap: 24px;
}

/*
   The viewer stays in view beside the list. Only its pages scroll; the heading, readout and zoom above them stay put.
   The viewer itself scrolls only if what is above the pages leaves them less than their minimum height.
*/
.document-viewer {
    position: sticky;
    top: 16px;
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 32px);
    margin-top: 16px;
    padding: 18px 20px;
    overflow-y: auto;
    background: var(--sheet);
    border: 1px solid var(--rule);
    border-radius: 8px;
}

.document-viewer > * {
    flex-shrink: 0;
}

/*
   Opened from a Security's pane, the viewer names where it came from above the title, where the eye lands with the
   document: one line, the name cut short if it must be, and a way back to it.
*/
.document-viewer .viewer-origin {
    display: flex;
    align-items: center;
    gap: 6px;
    align-self: flex-start;
    max-width: 100%;
    min-height: 24px;
    margin: -4px 0 6px;
    font-size: 13px;
}

.document-viewer .viewer-origin svg {
    flex-shrink: 0;
    width: 7px;
    height: 11px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.document-viewer .viewer-origin span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.document-viewer header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 12px;
}

.document-viewer h2 {
    margin: 0;
    font-size: 18px;
}

.document-viewer .file-name {
    margin: 4px 0 0;
    overflow-wrap: anywhere;
}

/* The ✕ in the corner, the actions below it: Löschen never sits right next to the close control. */
.document-viewer .viewer-side {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 6px;
    flex-shrink: 0;
}

.document-viewer .viewer-actions {
    display: flex;
    align-items: center;
    gap: 10px;
}

.zoom-bar {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    margin-bottom: 8px;
}

.zoom-bar button {
    padding: 2px 12px;
}

.zoom-level {
    min-width: 4.5em;
    text-align: center;
    font-variant-numeric: tabular-nums;
    color: var(--muted);
}

/* The pages take the viewer's remaining height and scroll in both directions: zoomed, they are wider than it. */
.document-viewer .document-pages {
    flex: 1 1 auto;
    min-height: 240px;
    overflow: auto;
}

/*
   Opening a document, the list narrows over 460ms (a softer start than ease-out, so it does not lurch) while the viewer slides in 24px from the side it takes and fades up
   just behind it, so the squeeze of its first frames is not seen; its pages, rendered a moment later, fade up in place
   of "Wird geladen …" rather than flashing in. Closing is quicker and goes the other way: the viewer fades and slides
   out first, the list widens back after it (320ms in all, ClosingTime in DocumentsPage).
   The page is a .workspace > main, which brings the page-switch transition; it is repeated here with the width.
*/
.workspace > main.documents-page {
    transition: opacity 400ms var(--ease-settle), translate 560ms var(--ease-settle), max-width 460ms var(--ease-settle);
}

.document-workspace {
    transition: grid-template-columns 460ms var(--ease-settle), column-gap 460ms var(--ease-settle);
}

.document-viewer {
    transition: opacity 260ms ease-out 60ms, transform 420ms var(--ease-out);
}

.document-viewer .document-pages {
    transition: opacity 200ms ease-out;
}

@starting-style {
    .document-viewer,
    .document-viewer .document-pages {
        opacity: 0;
    }

    .document-viewer {
        transform: translateX(24px);
    }
}

.workspace > main.documents-page.closing {
    transition: opacity 320ms var(--ease-out), translate 480ms var(--ease-out), max-width 300ms cubic-bezier(0.4, 0, 0.2, 1) 20ms;
}

.closing .document-workspace {
    transition: grid-template-columns 300ms cubic-bezier(0.4, 0, 0.2, 1) 20ms, column-gap 300ms cubic-bezier(0.4, 0, 0.2, 1) 20ms;
}

.closing .document-viewer {
    opacity: 0;
    transform: translateX(24px);
    pointer-events: none;
    transition: opacity 160ms cubic-bezier(0.4, 0, 1, 1), transform 240ms cubic-bezier(0.4, 0, 1, 1);
}

/* Without movement the layout changes at once and the viewer only fades in and out. */
@media (prefers-reduced-motion: reduce) {
    .workspace > main.documents-page,
    .workspace > main.documents-page.closing {
        transition: opacity 200ms ease-out;
    }

    .document-workspace,
    .closing .document-workspace {
        transition: none;
    }

    .document-viewer {
        transition: opacity 240ms ease-out;
    }

    .closing .document-viewer {
        transform: none;
        transition: opacity 160ms ease-out;
    }

    @starting-style {
        .document-viewer {
            transform: none;
        }
    }
}

/* Pages are PNGs of white paper in either theme; a rule sets each apart. */
.document-pages img {
    display: block;
    width: 100%;
    height: auto;
    margin-bottom: 12px;
    background: #ffffff;
    border: 1px solid var(--rule);
}

.document-pages.zoom-125 img { width: 125%; }
.document-pages.zoom-150 img { width: 150%; }
.document-pages.zoom-200 img { width: 200%; }
.document-pages.zoom-250 img { width: 250%; }
.document-pages.zoom-300 img { width: 300%; }

/* Where the pages overflow, they scroll by dragging them with the mouse (js/document-pages.js). */
.document-pages[data-draggable] {
    cursor: grab;
}

.document-pages[data-dragging] {
    cursor: grabbing;
    user-select: none;
}

/* What was read from the document, above its pages. */
.document-readout {
    margin-bottom: 16px;
}

.document-readout h3 {
    margin: 12px 0 6px;
    font-size: 13px;
    font-weight: 600;
}

.document-readout p {
    margin: 6px 0;
}

.readout-scroll {
    overflow-x: auto;
}

.readout-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}

.readout-table th,
.readout-table td {
    padding: 5px 8px;
    text-align: left;
    white-space: nowrap;
    border-bottom: 1px solid var(--rule);
}

.readout-table th:first-child,
.readout-table td:first-child {
    padding-left: 0;
}

.readout-table th {
    font-size: 12px;
    font-weight: 400;
    color: var(--muted);
}

.readout-table .number {
    text-align: right;
}

.readout-table tr.mismatch td {
    color: var(--negative);
}

.readout-facts {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 4px 16px;
    margin: 0;
    font-size: 13px;
    font-variant-numeric: tabular-nums;
}

.readout-facts dt {
    color: var(--muted);
}

.readout-facts dd {
    margin: 0;
}

/* ---------- flatex Import, market data, refresh ---------- */

.flatex-import .actions {
    display: flex;
    gap: 8px;
}

/* flatex Import: the Fehlerbericht of a failed Import, its trace scrolling in place. */
.error-report .actions {
    align-items: center;
    gap: 16px;
}

.error-report details {
    margin: 0 0 12px;
}

.error-report pre {
    max-height: 240px;
    overflow: auto;
    margin: 8px 0 0;
    padding: 8px 10px;
    border: 1px solid var(--rule);
    font-size: 12px;
    white-space: pre;
}

/* flatex Import: four counts side by side, then the notes. */
.import-summary {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    margin: 0 0 14px;
    border-top: 1px solid var(--rule);
    border-bottom: 1px solid var(--rule);
}

.import-summary div {
    padding: 10px 12px 10px 0;
}

.import-summary div + div {
    padding-left: 12px;
    border-left: 1px solid var(--rule);
}

.import-summary dt {
    color: var(--muted);
    font-size: 12px;
}

.import-summary dd {
    margin: 0;
    color: var(--figure);
    font-family: var(--font-figure);
    font-size: 24px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.import-summary .negative dd {
    color: var(--negative);
}

/* flatex Import: the archive search drawn as the depot's history from today back (ImportTimeline). One row per year,
   newest on top, so the history unrolls downwards as the search goes back. A row is a calendar year of days between
   month hairlines: what is searched is laid in, the window being asked for right now is outlined in the accent and
   breathes, and every document found lands at its date, arriving in the accent and settling to the figure colour
   (known ones to grey). Once a year is searched it is filed: a check is drawn and its counts roll in. */
.import-timeline {
    margin: 14px 0 18px;
}

.import-timeline ol {
    display: grid;
    gap: 4px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.import-timeline li {
    display: grid;
    grid-template-columns: 58px minmax(0, 1fr) 112px;
    align-items: center;
    gap: 12px;
    min-height: 22px;
    transition: opacity 240ms ease-out, translate 360ms var(--ease-out);
}

@starting-style {
    .import-timeline li {
        opacity: 0;
        translate: 0 -6px;
    }
}

/* The initials sit over the track column, one twelfth each: the row's outer columns plus their 12px gaps. */
.timeline-months {
    display: grid;
    grid-template-columns: 70px repeat(12, minmax(0, 1fr)) 124px;
    margin-bottom: 4px;
    color: var(--muted);
    font-size: 12px;
}

.timeline-months span:first-child {
    grid-column: 2;
}

.import-timeline .year {
    display: flex;
    align-items: center;
    gap: 4px;
    color: var(--figure);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.import-timeline .track {
    display: block;
    width: 100%;
    height: 16px;
    overflow: visible;
}

.import-timeline .ground {
    fill: var(--raised);
}

.import-timeline .covered {
    fill: var(--rule-strong);
    transition: x 320ms var(--ease-out), width 320ms var(--ease-out);
}

.import-timeline .month {
    stroke: var(--ground);
    stroke-width: 1;
    vector-effect: non-scaling-stroke;
}

.import-timeline .today {
    stroke: var(--figure);
    stroke-width: 1.5;
    stroke-linecap: round;
    vector-effect: non-scaling-stroke;
}

.import-timeline .window {
    fill: var(--selection);
    stroke: var(--accent);
    stroke-opacity: 0.45;
    stroke-width: 1;
    vector-effect: non-scaling-stroke;
}

/* While flatex answers, which takes seconds, a light runs round the window: a fifth of its edge, clockwise, once
   every 1.8s whatever the window's size (pathLength="100"). */
.import-timeline .window-orbit {
    fill: none;
    stroke: var(--accent);
    stroke-width: 2;
    stroke-linecap: round;
    stroke-dasharray: 20 80;
    vector-effect: non-scaling-stroke;
    animation: timeline-orbit 1800ms linear infinite;
}

@keyframes timeline-orbit {
    from { stroke-dashoffset: 100; }
    to { stroke-dashoffset: 0; }
}

@keyframes timeline-breathe {
    from { opacity: 0.55; }
    to { opacity: 1; }
}

.import-timeline .tick.new {
    fill: var(--figure);
}

.import-timeline .tick.known {
    fill: var(--chart-invested);
}

.import-timeline .tick.landing {
    animation: tick-land 640ms var(--ease-out) backwards;
}

@keyframes tick-land {
    0% {
        opacity: 0;
        transform: translateY(-6px);
        fill: var(--accent);
    }

    35% {
        opacity: 1;
        transform: none;
        fill: var(--accent);
    }
}


/* The seal: when the Import completes, a band of accent light sweeps each year from January on, the oldest year
   first and each 40ms after the one before, so the wave rises through the history to today; every document flashes
   in the accent as the band crosses it. The band runs in the track's own clip (an inner SVG), 760ms at an even pace,
   so a tick's flash is timed from its place: 70ms plus 33ms for each twentieth of the year. */
/* Holds the band's gradient only; out of the flow. */
.timeline-defs {
    position: absolute;
}

.import-timeline .seal-edge {
    stop-color: var(--accent);
    stop-opacity: 0;
}

.import-timeline .seal-core {
    stop-color: var(--accent);
    stop-opacity: 0.6;
}

.import-timeline .seal-light {
    fill: url(#import-seal-light);
    animation: seal-sweep 760ms linear var(--seal-row, 0ms) both;
}

@keyframes seal-sweep {
    from { transform: translateX(0); }
    to { transform: translateX(430px); }
}

.import-timeline.sealing .tick {
    transform-box: fill-box;
    transform-origin: 50% 50%;
    animation: tick-seal 460ms var(--ease-out) calc(var(--seal-row, 0ms) + var(--seal-at, 0ms));
}

@keyframes tick-seal {
    from {
        fill: var(--accent);
        transform: scaleY(1.5);
    }
}

@keyframes tick-seal-still {
    from { fill: var(--accent); }
}

.import-timeline .track.r1 { --seal-row: 40ms; }
.import-timeline .track.r2 { --seal-row: 80ms; }
.import-timeline .track.r3 { --seal-row: 120ms; }
.import-timeline .track.r4 { --seal-row: 160ms; }
.import-timeline .track.r5 { --seal-row: 200ms; }
.import-timeline .track.r6 { --seal-row: 240ms; }
.import-timeline .track.r7 { --seal-row: 280ms; }
.import-timeline .track.r8 { --seal-row: 320ms; }
.import-timeline .track.r9 { --seal-row: 360ms; }
.import-timeline .track.r10 { --seal-row: 400ms; }
.import-timeline .track.r11 { --seal-row: 440ms; }
.import-timeline .track.r12 { --seal-row: 480ms; }
.import-timeline .tick.w0 { --seal-at: 70ms; }
.import-timeline .tick.w1 { --seal-at: 103ms; }
.import-timeline .tick.w2 { --seal-at: 136ms; }
.import-timeline .tick.w3 { --seal-at: 169ms; }
.import-timeline .tick.w4 { --seal-at: 202ms; }
.import-timeline .tick.w5 { --seal-at: 235ms; }
.import-timeline .tick.w6 { --seal-at: 268ms; }
.import-timeline .tick.w7 { --seal-at: 301ms; }
.import-timeline .tick.w8 { --seal-at: 334ms; }
.import-timeline .tick.w9 { --seal-at: 367ms; }
.import-timeline .tick.w10 { --seal-at: 400ms; }
.import-timeline .tick.w11 { --seal-at: 433ms; }
.import-timeline .tick.w12 { --seal-at: 466ms; }
.import-timeline .tick.w13 { --seal-at: 499ms; }
.import-timeline .tick.w14 { --seal-at: 532ms; }
.import-timeline .tick.w15 { --seal-at: 565ms; }
.import-timeline .tick.w16 { --seal-at: 598ms; }
.import-timeline .tick.w17 { --seal-at: 631ms; }
.import-timeline .tick.w18 { --seal-at: 664ms; }
.import-timeline .tick.w19 { --seal-at: 697ms; }

.year-check {
    width: 12px;
    height: 12px;
    fill: none;
    stroke: var(--muted);
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 12;
    animation: import-check 320ms var(--ease-out) 80ms both;
}

.import-timeline li.has-new .year-check {
    stroke: var(--figure);
}

@keyframes import-check {
    from { stroke-dashoffset: 12; }
    to { stroke-dashoffset: 0; }
}

.import-timeline .counts {
    display: block;
    overflow: hidden;
    color: var(--muted);
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.import-timeline .filed {
    display: block;
    animation: roll-in-up 420ms var(--ease-out) both;
}

.import-timeline li.has-new .new {
    color: var(--figure);
    font-weight: 600;
}

.import-recent {
    margin: 4px 0 12px;
    padding-left: 18px;
    color: var(--muted);
}

.import-recent li {
    transition: opacity 240ms ease-out, translate 320ms var(--ease-out);
}

@starting-style {
    .import-recent li {
        opacity: 0;
        translate: 0 -4px;
    }
}

/* The closing moment of a completed Import, above the timeline it completes: what is new in the Vault set as the
   sheet's figure and the sentence on how far back the Vault now reaches. Arriving, the digits come to rest left to
   right like counter wheels and the sentence follows. */
.import-recap {
    margin: 4px 0 0;
}

.recap-label {
    margin: 0 0 2px;
    color: var(--muted);
    font-size: 13px;
}

.recap-figure {
    margin: 0;
    color: var(--figure);
    font-family: var(--font-figure);
    font-size: 56px;
    font-weight: 600;
    line-height: 1.05;
    letter-spacing: -0.03em;
    font-variant-numeric: tabular-nums;
}

.recap-tally {
    display: inline-flex;
    overflow: hidden;
}

.recap-tally > span {
    display: inline-block;
}

.recap-sentence {
    max-width: 42ch;
    margin: 6px 0 0;
    color: var(--text);
    font-size: 17px;
    line-height: 1.5;
    text-wrap: pretty;
}

/* The figure arrives as the seal's wave reaches today, lit by its light for a moment. */
.import-recap.arriving .recap-label,
.import-recap.arriving .recap-sentence {
    animation: recap-fade-up 420ms var(--ease-out) both;
}

.import-recap.arriving .recap-label {
    animation-delay: 700ms;
}

.import-recap.arriving .recap-sentence {
    animation-delay: 920ms;
}

.import-recap.arriving .recap-figure {
    animation: recap-glow 1400ms ease-out 780ms both;
}

@keyframes recap-glow {
    from { text-shadow: 0 0 0 transparent; }
    25% { text-shadow: 0 0 24px color-mix(in srgb, var(--accent) 42%, transparent); }
    to { text-shadow: 0 0 36px transparent; }
}

.import-recap.arriving .recap-tally > span {
    animation: roll-in-up 560ms var(--ease-out) both;
}

.import-recap.arriving .recap-tally .d0 { animation-delay: 760ms; }
.import-recap.arriving .recap-tally .d1 { animation-delay: 800ms; }
.import-recap.arriving .recap-tally .d2 { animation-delay: 840ms; }
.import-recap.arriving .recap-tally .d3 { animation-delay: 880ms; }
.import-recap.arriving .recap-tally .d4 { animation-delay: 920ms; }
.import-recap.arriving .recap-tally .d5 { animation-delay: 960ms; }

@keyframes recap-fade-up {
    from {
        opacity: 0;
        transform: translateY(6px);
    }
}

/* Without movement nothing travels: rows, checks and documents fade in where they belong, the window holds still,
   and the closing moment fades in as one. */
@media (prefers-reduced-motion: reduce) {
    .import-timeline li,
    .import-recent li {
        transition: opacity 200ms ease-out;
    }

    @starting-style {
        .import-timeline li,
        .import-recent li {
            translate: none;
        }
    }

    .import-timeline .covered {
        transition: none;
    }

    /* The light holds still; the window breathes instead, so the wait still shows. */
    .import-timeline .window-orbit {
        display: none;
    }

    .import-timeline .window {
        animation: timeline-breathe 1600ms ease-in-out infinite alternate;
    }

    .import-timeline .tick.landing,
    .year-check,
    .import-timeline .filed {
        animation: roll-fade-in 200ms ease-out both;
    }

    /* The seal without the travelling band: every document lights at once and fades back. */
    .import-timeline .seal-light {
        display: none;
    }

    .import-timeline.sealing .tick {
        animation: tick-seal-still 600ms ease-out 200ms;
    }

    .import-recap.arriving .recap-label,
    .import-recap.arriving .recap-sentence,
    .import-recap.arriving .recap-tally > span[class] {
        animation: roll-fade-in 320ms ease-out both;
        animation-delay: 0ms;
    }
}

.market-problems {
    margin: 0 0 16px;
    padding-left: 28px;
}

.market-table th,
.market-table td {
    padding: 7px 8px 7px 0;
    text-align: left;
    vertical-align: top;
    border-bottom: 1px solid var(--rule);
}

.market-table th {
    font-size: 12px;
    font-weight: 400;
    color: var(--muted);
}

.market-table .name,
.market-table .isin {
    display: block;
}

.market-table .no-price {
    color: var(--warning-ink);
}

.market-data .override {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.refresh-status {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 12px;
    margin: 8px 0;
}

.refresh-status .refresh-step {
    flex-basis: 100%;
}

.refresh-status progress {
    flex: 1;
    min-width: 160px;
    height: 6px;
}

.refresh-status .refresh-count {
    color: var(--muted);
    font-size: 12px;
    white-space: nowrap;
}

/* An expert detail: small and quiet, below everything else in the Sicherung section. */
/* ---------- Prognose ---------- */

.forecast-page > header .range {
    margin: 4px 0 0;
    color: var(--muted);
}

.forecast-page .forecast-views {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 18px;
}

/* The scenarios as one hairline row; Basis is the figure set large. */
.forecast-page .forecast-figures {
    margin-top: 28px;
}

.forecast-page .forecast-hero {
    margin-top: 32px;
}

.forecast-hero .hero-summary {
    font-size: 17px;
    color: var(--muted);
}

.forecast-page .forecast-figures {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* Two lines held either way: "in heutiger Kaufkraft" adds a line in real terms, and nominal/real must not move the row. */
.forecast-figures .headline .note {
    margin: 6px 0 0;
    min-height: 2lh;
    max-width: 30ch;
    color: var(--muted);
    font-size: 12px;
}

.forecast-page .chart-card {
    margin-top: 28px;
}

/* While the first Forecast is computed the page holds its shape: the labels, the hairlines and the chart's room are
   there, the values not yet. The Basis note takes two lines, so the status line holds both. */
.forecast-hero .hero-summary {
    min-height: 2lh;
}

.forecast-page .pending .explain {
    visibility: hidden;
}

/* What arrives then fades in where it was held; nothing moves. */
.forecast-page.arrived :is(.hero-value, .hero-summary, .hero-figure .explain, .headline .value, .headline .note,
    .headline .explain, .chart-card, .warning, .forecast-assumptions, .forecast-data-basis) {
    transition: opacity 400ms var(--ease-settle);
}

@starting-style {
    .forecast-page.arrived :is(.hero-value, .hero-summary, .hero-figure .explain, .headline .value, .headline .note,
        .headline .explain, .chart-card, .warning, .forecast-assumptions, .forecast-data-basis) {
        opacity: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .forecast-page.arrived :is(.hero-value, .hero-summary, .hero-figure .explain, .headline .value, .headline .note,
        .headline .explain, .chart-card, .warning, .forecast-assumptions, .forecast-data-basis) {
        transition-duration: 200ms;
    }
}

/* While a change is computed, the figures and the fan step back and the header says so, so the wait of a long horizon
   reads as work rather than lag (#362). Only after 200ms: a quick change shows nothing but its figures rolling. */
.forecast-page > header .computing-status {
    opacity: 0;
    transition: opacity 160ms ease-out;
}

.forecast-page :is(.forecast-hero .hero-value, .forecast-hero .hero-summary, .forecast-figures .headline .value,
    .forecast-figures .headline .note, .chart-card) {
    transition: opacity 200ms ease-out;
}

.forecast-page.computing > header .computing-status {
    opacity: 1;
    transition: opacity 200ms ease-out 200ms;
}

.forecast-page.computing :is(.forecast-hero .hero-value, .forecast-hero .hero-summary, .forecast-figures .headline .value,
    .forecast-figures .headline .note, .chart-card) {
    opacity: 0.4;
    transition: opacity 240ms ease-out 200ms;
}

/* The disclaimer: always in view, never mistaken for a figure. */
.forecast-page .disclaimer {
    margin-top: 16px;
    max-width: 80ch;
    color: var(--muted);
    font-size: 13px;
}

.forecast-assumptions,
.forecast-data-basis {
    margin-top: 32px;
}

.forecast-assumptions h2,
.forecast-data-basis h2 {
    margin: 0 0 12px;
    font-size: 15px;
}

.forecast-assumptions dl {
    display: grid;
    gap: 10px;
    margin: 0;
}

.forecast-assumptions dl > div {
    display: grid;
    grid-template-columns: minmax(160px, 220px) 1fr;
    gap: 16px;
}

.forecast-assumptions dt {
    color: var(--muted);
}

.forecast-assumptions dd {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 12px;
    margin: 0;
}

.forecast-assumptions dd .value {
    font-weight: 600;
    color: var(--figure);
    font-variant-numeric: tabular-nums;
}

.forecast-assumptions dd .note,
.forecast-assumptions > .note {
    color: var(--muted);
    font-size: 13px;
}

.forecast-assumptions > .note {
    margin: 0 0 14px;
}

/* An editable assumption: an input that looks like its value until hovered or focused. */
.forecast-assumptions dd input.value {
    width: 13.5em;
    padding: 4px 8px;
}

.forecast-assumptions dd input.value[aria-invalid="true"] {
    border-color: var(--negative);
}

/* Whether the value is calculated or the user's: a small tag, accented when overridden. */
.forecast-assumptions dd .source {
    padding: 1px 8px;
    font-size: 12px;
    color: var(--muted);
    border: 1px solid var(--rule-strong);
    border-radius: 999px;
}

.forecast-assumptions .overridden dd .source {
    color: var(--accent);
    border-color: var(--accent);
}

.forecast-assumptions dd .error {
    flex-basis: 100%;
    font-size: 13px;
}

/* The fan chart's tooltip (ECharts sets its box; the content is class-styled because the CSP forbids inline styles):
   the date, one row per value with a chip matching its series and the amounts lined up, then the reading note. */
.forecast-tooltip {
    color: var(--text);
    font-size: 13px;
    line-height: 1.5;
}

.forecast-tooltip .date {
    margin: 0 0 6px;
    font-weight: 600;
    color: var(--figure);
}

.forecast-tooltip table {
    border-collapse: collapse;
}

.forecast-tooltip td {
    padding: 1px 0;
    color: var(--muted);
}

.forecast-tooltip td:last-child {
    padding-left: 20px;
    text-align: right;
    color: var(--text);
    font-weight: 600;
}

.forecast-tooltip tr.strong td {
    color: var(--figure);
    font-weight: 700;
}

.forecast-tooltip .chip {
    display: inline-block;
    width: 10px;
    height: 10px;
    margin-right: 8px;
    border-radius: 2px;
    vertical-align: -1px;
}

.forecast-tooltip .chip.outer {
    background: var(--chart-band-outer);
}

.forecast-tooltip .chip.inner {
    background: var(--chart-band-inner);
}

.forecast-tooltip .chip.median,
.forecast-tooltip .chip.history,
.forecast-tooltip .chip.invested {
    height: 3px;
    border-radius: 0;
    vertical-align: 3px;
}

.forecast-tooltip .chip.median {
    background: var(--chart-median);
}

.forecast-tooltip .chip.history {
    background: var(--chart-history);
}

.forecast-tooltip .chip.invested {
    height: 0;
    border-top: 2px dashed var(--chart-invested);
}

.forecast-tooltip .note {
    max-width: 260px;
    margin: 8px 0 0;
    padding-top: 6px;
    border-top: 1px solid var(--rule);
    color: var(--muted);
    font-size: 12px;
    white-space: normal;
}

/* The Datenbasis panel: per security a bar over the replay's 25-year window, oldest month left. Months filled from the
   Stand-in index are hatched, months without any price stay empty. */
.forecast-data-basis > .note {
    margin: 0 0 12px;
    color: var(--muted);
    font-size: 13px;
}

.forecast-data-basis ul {
    display: grid;
    gap: 10px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.forecast-data-basis li,
.forecast-data-basis .axis {
    display: grid;
    grid-template-columns: 200px 4.5em 1fr;
    gap: 2px 12px;
    align-items: start;
}

/* A name over two lines takes the height of bar and note together, so the note stays right under the bar. */
.forecast-data-basis li .name,
.forecast-data-basis li .weight {
    grid-row: span 2;
}

.forecast-data-basis li .name {
    color: var(--figure);
}

.forecast-data-basis li .weight {
    text-align: right;
    font-variant-numeric: tabular-nums;
    color: var(--muted);
}

.forecast-data-basis li .note {
    grid-column: 3;
    color: var(--muted);
    font-size: 12px;
}

.forecast-data-basis li .stand-in-choice {
    grid-column: 3;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 8px;
    color: var(--muted);
    font-size: 12px;
}

.forecast-data-basis li .stand-in-choice select {
    padding: 2px 30px 2px 6px;
    background-position: right 8px center;
}

/* Each bar is an SVG: the page's Content-Security-Policy blocks inline styles, so the parts are placed by attributes.
   Each part's data-tip is its hover tooltip (js/bar-tooltip.js). */
.forecast-data-basis .bar {
    display: block;
    width: 100%;
    height: 14px;
    /* Centred on the name's first line. */
    margin-top: 3px;
    background: var(--raised);
    border: 1px solid var(--rule);
    border-radius: 2px;
}

.forecast-data-basis .bar .own {
    fill: var(--muted);
}

.forecast-data-basis .bar .filled {
    fill: url(#forecast-hatch);
}

/* Painted, though invisible, so its tooltip shows on hover. */
.forecast-data-basis .bar .without {
    fill: transparent;
}

.forecast-data-basis .bar rect:hover {
    opacity: 0.8;
}

.forecast-data-basis .bar .without:hover {
    fill: var(--rule);
}

.forecast-data-basis .hatch-ground {
    fill: var(--hatch-ground);
}

.forecast-data-basis .hatch-line {
    fill: var(--muted);
}

/* Holds only the hatch pattern; takes no space. */
.forecast-data-basis svg.defs {
    position: absolute;
    width: 0;
    height: 0;
}

.forecast-data-basis .swatch {
    display: inline-block;
    width: 14px;
    height: 10px;
    margin-right: 6px;
    vertical-align: middle;
    border: 1px solid var(--rule);
    border-radius: 2px;
}

.forecast-data-basis .swatch.own {
    background: var(--muted);
}

.forecast-data-basis .swatch.filled {
    background: repeating-linear-gradient(135deg, var(--muted) 0 2px, var(--hatch-ground) 2px 6px);
}

.forecast-data-basis .swatch.without {
    background: var(--raised);
}

/* Above the bars what their whole width stands for; under them the months and years along it. */
.forecast-data-basis .axis {
    margin: 4px 0 0;
}

.forecast-data-basis .axis.span {
    margin: 0 0 8px;
}

.forecast-data-basis .axis .range {
    grid-column: 3;
    color: var(--muted);
    font-size: 12px;
}

.forecast-data-basis .axis.span .range {
    font-weight: 600;
}

.forecast-data-basis .ticks {
    display: block;
    width: 100%;
    height: 20px;
    overflow: visible;
}

.forecast-data-basis .ticks line {
    stroke: var(--muted);
}

.forecast-data-basis .ticks text {
    fill: var(--muted);
    font-size: 12px;
}

.forecast-data-basis .legend {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 16px;
    margin: 12px 0 0;
    color: var(--muted);
    font-size: 12px;
}

/* The Stand-in index's ISIN: one click selects all of it, the button copies it. */
.forecast-data-basis .isin {
    font-family: var(--font-code);
    color: var(--text);
    user-select: all;
}

.forecast-data-basis .copy {
    margin-left: 4px;
    font-size: inherit;
}

/* The tooltip js/bar-tooltip.js shows next to the pointer, in the style of the fan chart's. */
.bar-tooltip {
    position: fixed;
    z-index: 100;
    white-space: nowrap;
    padding: 6px 10px;
    color: var(--text);
    font-size: 12px;
    line-height: 1.4;
    background: var(--sheet);
    border: 1px solid var(--rule);
    border-radius: 6px;
    box-shadow: 0 6px 20px var(--shadow-color);
    pointer-events: none;
}

.bar-tooltip[hidden] {
    display: none;
}

/* The off-screen field js/copy.js copies from when the clipboard API is refused. */
.copy-buffer {
    position: fixed;
    top: -100px;
    left: 0;
    opacity: 0;
}

/* ---------- Narrow windows ---------- */

@media (max-width: 1000px) {
    .documents-page.viewing .document-workspace {
        display: block;
    }

    .document-viewer {
        position: static;
        max-height: none;
    }

    .document-viewer .document-pages {
        max-height: calc(100vh - 32px);
    }
}

/* Four figures in a row get too tight: two by two, the rules following the grid. */
@media (max-width: 1180px) {
    .headlines {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .headline:nth-child(odd) {
        padding-left: 0;
        border-left: none;
    }

    .headline:nth-child(n + 3) {
        border-top: 1px solid var(--rule);
    }

    /* The Prognose keeps its three figures in one row: the third stays beside the second, not under it. */
    .forecast-figures .headline:nth-child(3) {
        padding-left: 20px;
        border-left: 1px solid var(--rule);
        border-top: none;
    }

    /* The ledger follows the row onto two columns. */
    .ledger-line dl {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        row-gap: 6px;
    }

    .ledger-item:nth-child(odd) {
        padding-left: 0;
    }
}

/* One pair per line across the width: label left, amount right. */
@media (max-width: 760px) {
    .ledger {
        gap: 12px;
    }

    .ledger-line dl {
        grid-template-columns: minmax(0, 1fr);
    }

    .ledger-item,
    .ledger-item + .ledger-item {
        padding: 0;
    }
}

/* The rail becomes a bar across the top. */
@media (max-width: 900px) {
    .app-shell {
        grid-template-columns: minmax(0, 1fr);
    }

    .rail {
        z-index: 15;
        flex-direction: row;
        align-items: center;
        gap: 4px;
        height: auto;
        padding: 8px 12px;
        overflow-x: auto;
        background: var(--ground);
        border-right: none;
        border-bottom: 1px solid var(--rule);
    }

    .brand {
        padding: 0 10px 0 4px;
    }

    .brand span {
        display: none;
    }

    .rail-item {
        width: auto;
        flex: none;
        white-space: nowrap;
    }

    .rail-item .hint {
        display: none;
    }

    .rail-item.rail-group-start {
        margin: 0 0 0 12px;
    }

    .rail-item.rail-foot-start {
        margin: 0 0 0 auto;
    }

    .rail-foot,
    .rail-item + .rail-foot {
        flex-direction: row;
        align-items: center;
        margin: 0 0 0 auto;
    }

    .rail-item + .rail-foot {
        margin-left: 4px;
    }

    .rail-foot .vault-state {
        display: none;
    }

    .connection-banner,
    .backup-offer,
    .fallback-banner,
    .backup-banner,
    .narrow-screen-banner,
    .sample-banner {
        margin: 16px 16px 0;
    }

    .refresh-status.strip {
        padding: 10px 16px;
    }

    .dashboard,
    .documents-page,
    .forecast-page,
    .market-page,
    .settings-page,
    .tax-page {
        padding: 20px 16px 48px;
    }

    .range-picker {
        margin-left: 0;
    }

    .custom-range {
        justify-content: flex-start;
    }

    .hero {
        margin-top: 28px;
    }

    .hero-summary {
        font-size: 18px;
    }

    .chart {
        height: 340px;
    }
}

@media (max-width: 760px) {
    .forecast-assumptions dl > div {
        grid-template-columns: 1fr;
        gap: 2px;
    }

    .forecast-data-basis li,
    .forecast-data-basis .axis {
        grid-template-columns: 1fr auto;
    }

    .forecast-data-basis li .name,
    .forecast-data-basis li .weight {
        grid-row: auto;
    }

    /* The Ersatzindex choice too: left in its wide-layout column it opens a third track and squeezes the bar. */
    .forecast-data-basis li .bar,
    .forecast-data-basis li .note,
    .forecast-data-basis li .stand-in-choice,
    .forecast-data-basis .axis .range {
        grid-column: 1 / -1;
    }

    .import-summary {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* ---------- Steuern page ---------- */

.tax-page > header .tax-years {
    margin-top: 16px;
}

.tax-page > .warning,
.tax-page > .info {
    margin: 20px 0 0;
}

/* Only one year to show: its name as a label, set like the switch's. */
.tax-years.single {
    margin-bottom: 0;
    color: var(--muted);
}

.tax-years.single span {
    margin-right: 8px;
}

/* The year's KESt as the hero, as on the Übersicht; the switch above is closer than the Übersicht's top line. */
.tax-page .tax-hero {
    margin-top: 28px;
}

/* The four figures behind it: the Übersicht's ruled row. */
.tax-basis-note {
    max-width: 90ch;
    margin: 12px 0 28px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--muted);
}

/* Over the year's entries, like "Verluste realisieren" over its section. */
.tax-entries-heading {
    margin: 0 0 6px;
    font-size: 15px;
}

.tax-table {
    width: 100%;
    border-collapse: collapse;
}

.tax-table th,
.tax-table td {
    padding: 8px 12px;
    border-bottom: 1px solid var(--rule);
    white-space: nowrap;
}

.tax-table th {
    font-size: 12px;
    font-weight: 400;
    color: var(--muted);
    border-bottom-color: var(--rule-strong);
}

.tax-table th:first-child,
.tax-table td:first-child {
    padding-left: 0;
}

.tax-table .text {
    text-align: left;
}

.tax-table .number {
    text-align: right;
}

/* The year's table: the Wertpapier may wrap. (The Harvest's second column is Stück.) */
.tax-table:not(.harvest-table) td:nth-child(2) {
    min-width: 16ch;
    white-space: normal;
}

/* The end amount after KESt and Quellensteuer: the column to read along, in the figures' ink. */
.year-table > tbody td.after-tax {
    color: var(--figure);
}

/* The year's sums under its entries, ruled off like the Erklären sheet's: the KESt is the large figure above. */
.year-table > tfoot td {
    font-weight: 600;
    color: var(--figure);
    border-top: 1px solid var(--figure);
    border-bottom: none;
}

/* The Übersicht's taxes per year: what a range left out of a year, under it; the link to Steuern at the row's end. */
.tax-by-year .whole-year {
    display: block;
    font-size: 12px;
    color: var(--muted);
}

.tax-by-year .to-taxes {
    text-align: right;
    white-space: nowrap;
    font-size: 13px;
}

/* In a row's preview, above the document: how the pot came to the row's KESt. */
.event-preview .tax-computation {
    margin: 0 0 12px;
    font-size: 13px;
    white-space: normal;
    color: var(--text);
}

.event-preview .tax-computation p {
    margin: 0 0 6px;
}

/* A loss's credits: a compact table, one row per gain or income it got KESt back from. */
.event-preview .credit-table {
    width: auto;
    min-width: min(100%, 520px);
    border-collapse: collapse;
    font-size: 12px;
    font-variant-numeric: tabular-nums;
}

.event-preview .credit-table th,
.event-preview .credit-table td {
    padding: 3px 12px 3px 0;
    text-align: left;
    border-bottom: 1px solid var(--rule);
}

.event-preview .credit-table th {
    font-weight: 500;
    color: var(--muted);
}

.event-preview .credit-table .number {
    text-align: right;
    white-space: nowrap;
}

.event-preview .credit-table tfoot td {
    border-bottom: none;
    font-weight: 600;
}

.event-preview .tax-computation .open-rest {
    margin: 6px 0 0;
    color: var(--muted);
}

/* The ledger needs the Erklären sheet's width, wider than a settings sheet's. */
.side-panel:has(.tax-ledger) {
    width: min(680px, 100%);
}

.tax-ledger td {
    vertical-align: top;
}

/* Under a gain's or income's Vorgang, in its hanging indent: how its KESt came about. */
.tax-ledger .ledger-note {
    display: block;
    margin-top: 2px;
    text-indent: 0;
    font-size: 12px;
    line-height: 1.45;
    color: var(--muted);
}

/* A loss and its credits read as one block: the rule comes after the last credit. */
.tax-ledger .ledger-entry.has-credits td,
.tax-ledger .ledger-credit td {
    border-bottom: none;
}

.tax-ledger .ledger-entry.has-credits td {
    padding-bottom: 4px;
}

/* The credits: smaller and muted in every column, the Vorgang in line with the loss's text after its mark. */
.tax-ledger .ledger-credit td {
    padding-top: 1px;
    padding-bottom: 1px;
    font-size: 12px;
    color: var(--muted);
}

.tax-ledger .ledger-credit td:nth-child(2) {
    padding-left: 27px;
}

.tax-ledger .ledger-credit .rate {
    margin-left: 4px;
    white-space: nowrap;
}

.tax-ledger .ledger-credit.last td {
    padding-bottom: 9px;
    border-bottom: 1px solid var(--rule);
}

/* Paid, credited, and the year's KESt: one rule over the three, not one over each. */
.tax-ledger tfoot tr + tr td {
    border-top: none;
    padding-top: 2px;
}

.tax-ledger tfoot tr:not(:last-child) td {
    font-weight: 400;
    color: var(--text);
}

/* "Verluste realisieren": a calculator between the year's figures and its entries, set off by a rule. */
.harvest {
    margin: 0 0 36px;
    padding-top: 22px;
    border-top: 1px solid var(--rule-strong);
}

.harvest h2 {
    margin: 0;
    font-size: 15px;
}

.harvest .disclaimer {
    margin: 4px 0 0;
    font-size: 12px;
    color: var(--muted);
}

/* What this section adds, below the figure size of its answer: the hero above stays the page's one large figure. */
.harvest-recoverable {
    margin: 12px 0 0;
    font-family: var(--font-figure);
    font-size: 17px;
    font-weight: 600;
    color: var(--figure);
    font-variant-numeric: tabular-nums;
}

.harvest-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 16px;
    margin: 18px 0 6px;
}

.harvest-actions .proposal-label {
    margin: 0;
    font-size: 13px;
    color: var(--muted);
}

/* The rows keep their columns on a narrow window and scroll sideways instead of the page. */
.harvest-scroll {
    overflow-x: auto;
}

.harvest-table td {
    vertical-align: top;
}

.harvest-table .close-date {
    display: block;
    font-size: 12px;
    color: var(--muted);
}

.harvest-table .close-date.stale {
    color: var(--warning-ink);
}

.harvest-table .sell-input {
    display: flex;
    gap: 6px;
}

.harvest-table .sell-input input {
    width: 9em;
    padding: 4px 8px;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.harvest-table .sell-input input[readonly] {
    color: var(--muted);
    background: transparent;
}

.harvest-table .sell-input input[aria-invalid="true"] {
    border-color: var(--negative);
}

.harvest-table .sell-input button.all {
    padding: 4px 10px;
}

.harvest-table .sell-input button.all.active {
    color: var(--accent-ink);
    background: var(--accent);
    border-color: var(--accent);
}

.harvest-table .error {
    max-width: 22em;
    margin: 4px 0 0;
    font-size: 12px;
    white-space: normal;
    color: var(--negative);
}

.harvest-table .hint {
    display: block;
    margin-top: 2px;
    font-size: 12px;
    color: var(--muted);
}

/* The answer and the other totals on one line, bottom-aligned like the hero and its sentence. */
.harvest-outcome {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 14px 48px;
    margin-top: 20px;
}

.harvest-result .label {
    margin: 0;
    font-size: 13px;
    color: var(--text);
}

/* At the figure row's size: the one figure of the section to read, the cents set back as everywhere. */
.harvest-result .value {
    margin: 4px 0 0;
    color: var(--figure);
    font-family: var(--font-figure);
    font-size: 26px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.02em;
}

.harvest-result .value .cents {
    font-size: 0.65em;
    font-weight: 300;
    color: var(--muted);
    letter-spacing: -0.01em;
}

/* "von 26,95 € rückholbar": the most it can be, in text beside the figure. */
.harvest-result .value .of {
    margin-left: 6px;
    font-family: var(--font-ui);
    font-size: 14px;
    font-weight: 400;
    letter-spacing: 0;
    color: var(--muted);
}

.harvest-totals {
    display: flex;
    flex-wrap: wrap;
    gap: 12px 40px;
    margin: 0 0 3px;
}

.harvest-totals dt {
    font-size: 12px;
    color: var(--muted);
}

/* What else the entries do, a step below the answer beside them. */
.harvest-totals dd {
    margin: 2px 0 0;
    font-size: 15px;
    font-weight: 600;
    color: var(--figure);
    font-variant-numeric: tabular-nums;
}

.harvest-totals dd.positive {
    color: var(--positive);
}

.harvest-totals dd.negative {
    color: var(--negative);
}

/* KESt fällig: the total the Gain harvest exists to avoid, in the amber of the warning under it. */
.harvest-totals dd.caution {
    color: var(--warning-ink);
}

/* Over the Open loss: right under the totals it explains, as wide as the notes below. */
.harvest-warning {
    max-width: 90ch;
    margin: 14px 0 0;
    font-size: 13px;
    line-height: 1.5;
}

.harvest-note {
    margin: 10px 0 0;
    font-size: 13px;
    color: var(--muted);
}

.harvest-notes {
    max-width: 90ch;
    margin-top: 22px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--muted);
}

.harvest-notes .deadline {
    margin: 0;
    color: var(--text);
    font-weight: 600;
}

.harvest-notes .crossing {
    margin-top: 14px;
}

.harvest-notes .crossing summary {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-weight: 600;
    color: var(--text);
    cursor: pointer;
    list-style: none;
}

.harvest-notes .crossing summary::-webkit-details-marker {
    display: none;
}

/* The chevron of the selects, turned to point at what folds open. */
.harvest-notes .crossing summary::before {
    content: "";
    width: 12px;
    height: 8px;
    background: var(--chevron) no-repeat center / 12px 8px;
    transform: rotate(-90deg);
    transition: transform 120ms ease-out;
}

.harvest-notes .crossing[open] summary::before {
    transform: none;
}

.harvest-notes .crossing summary:hover {
    color: var(--figure);
}

.harvest-notes .crossing summary:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 3px;
}

.harvest-notes ul {
    margin: 6px 0 0;
    padding-left: 18px;
}

.harvest-notes p {
    margin: 6px 0 0;
}

/* In the section's place when it is hidden for the current year: why, in one line. */
.harvest-hidden {
    max-width: 90ch;
    margin: 0 0 28px;
    padding-top: 14px;
    font-size: 13px;
    color: var(--muted);
    border-top: 1px solid var(--rule);
}

.tax-table .booked-kest {
    display: block;
    font-size: 12px;
    color: var(--warning-ink);
}

@media (max-width: 720px) {
    /*
       The year's entries, like the Harvest's holdings: each a block instead of a row too wide for the window, so its
       KESt stays in view. Datum and Art, the Wertpapier, then the amount, Saldo danach and KESt under their labels. The
       header keeps only the Datum sort; the others stay for screen readers, and the roles keep it a table.
    */
    .year-table,
    .year-table > tbody {
        display: block;
    }

    .year-table > thead > tr {
        display: flex;
        border-bottom: 1px solid var(--rule-strong);
    }

    .year-table > thead > tr > th {
        border-bottom: none;
    }

    .year-table > thead > tr > th:not(:first-child) {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
    }

    .year-table > tbody > tr:not(.preview-row) {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        grid-template-areas:
            "date kind kind"
            "security security security"
            "amount net kest"
            "after . qst";
        gap: 4px 16px;
        padding: 12px 0;
        border-bottom: 1px solid var(--rule);
        transition: background-color 120ms ease-out;
    }

    .year-table > tbody > tr:not(.preview-row) > td {
        display: block;
        padding: 0;
        border: none;
        text-align: left;
        white-space: normal;
        background: none;
    }

    /* The row's states on the whole block, not on cells that no longer touch. */
    .year-table > tbody > tr.differs {
        background: var(--warning-bg);
    }

    .year-table > tbody > tr.previewable:hover {
        background: var(--raised);
    }

    .year-table > tbody > tr.previewing,
    .year-table > tbody > tr.previewing:hover {
        background: var(--selection);
        border-bottom-color: transparent;
    }

    .year-table > tbody > tr > td.date { grid-area: date; }
    .year-table > tbody > tr > td.kind { grid-area: kind; }
    .year-table > tbody > tr > td.security { grid-area: security; font-weight: 600; color: var(--figure); }
    .year-table > tbody > tr > td.amount { grid-area: amount; }
    .year-table > tbody > tr > td.net { grid-area: net; }
    .year-table > tbody > tr > td.kest { grid-area: kest; text-align: right; }
    /* Under the KESt, only where there was Quellensteuer; under the amount, what is left of it after both. */
    .year-table > tbody > tr > td.qst { grid-area: qst; text-align: right; }
    .year-table > tbody > tr > td.qst.none { display: none; }
    .year-table > tbody > tr > td.after-tax { grid-area: after; }

    .year-table > tfoot,
    .year-table > tfoot > tr {
        display: block;
    }

    .year-table > tfoot > tr {
        display: grid;
        grid-template-columns: minmax(0, 1fr);
        grid-auto-flow: column;
        gap: 4px 16px;
        padding: 12px 0;
        border-top: 1px solid var(--figure);
    }

    .year-table > tfoot > tr > td {
        display: block;
        padding: 0;
        border: none;
    }

    .year-table > tfoot > tr > td[data-label]::before {
        content: attr(data-label);
        display: block;
        margin-bottom: 2px;
        font-size: 12px;
        font-weight: 400;
        color: var(--muted);
    }

    .year-table > tbody > tr > td[data-label]::before {
        content: attr(data-label);
        display: block;
        margin-bottom: 2px;
        font-size: 12px;
        color: var(--muted);
    }

    .year-table > tbody > tr.preview-row,
    .year-table > tbody > tr.preview-row > td {
        display: block;
    }

    /*
       The Harvest: each holding a block rather than a row too wide for the window, so what is sold stays in view.
       Wertpapier, then "zu verkaufen" and what it realizes, then the holding's figures, each under its data-label.
       The table's ARIA roles keep it a table for screen readers.
    */
    .harvest-table thead {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
    }

    .harvest-table tbody {
        display: block;
    }

    .harvest-table tbody tr {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        grid-template-areas:
            "security security security"
            "sell sell realized"
            "pieces entry-price close"
            "unrealized unrealized unrealized";
        gap: 10px 16px;
        padding: 14px 0;
        border-bottom: 1px solid var(--rule);
    }

    .harvest-table tbody tr:first-child {
        border-top: 1px solid var(--rule-strong);
    }

    .harvest-table tbody td {
        display: block;
        padding: 0;
        border: none;
        text-align: left;
        white-space: normal;
    }

    .harvest-table td::before {
        content: attr(data-label);
        display: block;
        margin-bottom: 2px;
        font-size: 12px;
        color: var(--muted);
    }

    /* Each value under its label; only what is realized lines up right, beside the entry it follows. */
    .harvest-table tbody td.number {
        text-align: left;
    }

    .harvest-table tbody td.realized {
        text-align: right;
    }

    .harvest-table td.security { grid-area: security; }
    .harvest-table td.sell { grid-area: sell; }
    .harvest-table td.realized { grid-area: realized; }
    .harvest-table td.pieces { grid-area: pieces; }
    .harvest-table td.entry-price { grid-area: entry-price; }
    .harvest-table td.close { grid-area: close; }
    .harvest-table td.unrealized { grid-area: unrealized; }

    .harvest-table .sell-input input {
        width: 100%;
        min-width: 0;
    }
}