/* Mietblick — landlord-suite UI on a warm cream ground. Six colours, each
 * with one job: deep navy for trust (headings, the sidebar), fresh green for
 * progress, ownership and positive actions, warm cream as the canvas, soft
 * blue for the calculators and the explanations, charcoal for body text, and
 * warm orange held back for the occasional highlight or the one CTA that
 * matters on a screen. Figtree, white surfaces, one 8px corner.
 *
 * The one rule the palette imposes: a TINT is a background, never a
 * letterform. Anything that reads as text takes `--accent`, `--ok`, `--bad` …
 * which are the DARK members of each family; anything that reads as a
 * surface takes the tint and puts that family's ink on top. The two
 * saturated swatches are FILLS, and they carry different inks: green takes
 * white, orange takes charcoal — white on #E38B4A is 2.6:1. */

/* ---------- fonts: served from this host, never from Google ----------
 * Loading them from fonts.googleapis.com sent every visitor's IP address to
 * Google on every page load — a transfer the Datenschutzerklärung had to
 * declare, on a legitimate-interest basis the LG München I rejected
 * (3 O 17493/20, 2022) precisely because self-hosting was possible. So the
 * files live in `public/fonts/` (SIL OFL 1.1, licences beside them) and no
 * page may link a font CDN again: doing so is a change to §3 of that page.
 *
 * Variable fonts, one file per subset, upright only — nothing here is set in
 * italic. Latin covers German; Latin Extended catches names like „Yılmaz“.
 * `unicode-range` means a page with no such letter never downloads it. */
@font-face{font-family:"Figtree";font-style:normal;font-weight:300 900;font-display:swap;
  src:url(/fonts/figtree-latin-wght-normal.woff2) format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:"Figtree";font-style:normal;font-weight:300 900;font-display:swap;
  src:url(/fonts/figtree-latin-ext-wght-normal.woff2) format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
/* §5.41.5 — the two hands a typed signature may be written in (SIL OFL, the
 * licences beside them). Only a page that draws a typed signature uses them. */
@font-face{font-family:"Herr Von Muellerhoff";font-style:normal;font-weight:400;font-display:swap;
  src:url(/fonts/herr-von-muellerhoff-latin-400-normal.woff2) format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:"Herr Von Muellerhoff";font-style:normal;font-weight:400;font-display:swap;
  src:url(/fonts/herr-von-muellerhoff-latin-ext-400-normal.woff2) format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:"Dancing Script";font-style:normal;font-weight:400;font-display:swap;
  src:url(/fonts/dancing-script-latin-400-normal.woff2) format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:"Dancing Script";font-style:normal;font-weight:400;font-display:swap;
  src:url(/fonts/dancing-script-latin-ext-400-normal.woff2) format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:"Geist Mono";font-style:normal;font-weight:100 900;font-display:swap;
  src:url(/fonts/geist-mono-latin-wght-normal.woff2) format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:"Geist Mono";font-style:normal;font-weight:100 900;font-display:swap;
  src:url(/fonts/geist-mono-latin-ext-wght-normal.woff2) format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}

/* ---------- tokens ---------- */
:root{
  /* ---- the palette, named as it is named ----------------------------------
     So a rule can say which swatch it means rather than repeating a hex
     nobody can place. Everything below derives from these six. */
  --brand-navy:#183153;     /* trust, stability, headings */
  --brand-green:#4f8a68;    /* progress, ownership, positive actions */
  --brand-cream:#f7f4ed;    /* the friendly background */
  --brand-blue:#ddeaf3;     /* educational sections and calculators */
  --brand-charcoal:#263238; /* body text */
  --brand-orange:#e38b4a;   /* occasional highlights, important CTAs */

  --bg:var(--brand-cream);
  --surface:#ffffff;
  --surface-2:#f2eee5;
  --border:#e6e0d3;
  --border-strong:#d3cbbb;
  --heading:var(--brand-navy);
  --ink:var(--brand-charcoal);
  --ink-2:#37474f;
  --muted:#5f6b71;
  /* Small grey text must still read: 3.6:1 on white, not the 2.6:1 it was. */
  --faint:#7a868c;

  /* THE ACCENT IS TWO THINGS. `--accent` is the INK — the green taken dark
     enough to read as 13px text on white (#4f8a68 itself is 4.1:1, under
     AA) — used wherever the accent is a letterform: links, ghost buttons,
     the unlocks line. `--accent-fill` is the SURFACE, the brand green, and
     anything sitting on it takes `--on-fill`. `--accent-mid` is for thin
     decorative fills — progress rails, step lines — where the brand green
     is already strong enough to show. */
  --accent:#3d6e52;
  --accent-strong:#2f5840;
  --accent-soft:#e4efe8;
  --accent-fill:var(--brand-green);
  --accent-fill-hover:#437a5b;
  --accent-mid:var(--brand-green);
  --on-fill:#ffffff;

  /* Orange is NOT a button colour. It is the root of the attention family
     (`--warn` / `--warn-tint` below): a thing that needs looking at. A button
     in orange read as a warning or as an alarm, and it belonged to no other
     control on the screen. Actions are green (§5.30, "three levels"). */

  /* Running text stops at a readable line: 55–70 characters. */
  --measure:65ch;
  /* Rhythm: related things sit closer than sections do. Three steps and
     nothing between them (§5.30): a PAIR is a heading or a figure and its own
     line underneath (a title and its subtitle, a label and its figure); RELATED
     is the things inside one group (a section title and what it heads, a line
     that belongs to the block above it); SECTION is between panels and before
     a form's actions. A gap of 0, 2, 3, 12, 14, 18 or 22 between two strings
     is a value somebody picked locally — pick the step instead. */
  --gap-pair:4px;
  --gap-related:8px;
  --gap-section:16px;
  /* ONE left edge inside a surface (§5.30). A panel's heading, its body, the
     first column of its table, the figures of a headline and the tabs of a
     property all start this far from the panel's border — it was 12, 18, 20,
     22 and 24px depending on which component drew the text, so a heading and
     the table under it never shared an edge. A surface's inset is this token;
     a number there is the same local pick the gaps above warn about. */
  --inset:20px;

  /* Calculators and explanations: the soft blue as the ground, navy as the
     ink, and a border one step darker so the block has an edge on cream. */
  --edu:var(--brand-blue);
  --edu-border:#c5d8e7;
  --edu-ink:var(--brand-navy);

  /* Chart series. Distinguishable by HUE rather than by lightness alone, so
     the stack survives a greyscale print and a red-green colour deficiency:
     the brand green, a mid blue, the brand orange, a muted violet and a rose.
     All MID tones — a pale swatch vanishes as a 6px bar on white. */
  --c0:#4f8a68;  --c1:#5b84b1;  --c2:#e38b4a;  --c3:#8a7baa;  --c4:#b35c6b;
  /* …and a pale partner where a LARGE area carries the colour: a legend
     key, a stacked band. The rose is the second rent series. */
  --c4-soft:#f4dde1;

  --live:#2f5d8a;  --live-tint:var(--brand-blue);
  --ok:#3d6e52;    --ok-tint:#e4efe8;
  --warn:#9a531f;  --warn-tint:#fbe8d8;
  --bad:#a63d40;   --bad-tint:#f6e0df;
  --info:var(--brand-navy); --info-tint:var(--brand-blue);
  /* two neutral pills */
  --violet-tint:#e8e3f0;
  --grey-tint:#e3e6e8;

  --font-sans:"Figtree",system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --font-mono:"Geist Mono","JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,monospace;

  /* ---- TYPE: the ONE place a size or a weight is decided ----------------
     Every `font-size` and `font-weight` in this file is one of these
     tokens; a raw number is a bug (tests/v3-typescale.test.js says so).
     Readability beats density: body 15px at 1.5, and nothing on screen
     below 12px. To change how big something is, change its STEP here —
     or move the rule to another step — never write a number in a rule.
     The phone steps are the two :root blocks right below this one. */
  --fs-glyph:8px;         /* ▲▼ markers inside a delta chip — not text */
  --fs-label:12px;        /* table headers, UPPERCASE metadata, badges, pills */
  --fs-axis:12px;         /* chart axis labels (a phone drops it to 11px) */
  --fs-caption:13px;      /* chips, footnotes, secondary lines under a line */
  --fs-small:14px;        /* table data, helper text, the line under a figure */
  --fs-body:15px;         /* body, navigation, buttons, form fields */
  --fs-lead:16px;         /* section headings, lead paragraphs, tabs */
  --fs-card-title:18px;   /* card and dialog titles, small figures */
  --fs-title:24px;        /* property title, the wizard's question, mid figures */
  --fs-page:26px;         /* page titles, KPI figures */
  --fs-figure:36px;       /* the big financial figure */
  --fs-display:40px;      /* the landing page's name */
  --lh-body:1.5;
  --fw-regular:400; --fw-medium:500; --fw-semibold:600; --fw-bold:700;

  /* CORNERS BY ROLE, low and few: soft-edged, not bubbly. Hierarchy comes
   * from spacing, type, borders and background — never from a bigger radius,
   * which is how the old scale (8/12/16/28) made the largest surface the
   * softest. The page's big blocks are architectural and square; only what
   * you touch is rounded, and only slightly.
   *   --r-panel  0    page panels, the sidebar, the legal article
   *   --r-card   8px  cards, dialogs, popovers, toasts — things that float
   *                   or stand alone as one choice
   *   --r-btn    6px  buttons and every clickable control that is not a pill
   *   --r-input  4px  fields, selects, textareas
   *   --r-md     4px  boxes INSIDE a surface: notices, transcripts, comments
   *   --r-sm     4px  badges, icon tiles, items inside a menu or a control
   *   --r-mark   3px  chart marks: bar tops, legend swatches, thin rails
   * Fully round is reserved for pills, chips, filters, avatars, dots and
   * progress rails (999px / 50%); `0` is allowed for a field drawn as a line.
   * Add a corner by picking the role, never a fresh number. */
  --r-panel:0; --r-card:8px; --r-btn:6px; --r-input:4px; --r-md:4px; --r-sm:4px;
  --r-side:var(--r-panel);
  /* Chart marks — bars (top corners only), legend swatches, thin rails. */
  --r-mark:3px;
  /* Shadows are navy, not black: a grey shadow reads dirty on cream. */
  --shadow-1:0 4px 8px rgba(24,49,83,.08);
  --shadow-2:0 12px 32px rgba(24,49,83,.16),0 2px 6px rgba(24,49,83,.06);

  /* legacy aliases */
  --panel:var(--surface); --text:var(--ink);
  --green:var(--ok);   --green-soft:var(--ok-tint);
  --red:var(--bad);    --red-soft:var(--bad-tint);
  --amber:var(--warn); --amber-soft:var(--warn-tint);
  --purple:var(--info);--purple-soft:var(--info-tint);
  --radius:var(--r-card); --shadow:var(--shadow-1);
  --r-lg:var(--r-panel);
}
/* Phone steps: the only place type changes with the screen. */
@media(max-width:860px){:root{--fs-display:32px}}
@media(max-width:620px){:root{--fs-page:24px;--fs-title:21px;--fs-figure:32px;--fs-axis:11px;--inset:16px}}
*{box-sizing:border-box;margin:0;padding:0}
/* `hidden` means hidden. The browser's own rule for it weighs nothing, so any
   component that sets `display` (`.frow` is flex) showed an element the code
   had hidden — the „Abrechnungszeitraum“ row stood in the entry dialog for
   every category, including the ones it does not belong to. */
[hidden]{display:none !important}
/* A consumer product about rent, money and deadlines: readability beats
   density. 15px body at 1.5, and nothing on screen below 12px (the one
   exception is a chart's month labels on a phone, which truncate instead). */
body{font-family:var(--font-sans);background:var(--bg);color:var(--ink);font-size:var(--fs-body);line-height:1.5;-webkit-font-smoothing:antialiased}
/* Headings are navy; body text stays charcoal. `:where()` weighs nothing, so
   any component that colours its own heading still wins. */
:where(h1,h2,h3){color:var(--heading)}
/* The browser's <b> is `bolder` — relative, so a <b> inside bold text lands on
   900, a weight the scale does not have. Bold is one step, wherever it sits. */
:where(b,strong){font-weight:var(--fw-bold)}
#app{display:flex;min-height:100vh}

/* ---------- topbar: centered logo + user pill + bell ---------- */
.topbar{position:fixed;top:0;left:0;right:0;height:72px;display:flex;align-items:center;z-index:15;padding:0 20px;pointer-events:none}
.topbar .brand{position:absolute;left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:9px;font-size:var(--fs-title);font-weight:var(--fw-bold);letter-spacing:-0.02em;color:var(--heading);pointer-events:auto}
.topbar .brand .mark{font-size:var(--fs-title)}
.topbar-right{margin-left:auto;display:flex;align-items:center;gap:10px;pointer-events:auto}
.userpill{display:flex;align-items:center;gap:10px;background:var(--surface);border-radius:999px;padding:6px 14px 6px 6px;box-shadow:var(--shadow-1)}
.userpill .avatar{width:34px;height:34px;border-radius:50%;background:var(--accent-soft);color:var(--accent-strong);display:flex;align-items:center;justify-content:center;font-weight:var(--fw-bold);font-size:var(--fs-small)}
.userpill .uname{font-size:var(--fs-small);font-weight:var(--fw-bold);line-height:1.2}
.userpill .badges{display:flex;gap:4px;margin-top:1px}
.userpill .badges span{font-size:var(--fs-label);font-weight:var(--fw-semibold);color:var(--muted);background:var(--surface-2);border-radius:999px;padding:1px 7px;text-transform:uppercase;letter-spacing:.03em}
.bellbtn{width:44px;height:44px;border-radius:50%;background:var(--surface);border:0;box-shadow:var(--shadow-1);cursor:pointer;font-size:var(--fs-card-title);position:relative}
.bellbtn .dot{position:absolute;top:9px;right:10px;width:8px;height:8px;border-radius:50%;background:var(--bad)}

/* ---------- sidebar: floating white card ---------- */
/* A flush rail, not a floating card: the frame of the screen is architecture. */
.sidebar{width:224px;background:var(--nav-bg);color:var(--nav-dim);padding:14px 12px;position:fixed;top:0;bottom:0;left:0;display:flex;flex-direction:column;z-index:40;border-radius:var(--r-side);overflow-y:auto;transition:transform .22s ease}
.logo{display:none}
.whoami{display:flex;align-items:center;gap:10px;font-size:var(--fs-label);padding:6px 8px 14px;color:var(--muted);line-height:1.35}
.whoami::before{content:"⌂";flex-shrink:0;width:34px;height:34px;border-radius:var(--r-sm);background:var(--accent-soft);color:var(--accent-strong);display:flex;align-items:center;justify-content:center;font-size:var(--fs-card-title)}
.whoami b{color:var(--ink);font-size:var(--fs-small);display:block}
.nav-section{font-size:var(--fs-label);font-weight:var(--fw-bold);letter-spacing:.09em;text-transform:uppercase;color:var(--faint);padding:14px 10px 6px}
.nav-item{width:100%;border:0;background:none;font:inherit;text-align:left;
  display:flex;align-items:center;gap:11px;padding:9px 11px;border-radius:var(--r-btn);cursor:pointer;margin-bottom:2px;font-size:var(--fs-body);color:var(--ink-2);font-weight:var(--fw-medium)}
.nav-item span:first-child{width:18px;text-align:center;color:var(--muted)}
.nav-item:hover{background:var(--surface-2)}
.nav-item.active{background:var(--accent-soft);color:var(--ink);font-weight:var(--fw-semibold)}
.nav-item.active span:first-child{color:var(--accent-strong)}
.nav-item .badge{margin-left:auto;background:var(--bad-tint);color:var(--bad);border-radius:var(--r-sm);font-size:var(--fs-label);padding:1px 7px;font-weight:var(--fw-semibold);font-family:var(--font-mono)}
.nav-item.active .badge{background:#fff;color:var(--accent-strong)}
.nav-item .badge.blue{background:var(--info-tint);color:var(--info)}
.sidebar-foot{margin-top:auto;padding:10px;font-size:var(--fs-label);line-height:1.5;color:var(--faint)}
.lang-toggle{display:flex;margin:12px 10px 6px;background:var(--surface-2);border-radius:var(--r-btn);overflow:hidden;padding:3px;gap:3px}
.lang-toggle button{flex:1;background:transparent;border:0;color:var(--muted);padding:5px 0;cursor:pointer;font-size:var(--fs-caption);font-weight:var(--fw-semibold);font-family:inherit;border-radius:var(--r-sm)}
.lang-toggle button.on{background:var(--surface);color:var(--ink);box-shadow:var(--shadow-1)}
.logout{width:calc(100% - 20px);margin:8px 10px 0;background:var(--surface);border:1px solid var(--border-strong);color:var(--muted);border-radius:var(--r-btn);padding:7px;cursor:pointer;font-size:var(--fs-caption);font-family:inherit;font-weight:var(--fw-medium)}
.logout:hover{border-color:var(--accent);color:var(--accent-strong)}

/* ---------- main ---------- */
/* `min-width:0` is the whole fix for the sideways scrolling: without it a flex
   item refuses to shrink below its content, so a wide table widened the PAGE
   instead of scrolling inside its own wrapper.
   The top equals the side: the content starts as far below the window's edge
   as it stands from the sidebar. It was 84px, room for a fixed top bar that
   is no longer drawn. (The phone keeps its own, under its real header bar.) */
.main{margin-left:224px;flex:1;min-width:0;padding:34px 34px 26px;max-width:1500px}
.page-head{display:flex;align-items:center;gap:0 12px;margin-bottom:20px;flex-wrap:wrap}
.page-head h1{font-size:var(--fs-page);font-weight:var(--fw-bold);letter-spacing:-0.02em;line-height:1.2}
/* The line under a title is its OWN row, starting under the title's first
   letter, never beside it. A `max-width` on the box let it shrink to the
   measure and fit next to the title on a wide screen, where it ran past the
   right edge of the form beneath. So the box always takes the full row and
   the measure is kept by its right padding instead. */
/* The gap is the line's own margin, not the head's row gap: in a head WITH
   actions the title and its line share a wrapper, the row gap never reached
   them, and the subtitle sat on the title's descenders. */
.page-head .sub{color:var(--muted);font-size:var(--fs-body);flex:0 0 100%;width:100%;margin-top:var(--gap-pair);
  padding-right:max(0px,calc(100% - var(--measure)))}
.spacer{flex:1}
/* The figures are COLUMNS in one strip, not a row of cards: one hairline box,
   and a rule between the figures. Each figure draws its rule as a -1px shadow
   to its left and top, and the strip clips at its own edge — so whichever
   figure starts a row (or the strip) loses it, and the rules stay right when
   the row wraps on a narrow screen. */
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(175px,1fr));gap:0;margin-bottom:var(--gap-section);
  background:var(--surface);border:1px solid var(--border);border-radius:var(--r-panel);overflow:hidden}
.kpi{background:var(--surface);padding:16px var(--inset);box-shadow:-1px -1px 0 var(--border);cursor:pointer}
.kpi:hover{background:var(--surface-2)}
.kpi .num{font-size:var(--fs-page);font-weight:var(--fw-bold);margin:var(--gap-pair) 0;letter-spacing:-0.02em}
.kpi > span{display:block}   /* a label and its figure are a PAIR, not two inline boxes */
.kpi > .stat-big{margin:var(--gap-pair) 0}
.kpi .lbl{color:var(--muted);font-size:var(--fs-small);font-weight:var(--fw-medium)}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:16px}
@media(max-width:1100px){.grid2{grid-template-columns:1fr}}
.dashgrid{display:grid;grid-template-columns:1fr 340px;gap:16px;align-items:start}
@media(max-width:1100px){.dashgrid{grid-template-columns:1fr}}
/* A page panel is a square block with a hairline, not a lifted card: on the
   cream ground the white fill and the border already say where it ends. */
.panel{background:var(--surface);border-radius:var(--r-panel);border:1px solid var(--border);margin-bottom:16px;overflow:hidden}
/* The row WRAPS: on a phone a title and its right-hand label used to squeeze
   each other until both broke onto two lines („Woraus es / besteht" beside
   „laut Verträgen · pro / Monat"). Wrapping keeps the title whole and moves
   the label underneath only when the two do not fit on one line. */
.panel h3{font-size:var(--fs-lead);padding:15px var(--inset);border-bottom:1px solid var(--border);display:flex;flex-wrap:wrap;align-items:center;gap:2px 8px;font-weight:var(--fw-bold)}
.panel h3 .spacer{flex:1}
.panel .body{padding:10px var(--inset) 14px}
/* A table in a panel starts and ends on the panel's inset, so its first
   column stands under the heading above it and its last under the count. */
.panel th:first-child,.panel td:first-child{padding-left:var(--inset)}
.panel th:last-child,.panel td:last-child{padding-right:var(--inset)}
/* …and a table inside a padded body already stands on that inset: its outer
   cells add nothing, or it would sit one inset further in than the text
   around it. */
.panel .body th:first-child,.panel .body td:first-child{padding-left:0}
.panel .body th:last-child,.panel .body td:last-child{padding-right:0}
.row{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--surface-2);cursor:pointer}
.row:last-child{border-bottom:0}
.row .grow{flex:1;min-width:0}
.row .title{font-weight:var(--fw-semibold);font-size:var(--fs-small);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.row .meta{color:var(--muted);font-size:var(--fs-caption);margin-top:1px}

/* stat cards (dashboard right rail) */
.stat-big{font-size:var(--fs-page);font-weight:var(--fw-bold);letter-spacing:-0.02em}
.stat-big small{font-size:var(--fs-small);color:var(--muted);font-weight:var(--fw-medium)}
.progress{height:7px;background:var(--surface-2);border-radius:99px;margin:12px 0 8px;overflow:hidden}
.progress i{display:block;height:100%;background:var(--accent-mid);border-radius:99px}
.donut-wrap{display:flex;flex-direction:column;align-items:center;padding:8px 0 4px}
.donut{width:150px;height:150px;border-radius:50%;position:relative}
.donut::after{content:"";position:absolute;inset:22px;background:var(--surface);border-radius:50%}
.donut .pct{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:var(--fs-title);font-weight:var(--fw-bold);z-index:1}

/* A pill is a word, not a constant: the interface face, sentence case, the
   caption size and a medium weight. Mono and upper case made „ongoing“ read
   as ONGOING and a status read as the value the database stores — and made a
   one-word label the loudest thing in its row. It helps scanning; it does not
   compete with the title beside it. */
.pill{display:inline-block;padding:1px 8px;border-radius:999px;font-size:var(--fs-caption);
  font-weight:var(--fw-medium);line-height:1.6;white-space:nowrap}
.pill::first-letter{text-transform:uppercase}
.p-red{background:var(--bad-tint);color:var(--bad)}.p-green{background:var(--ok-tint);color:var(--ok)}
.p-amber{background:var(--warn-tint);color:var(--warn)}.p-blue{background:var(--live-tint);color:var(--live)}
.p-purple{background:var(--violet-tint);color:var(--info)}.p-gray{background:var(--grey-tint);color:var(--ink-2)}
.p-accent{background:var(--accent-soft);color:var(--accent-strong)}

/* `appearance:none` here too: Safari otherwise applies its own control
   chrome and ignores the background on a <button>. `inline-flex` + centring
   is what makes a button the same height as the field beside it whatever is
   in it — text alone, or text with a badge. */
.btn{-webkit-appearance:none;appearance:none;
  display:inline-flex;align-items:center;justify-content:center;gap:6px;
  background:var(--accent-fill);color:var(--on-fill);border:1px solid var(--accent-fill);
  border-radius:var(--r-btn);padding:0 16px;font:inherit;font-size:var(--fs-body);
  font-weight:var(--fw-semibold);line-height:1.2;cursor:pointer;text-align:center}
.btn:hover{background:var(--accent-fill-hover);border-color:var(--accent-fill-hover)}
/* A destructive button takes the red family — the pale tint as the surface, the
   dark member as the ink. `.p-red` alone loses to `.btn` (same weight, declared
   earlier), so it has to be said with the button's own specificity. */
.btn.p-red{background:var(--bad-tint);border-color:var(--bad-tint);color:var(--bad)}
.btn.p-red:hover{background:#efcfce;border-color:#efcfce}
/* WHITE, not transparent: „Zurück“ and „Später ausfüllen“ sit on the cream
   ground, and a transparent button there is a rectangle of nothing
   with a line round it. White gives them a surface of their own, the way the
   panels have one. */
.btn.ghost{background:var(--surface);color:var(--accent);border:1px solid var(--border-strong)}
/* THREE LEVELS, and a section has at most ONE primary (§5.30):
     primary    `.btn`           solid green — the one thing this section wants
     secondary  `.btn.ghost`     white with a hairline — a real, lesser action
     tertiary   `.btn.tertiary`  text in the accent ink — "see more", "continue →"
   When everything is green, green stops meaning "this one". */
.btn.tertiary{background:transparent;border-color:transparent;color:var(--accent);padding:0 6px}
.btn.tertiary:hover{background:var(--accent-soft);border-color:transparent}
/* A tertiary button that starts a line lines its TEXT up with the text above,
   not its padding box. */
.morelinks .btn.tertiary:first-child,.feedcard .btn.tertiary,.rk-cta .btn.tertiary{margin-left:-6px}
.btn.ghost:hover{background:var(--accent-soft);border-color:var(--accent-mid)}
/* A destructive action that is not yet the final step still looks like one:
   red ink and a red edge, never the neutral ghost it would otherwise inherit
   (`.btn.ghost` is declared after `.btn.p-red` and would win). */
.btn.ghost.p-red{background:var(--surface);color:var(--bad);border-color:var(--bad)}
.btn.ghost.p-red:hover{background:var(--bad-tint);border-color:var(--bad);color:var(--bad)}
.btn.green{background:var(--ok-tint);border-color:var(--ok-tint);color:var(--ok)}
.btn.red{background:var(--bad-tint);border-color:var(--bad-tint);color:var(--bad)}
.btn.sm{padding:0 11px;font-size:var(--fs-small);min-height:30px}
/* ================= FORM CONTROLS — ONE STYLE, EVERY BROWSER =================
 *
 * THE RULE FOR THIS PROJECT: never style a field ad hoc. Any new input,
 * select or textarea must inherit from this block, and anything it needs
 * beyond it (a width, an alignment) is added on top — never a second set of
 * borders, paddings or heights.
 *
 * WHY IT IS THIS LONG. Safari does not merely differ from Chrome here, it
 * ignores half of what the shorthand above used to say. Left alone it draws:
 * its own inset shadow and rounded corners on a text field, a native
 * double-chevron on a select that is a different height from everything
 * beside it, and a date field whose value box has its own padding and does
 * not stretch — which is exactly the misalignment on the Neue-Aufgabe dialog,
 * where "Was ist zu tun" and "Fällig am" ended up different heights and
 * neither matched the button next to them.
 *
 * So: appearance is reset to none, the box is described explicitly, and the
 * one control with a native affordance worth keeping — the select's chevron —
 * is drawn back as a background image so it looks the same everywhere.
 *
 * `--control-h` is the shared height. Buttons use it too, so a field and the
 * button that submits it line up on the same row without anyone nudging a
 * margin. */
:root{ --control-h:38px; }

/* A button the browser styles is 13.33px system-ui — off the scale and off
   the typeface. Every button inherits the text it sits in unless a rule
   gives it a step; `:where()` weighs nothing, so any rule still wins. */
:where(button){font:inherit}

/* Matched by EXCLUSION, not by listing the types. The list used to be
 * `input[type=text], input[type=email], …` — and `<input placeholder="…">`
 * with no type at all is a text field that matched none of them, so the
 * Neue-Aufgabe title box was drawn 20px high next to a 38px date field. A
 * rule everyone must remember to opt into is a rule that gets forgotten;
 * this one has to be opted OUT of.
 *
 * The exclusions are wrapped in `:where()` DELIBERATELY. A bare chain of nine
 * `:not([type=…])` carries the specificity of nine attribute selectors, which
 * beats `.ml-row > label input` and every other local rule in this file — the
 * money-line fields silently grew from 32px to 38px and broke the row they
 * sit in. `:where()` contributes nothing, so this whole selector weighs the
 * same as a bare `input`, and any component can still say otherwise. */
select,
input:not(:where([type=checkbox],[type=radio],[type=file],[type=range],[type=color],[type=submit],[type=button],[type=image],[type=hidden])),
textarea{
  -webkit-appearance:none;appearance:none;
  min-height:var(--control-h);
  width:auto;
  border:1px solid var(--border-strong);
  border-radius:var(--r-input);
  padding:0 11px;
  /* The WEIGHT is pinned, not inherited: a field written inside its `<label>`
     took the caption's semibold, so the same value was bold in one form and
     regular in the next — on Mein Konto the name stood bold over a regular
     placeholder, and nobody could tell which was the label. */
  font:inherit;font-size:var(--fs-body);font-weight:var(--fw-regular);line-height:1.35;
  background:var(--surface);color:var(--ink);
  /* Safari draws a rounded inset shadow on text fields and ignores
   * border-radius on some of them without this. */
  box-shadow:none;
  vertical-align:middle;
}
textarea{padding:9px 11px;min-height:calc(var(--control-h) * 2);line-height:1.5;
  resize:vertical}

/* A one-line field centres its own text rather than relying on padding that
   the date and select controls do not honour the same way. */
select,
input:not(:where([type=checkbox],[type=radio],[type=file],[type=range],[type=color],[type=submit],[type=button],[type=image],[type=hidden])):not([type=date]){
  height:var(--control-h);
}

/* The chevron Safari would otherwise draw itself, at its own size. */
select{
  padding-right:30px;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%236b7280' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right 11px center;
  cursor:pointer;
}
select::-ms-expand{display:none}

/* A date field is a text field in the reader's format (the date-field block
   in public/app.js — no native date control is ever shown, because that one
   draws the browser's language instead of the page's). The calendar icon is
   drawn here, in the slot the select's chevron uses, and a click on it opens
   the picker. The only native date input left is the invisible
   `.date-field-picker` beside it, sized inline. */
input.date-field{
  padding-right:34px;
  font-variant-numeric:tabular-nums;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14'%3E%3Cg fill='none' stroke='%236b7280' stroke-width='1.4' stroke-linecap='round'%3E%3Crect x='1.5' y='2.5' width='11' height='10' rx='1.5'/%3E%3Cpath d='M1.5 5.5h11M4.5 1v3M9.5 1v3'/%3E%3C/g%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right 11px center;
}
/* Wide enough for „14.09.2026“ beside the icon in any column it is put in —
   a narrow dialog row clipped the year's last digit under the calendar. */
input.date-field{min-width:9.5em}

/* A field that is wrong: marked on the field itself, the reason directly
   under it (`fieldError` in app.js). */
input.field-bad,select.field-bad,textarea.field-bad{border-color:var(--bad);
  box-shadow:0 0 0 3px var(--bad-tint)}
.field-msg{color:var(--bad);font-size:var(--fs-small);line-height:1.4;margin-top:4px;font-weight:var(--fw-medium)}
/* A row of controls aligns to its bottom edge — until a message under one
   field makes that edge move. Then the row holds its top line instead, so the
   buttons beside the field stay level with it. */
.frow:has(.field-msg){align-items:flex-start}

/* Search fields get Safari's magnifier and a cancel button otherwise. */
input[type=search]::-webkit-search-decoration,
input[type=search]::-webkit-search-cancel-button{-webkit-appearance:none}

input::placeholder,textarea::placeholder{color:var(--faint);opacity:1}

select:focus,input:focus,textarea:focus{outline:none;border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-soft)}
select:disabled,input:disabled,textarea:disabled{
  background:var(--surface-2);color:var(--muted);cursor:not-allowed}

/* Checkboxes and radios keep their native look — a hand-drawn tick is a
   worse tick — but must not inherit the reset above. */
input[type=checkbox],input[type=radio]{
  -webkit-appearance:auto;appearance:auto;
  height:auto;min-height:0;width:auto;padding:0;border:0;background:none;
  accent-color:var(--accent);cursor:pointer;vertical-align:middle}
input[type=file]{-webkit-appearance:auto;appearance:auto;height:auto;
  min-height:0;border:0;padding:0;background:none;font:inherit}

/* The button shares the height so a field and its action sit on one line. */
.btn{min-height:var(--control-h)}

table{width:100%;border-collapse:collapse;font-size:var(--fs-small)}
/* A column heading is a word, like a pill (§5.30): the interface face,
   sentence case, caption size. Monospace capitals made every table read as
   an admin console. */
th{color:var(--muted);text-align:left;font-size:var(--fs-caption);font-weight:var(--fw-semibold);padding:9px 12px;border-bottom:1px solid var(--border);background:var(--surface);position:sticky;top:0}
td{padding:9px 12px;border-bottom:1px solid var(--surface-2);vertical-align:top}
/* `vertical-align:top` is right for a cell of prose — a long Betriebskosten
   note should start level with its neighbours, not float in the middle. It is
   wrong for a row of ACTIONS: a button is ~30px against a ~19px line, so the
   button set the row height and the text sat 8px above the labels. Measured,
   not guessed: text centre 67.3 vs button centre 75.0 in a real invitation
   row; with this, both are 75.0 and the status pill comes with them.

   Targeted by CONTENT rather than by a class, which is also what makes it
   safe for public/impressum.html and public/datenschutz.html: those load this
   stylesheet with none of the app's scaffolding, and a global rule aimed at
   the operator UI has reached them before (`table{min-width:max-content}`
   clipped two columns of the privacy policy). Neither page has a single
   `.btn`, so this selector cannot match there at all. */
tr:has(.btn) td{vertical-align:middle}
tr:hover td{background:var(--surface-2)}
.tbl-wrap{max-height:calc(100vh - 260px);overflow:auto}

.kanban{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;align-items:start}
@media(max-width:1100px){.kanban{grid-template-columns:repeat(2,1fr)}}
.kcol{background:rgba(255,255,255,.6);border-radius:var(--r-card);padding:10px;min-height:200px}
.kcol h4{font-size:var(--fs-label);text-transform:uppercase;letter-spacing:.05em;color:var(--faint);padding:4px 6px 10px;display:flex;gap:6px;font-family:var(--font-mono);font-weight:var(--fw-semibold)}
.kcol h4 .cnt{background:var(--surface);border-radius:var(--r-sm);padding:0 7px;font-size:var(--fs-label)}
.kcard{background:var(--surface);border-radius:var(--r-md);padding:11px 13px;margin-bottom:8px;cursor:grab;border:1px solid var(--border)}
.kcard:hover{box-shadow:var(--shadow-2)}
.kcard .t{font-weight:var(--fw-semibold);font-size:var(--fs-small);line-height:1.35;margin-bottom:6px}
.kcard .m{display:flex;gap:6px;flex-wrap:wrap;align-items:center;font-size:var(--fs-label);color:var(--muted)}
.kcard.dragging{opacity:.4}
.kcol.dragover{outline:2px dashed var(--accent);outline-offset:-4px}
.kcard .need-accept{background:var(--warn-tint);color:var(--warn);border-radius:var(--r-sm);padding:2px 6px;font-size:var(--fs-label);font-weight:var(--fw-bold)}

.transcript{background:var(--surface-2);border-radius:var(--r-md);padding:14px;max-height:320px;overflow:auto}
.tline{display:flex;gap:10px;margin-bottom:10px}
.tline .who{flex-shrink:0;width:64px;font-size:var(--fs-label);font-weight:var(--fw-semibold);text-transform:uppercase;padding-top:3px;font-family:var(--font-mono);letter-spacing:.02em}
.tline.agent .who{color:var(--accent-strong)}.tline.caller .who{color:var(--warn)}
.tline .txt{background:var(--surface);color:var(--ink-2);border-radius:var(--r-md);padding:7px 11px;font-size:var(--fs-small);line-height:1.45;border:1px solid var(--border)}
.tline.agent .txt{background:var(--accent-soft);box-shadow:none}

.modal-bg{position:fixed;inset:0;background:rgba(24,49,83,.45);display:flex;align-items:center;justify-content:center;z-index:50;padding:20px;backdrop-filter:blur(2px)}
.modal{background:var(--surface);border-radius:var(--r-card);max-width:700px;width:100%;max-height:88vh;overflow:auto;padding:24px 26px;box-shadow:var(--shadow-2)}
.modal h2{font-size:var(--fs-card-title);margin-bottom:4px;letter-spacing:-0.01em;font-weight:var(--fw-bold)}
.modal .sub{color:var(--muted);font-size:var(--fs-small);margin-bottom:14px}
/* Field rows: a label sits ABOVE its control, wherever the row is used.
   These were scoped to .modal, so the same markup ran inline anywhere
   else — the label and its input on one line, reading as prose. */
.frow{display:flex;gap:10px;margin:10px 0;flex-wrap:wrap;align-items:flex-end}
.frow label{display:flex;flex-direction:column;gap:3px}
label{font-size:var(--fs-caption);color:var(--muted);font-weight:var(--fw-semibold)}
.frow label input,.frow label select{width:100%}
.modal .actions{display:flex;gap:10px;justify-content:flex-end;margin-top:18px;flex-wrap:wrap}
.kv{display:grid;grid-template-columns:170px 1fr;gap:6px 12px;font-size:var(--fs-small);margin:var(--gap-related) 0}
.kv .k{color:var(--muted)}

#toasts{position:fixed;bottom:20px;right:20px;z-index:99;display:flex;flex-direction:column;gap:8px}
.toast{background:var(--brand-navy);color:#fff;padding:12px 16px;border-radius:var(--r-card);font-size:var(--fs-small);box-shadow:var(--shadow-2);max-width:380px}
.toast b{color:#bfe0cb}

.filters{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px;align-items:center}
.section-title{font-size:var(--fs-label);font-weight:var(--fw-semibold);margin:16px 0 var(--gap-related);color:var(--faint);text-transform:uppercase;letter-spacing:.05em;font-family:var(--font-mono);display:flex;align-items:center;gap:8px}
/* What a section title heads sits RELATED to it whatever it is — a field
   row's or a key/value list's own top margin used to win the collapse and put
   it 8, 12 or 14px away depending on the card. */
.section-title + *{margin-top:0}
.empty{color:var(--muted);font-size:var(--fs-small);padding:18px 0;text-align:center}
a.link{color:var(--accent-strong);cursor:pointer;text-decoration:underline;text-underline-offset:2px;font-weight:var(--fw-semibold)}
.due-red{color:var(--bad);font-weight:var(--fw-bold)}.due-amber{color:var(--warn);font-weight:var(--fw-bold)}
.comments{margin-top:10px}
.comment{background:var(--surface-2);border-radius:var(--r-md);padding:9px 12px;font-size:var(--fs-small);margin-bottom:6px}
.comment .by{font-weight:var(--fw-bold);font-size:var(--fs-label);color:var(--muted)}

/* calendar */
.calgrid{display:grid;grid-template-columns:repeat(7,1fr);gap:6px}
.calgrid .dow{font-size:var(--fs-label);font-family:var(--font-mono);text-transform:uppercase;color:var(--faint);text-align:center;padding:4px 0;font-weight:var(--fw-semibold)}
.calday{background:var(--surface);border-radius:var(--r-md);min-height:86px;padding:6px;font-size:var(--fs-label);border:1px solid var(--border)}
.calday.other{opacity:0;box-shadow:none}
.calday.today{outline:2px solid var(--accent);outline-offset:-2px}
.calday .dnum{font-weight:var(--fw-bold);font-size:var(--fs-label);color:var(--muted);margin-bottom:4px}
.calevt{display:block;border-radius:var(--r-sm);padding:2px 6px;margin-bottom:3px;font-size:var(--fs-label);font-weight:var(--fw-semibold);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer}
.calevt.e-task{background:var(--accent-soft);color:var(--accent-strong)}
.calevt.e-rem{background:var(--bad-tint);color:var(--bad)}
.calevt.e-sched{background:var(--live-tint);color:var(--live)}

.docbody{background:var(--surface-2);border-radius:var(--r-md);padding:16px;white-space:pre-wrap;font-size:var(--fs-small);line-height:1.55;max-height:420px;overflow:auto;font-family:var(--font-mono)}

/* manual-steps disclosure (contractor fallback when a task is auto-resolvable) */
.manual-steps{margin-top:14px;border-top:1px solid var(--border);padding-top:10px}
.manual-steps summary{font-size:var(--fs-label);font-weight:var(--fw-semibold);color:var(--muted);cursor:pointer;list-style:none;text-transform:uppercase;letter-spacing:.04em;font-family:var(--font-mono)}
.manual-steps summary::-webkit-details-marker{display:none}
.manual-steps summary::before{content:"▸ ";color:var(--faint)}
.manual-steps[open] summary::before{content:"▾ "}

/* email-delivery status chip on notification rows */
.mailstat{font-size:var(--fs-label);font-family:var(--font-mono);color:var(--muted);white-space:nowrap}
.mailstat.ok{color:var(--ok)}
.mailstat.err{color:var(--bad)}

/* login */
/* ---- the page before sign-in: what this is, then the form -------------- */
/* Two columns on a desktop, one on a phone, and the form comes FIRST in the
   source so a returning reader — who does not need the pitch — tabs straight
   into the address field. `order` puts the pitch on the left visually. */
.landing{min-height:100vh;background:var(--bg);display:flex;align-items:center;
  justify-content:center;padding:48px 24px;position:relative}
/* Named areas, so the DOM can stay in the order a phone needs (pitch, form,
   detail) while a desktop still puts the form beside the pitch rather than
   between it and the points. */
.landing-inner{display:grid;grid-template-columns:minmax(0,1.15fr) 380px;
  gap:0 56px;align-content:center;max-width:1000px;width:100%;
  grid-template-areas:"pitch login" "points login"}
.landing-pitch{grid-area:pitch;max-width:520px}
.landing-name{font-size:var(--fs-display);font-weight:var(--fw-bold);letter-spacing:-0.03em;line-height:1}
.landing-tag{font-size:var(--fs-card-title);line-height:1.45;margin-top:14px;color:var(--ink);
  font-weight:var(--fw-semibold);letter-spacing:-0.01em}
.landing-lead{font-size:var(--fs-lead);line-height:1.65;margin-top:12px;color:var(--muted)}
/* §5.20a — the AI campaign page's clip: the function in four drawn scenes, a
   big headline over a card in the app's own look, read at phone size. The
   HTML is each scene's finished state and stands still, all four in a column
   (no script, or reduced motion); `.live` — set by the page's script — stacks
   them in one place and shows one at a time, the script drawing each from one
   clock. The card floats over the page, so it is the one thing with a shadow. */
.clip{margin-top:28px;min-width:0}
.clip-stage{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--gap-section)}
.clip-head{font-size:var(--fs-figure);font-weight:var(--fw-bold);line-height:1.1;
  letter-spacing:-0.02em;color:var(--heading)}
.clip-head span{display:block;color:var(--accent)}
.clip-card{margin-top:16px;background:var(--surface);border:1px solid var(--border);
  border-radius:var(--r-card);box-shadow:var(--shadow-1);padding:var(--inset)}
.clip-card-title{font-size:var(--fs-card-title);font-weight:var(--fw-bold);color:var(--heading)}
.clip-card-sub{font-size:var(--fs-small);color:var(--muted)}
.clip-typed{margin-top:12px;min-height:2.6em;font-size:var(--fs-title);line-height:1.3;color:var(--ink)}
.clip-chip{display:inline-block;margin-top:14px;padding:5px 12px;border-radius:999px;
  background:var(--accent-soft);color:var(--accent);font-size:var(--fs-small);font-weight:var(--fw-semibold)}
.clip-firms{list-style:none;margin-top:10px}
.clip-firms li{display:flex;justify-content:space-between;gap:12px;align-items:baseline;
  padding:9px 0;border-bottom:1px solid var(--border);font-size:var(--fs-card-title)}
.clip-firms li:last-child{border-bottom:0}
.clip-ok{color:var(--accent);font-size:var(--fs-small);font-weight:var(--fw-semibold);white-space:nowrap}
.clip-call{display:flex;align-items:center;gap:12px}
.clip-avatar{flex:none;width:44px;height:44px;border-radius:50%;display:grid;place-items:center;
  background:var(--accent-fill);color:var(--on-fill);font-weight:var(--fw-bold)}
.clip-who{flex:1;display:flex;flex-direction:column;font-size:var(--fs-card-title)}
.clip-who span{font-size:var(--fs-small);color:var(--accent);font-variant-numeric:tabular-nums}
.clip-wave{display:flex;gap:3px;align-items:center;height:28px}
.clip-wave i{width:4px;height:100%;background:var(--accent-fill);border-radius:var(--r-mark);transform:scaleY(.35)}
.clip-bubble{margin-top:12px;max-width:88%;padding:10px 14px;border-radius:var(--r-card);
  font-size:var(--fs-title);line-height:1.3;min-height:1.3em}
.clip-bubble.me{background:var(--surface-2);color:var(--ink)}
.clip-bubble.them{margin-left:auto;background:var(--accent-soft);color:var(--heading)}
.clip-offer{display:grid;grid-template-columns:1fr auto;column-gap:12px;padding:12px 0;
  border-bottom:1px solid var(--border);font-size:var(--fs-card-title)}
.clip-offer:last-child{border-bottom:0}
.clip-price{font-size:var(--fs-title);font-weight:var(--fw-bold);color:var(--heading);
  font-variant-numeric:tabular-nums;text-align:right}
.clip-kind{grid-column:2;text-align:right;font-size:var(--fs-small);color:var(--muted)}
.clip-foot{display:none}
.clip-note{margin-top:10px;font-size:var(--fs-caption);color:var(--muted)}
/* Live: one scene at a time, in one place; what has not happened yet is not there. */
.clip.live .clip-stage{gap:0}
.clip.live .clip-scene{grid-area:1/1;opacity:0;visibility:hidden;transition:opacity .35s,visibility .35s;
  display:flex;flex-direction:column}
/* Every card as tall as the tallest, so the box does not change size between scenes. */
.clip.live .clip-card{flex:1}
.clip.live .clip-scene.on{opacity:1;visibility:visible}
.clip.live [data-at]{opacity:0;transform:translateY(6px);transition:opacity .3s,transform .3s}
.clip.live [data-at].on{opacity:1;transform:none}
.clip.live .clip-bubble.empty{visibility:hidden}
.clip.live .typing::after{content:"";display:inline-block;width:2px;height:1em;margin-left:2px;
  vertical-align:-0.12em;background:var(--accent);animation:clip-caret 1s steps(1) infinite}
.clip.live .clip-scene.on .clip-wave i{animation:clip-wave .9s ease-in-out infinite}
.clip.live .clip-wave i:nth-child(2){animation-delay:-.2s}.clip.live .clip-wave i:nth-child(3){animation-delay:-.45s}
.clip.live .clip-wave i:nth-child(4){animation-delay:-.1s}.clip.live .clip-wave i:nth-child(5){animation-delay:-.6s}
.clip.live .clip-foot{display:flex;align-items:center;gap:var(--gap-related);margin-top:14px}
.clip-rail{flex:1;display:grid;grid-template-columns:repeat(4,1fr);gap:6px}
.clip-rail i{position:relative;height:3px;background:var(--border);border-radius:var(--r-mark);overflow:hidden}
.clip-rail i::after{content:"";position:absolute;inset:0;background:var(--accent-fill);
  transform:scaleX(var(--p,0));transform-origin:left}
.clip.paused .clip-wave i,.clip.paused .typing::after{animation-play-state:paused}
@keyframes clip-wave{0%,100%{transform:scaleY(.35)}50%{transform:scaleY(1)}}
@keyframes clip-caret{50%{opacity:0}}
/* §5.20 — the tenant invitation's calculator: the ads' sum with the reader's
   own rent, drawn with the app's own rent-or-buy panel (§5.8d): the soft blue
   band carries the words, and the figures stand in its white `.buyfig` cards
   — `.buysplit`, `.buyedit`, `.buyval`, `.hero-label` are those classes, not
   copies. Only what the landing page needs on top is here. The global
   `table{min-width:max-content}` is for the app and is undone — this page has
   no `.tbl-wrap`. */
.calc{margin-top:22px;background:var(--edu);border:1px solid var(--edu-border);
  border-radius:var(--r-card);padding:16px;color:var(--edu-ink);container-type:inline-size}
.calc .buyval:empty{display:none}
.calc-out .buyfig{margin-top:14px}
.calc-table{min-width:0;width:100%;font-variant-numeric:tabular-nums}
.calc-table th,.calc-table td{background:transparent;position:static;padding:7px 0;
  border-bottom:1px solid var(--border)}
.calc-table tbody tr:last-child td{border-bottom:0}
/* The app's row highlight says "this row opens something"; these do not. */
.calc-table tr:hover td{background:none}
.calc-table th:not(:first-child),.calc-table td:not(:first-child){text-align:right}
.calc-table td.calc-repaid{color:var(--accent);font-weight:var(--fw-semibold)}
/* §5.20 — a figure held back for the account: blurred, its hint above it on
   hover or focus (a tap focuses it on a phone). The hint floats, so it is the
   one thing here with a shadow; it opens towards the inside of the card. */
.calc-locked{position:relative;display:inline-block;cursor:pointer;border-radius:var(--r-sm)}
.calc-locked > span{filter:blur(5px);user-select:none}
.calc-locked:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.calc-locked::after{content:attr(data-hint);position:absolute;bottom:calc(100% + 6px);z-index:5;
  width:max-content;max-width:220px;white-space:normal;text-align:left;
  background:var(--edu-ink);color:var(--on-fill);font-size:var(--fs-caption);
  font-weight:var(--fw-medium);line-height:1.4;padding:7px 10px;border-radius:var(--r-card);
  box-shadow:var(--shadow-2);opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .12s}
.calc-locked.start::after{left:0}
.calc-locked.end::after{right:0}
.calc-locked:hover::after,.calc-locked:focus::after{opacity:1;visibility:visible}
/* In running text a figure can sit at the end of a line, where a hint opening
   from it would leave the card (and on a phone, the page). There the hint
   hangs from the PARAGRAPH, above it and inside its width. */
.calc-basis,#calc-split{position:relative}
.calc-basis .calc-locked,#calc-split .calc-locked{position:static}
.calc-basis .calc-locked::after,#calc-split .calc-locked::after{left:0;right:auto;max-width:100%}
.calc-asrent{font-size:var(--fs-small);font-weight:var(--fw-semibold);margin-top:14px}
.calc-next{font-size:var(--fs-small);margin-top:8px}
.calc-basis{font-size:var(--fs-caption);color:var(--muted);line-height:1.5;margin-top:12px}
/* The app stacks question and answer under 720px of WINDOW; here the column
   is narrow on any window, so the card asks its own width. */
@container (max-width:460px){
  .calc .buysplit{grid-template-columns:1fr}
  .calc .buyask{padding-right:0;padding-bottom:14px;border-right:0;border-bottom:1px solid var(--border)}
  .calc .buyget{padding-left:0;padding-top:14px}
}
/* Beside a calculator the form is centred against a column that grows as the
   answer appears, so it slid down while somebody typed. On a desktop it
   starts at the top and stays in view while the table is read, clear of the
   language switch in the corner; a phone stacks the two and needs neither. */
@media (min-width:861px){
  .landing:has(.calc){padding-top:72px}
  .landing-inner:has(.calc) .login{align-self:start;position:sticky;top:72px}
}

.landing-points{grid-area:points;list-style:none;margin-top:28px;display:grid;
  gap:20px;max-width:520px}
.landing-points li{display:flex;gap:14px;align-items:flex-start}
/* A pale disc — a tint behind the green ink, never the fill behind it. */
.lp-ico{flex:0 0 34px;height:34px;border-radius:var(--r-sm);background:var(--accent-soft);
  color:var(--accent);display:flex;align-items:center;justify-content:center}
.lp-ico .ico{width:18px;height:18px}
.landing-points b{font-size:var(--fs-body);display:inline}
.lp-body{display:block;font-size:var(--fs-body);line-height:1.6;color:var(--muted);margin-top:4px}

.landing-lang{position:absolute;top:20px;right:24px;display:flex;gap:4px}
.langbtn{border:1px solid var(--border);background:var(--surface);color:var(--muted);
  border-radius:var(--r-btn);padding:5px 11px;font-size:var(--fs-caption);font-weight:var(--fw-bold);
  letter-spacing:.04em;cursor:pointer;font-family:inherit}
.langbtn:hover{color:var(--ink)}
.langbtn.on{background:var(--accent-fill);color:var(--on-fill);border-color:transparent}
/* The invitation page (§5.20) switches language with links, not buttons. */
a.langbtn{text-decoration:none}
/* As links (the invitation and pricing pages, `?lang=`), the global
   `a`/`a:visited` ink would outweigh `.langbtn` — said again with a parent. */
.landing-lang a.langbtn{color:var(--muted)}
.landing-lang a.langbtn:hover{color:var(--ink)}
.landing-lang a.langbtn.on{color:var(--on-fill)}

@media (max-width:860px){
  /* `display:block`, not flex — taken out of the corner, the language row is
     a sibling of the grid, and in a flex row it stood BESIDE it and squeezed
     the whole page into two thirds of the screen. */
  .landing{display:block;padding:24px 20px 40px}
  .landing-inner{grid-template-columns:1fr;max-width:460px;margin:0 auto;
    grid-template-areas:"pitch" "login" "points";gap:26px 0}
  .landing-points{margin-top:0}
  .landing-lang{position:static;max-width:460px;margin:0 auto 16px;
    justify-content:flex-end}
}

.login{grid-area:login;background:var(--surface);border-radius:var(--r-card);
  padding:32px;width:100%;box-shadow:var(--shadow-2);align-self:center}
.login h2{font-size:var(--fs-card-title);margin-bottom:4px;letter-spacing:-0.02em;font-weight:var(--fw-bold)}
.login .sub{color:var(--muted);font-size:var(--fs-small);margin-bottom:18px}
.login-note{margin-top:14px;padding-top:12px;border-top:1px solid var(--border);
  font-size:var(--fs-caption);color:var(--faint);line-height:1.6}
/* §5.17 — directly under the button it qualifies. Ink, not faint: it is a
   statement the reader is agreeing to, and the link in it has to read as one. */
.login-agree{margin:10px 0 0;font-size:var(--fs-caption);color:var(--muted);line-height:1.6}
.login-agree a{color:var(--accent-strong);text-decoration:underline;text-underline-offset:2px}
/* The answer to "Code anfordern" stands apart from the privacy line above it. */
.login .login-msg:not(:empty){margin:14px 0 18px}
.landing-legal{position:absolute;bottom:18px;left:0;right:0;text-align:center;
  font-size:var(--fs-caption);display:flex;gap:18px;justify-content:center}
.landing-legal a{color:var(--muted);text-decoration:none}
.landing-legal a:hover{color:var(--ink);text-decoration:underline;text-underline-offset:2px}
@media (max-width:860px){.landing-legal{position:static;margin-top:28px}}
.sidebar-legal{display:block;margin-top:2px}
.sidebar-legal a{color:inherit;text-decoration:none}
.sidebar-legal a:hover{color:var(--ink);text-decoration:underline;text-underline-offset:2px}
/* §5.36 — *Feedback* opens a dialog, so it is a button, drawn as its link
   neighbours are. */
.sidebar-linkbtn{appearance:none;-webkit-appearance:none;background:none;border:0;padding:0;margin:0;
  font:inherit;color:inherit;cursor:pointer}
.sidebar-linkbtn:hover{color:var(--ink);text-decoration:underline;text-underline-offset:2px}
.fb-err{color:var(--bad)}
.login input{width:100%;margin-bottom:10px}
/* A refusal under the sign-in field sits with the field it names, and keeps
   the gap to the button the field had. */
.login .field-msg{margin:-4px 0 12px}
.login .btn{width:100%;padding:11px;border-radius:var(--r-btn)}
/* §5.20 — what signing up costs, first thing under the form's title: the
   last ad's promise, before anything is asked. The subtitle's size, in the
   accent ink and medium weight so it reads as the answer to "what does this
   cost?" rather than as a second subtitle; the title's 4px gap above it. */
.login-cost{margin:0 0 2px;font-size:var(--fs-small);font-weight:var(--fw-medium);
  color:var(--accent);line-height:1.5}
/* §5.37 — „Mit Google anmelden“ is the second way in, so a ghost button under
   a rule that says „oder“. Ink rather than accent: the mark carries Google's
   colours, and a green word beside it reads as ours. */
.login-or{display:flex;align-items:center;gap:12px;margin:16px 0;
  font-size:var(--fs-caption);color:var(--faint)}
.login-or::before,.login-or::after{content:"";flex:1;border-top:1px solid var(--border)}
.btn.ghost.google-btn{color:var(--ink);gap:10px;text-decoration:none}
.google-mark{width:18px;height:18px;flex:none}
.login .err{color:var(--bad);font-size:var(--fs-small);margin-bottom:8px;min-height:16px}
.login .demo{margin-top:16px;font-size:var(--fs-caption);color:var(--muted);line-height:1.7;border-top:1px solid var(--border);padding-top:12px}
.login .demo a{cursor:pointer;color:var(--accent-strong);text-decoration:underline;text-underline-offset:2px}
.notif-row.unread{background:var(--accent-soft)}
.mono{font-family:var(--font-mono);font-size:var(--fs-caption);word-break:break-all}
/* In a table a mono figure is table DATA, and table data is 14px. */
td.mono{font-size:var(--fs-small)}

/* ---------- Übernahme / onboarding (§5.5) ---------- */












.onb-main{min-width:0}

/* the block form */
.fld{padding:9px 0;border-bottom:1px solid var(--surface-2);display:grid;
  grid-template-columns:230px 1fr;gap:4px 14px;align-items:center}
.fld.wide{grid-template-columns:1fr}
.fld > label{font-size:var(--fs-small);font-weight:var(--fw-medium);color:var(--ink-2)}
.fld-in{display:flex;align-items:center;gap:10px}
.fld-in input[type=text],.fld-in select{min-width:210px}
.fld .sub{grid-column:2;font-size:var(--fs-caption);margin:0}
.fld.wide .sub{grid-column:1}
.fld .unlocks{grid-column:2;font-size:var(--fs-caption);color:var(--accent-strong);margin:0}
.fld.wide .unlocks{grid-column:1}
.unk{display:flex;align-items:center;gap:5px;font-size:var(--fs-caption);color:var(--muted);
  white-space:nowrap;cursor:pointer}
.unk input{margin:0}

/* the repeating contract editor */
.cx{display:flex;flex-direction:column;gap:6px;margin-top:6px}
.cx-head,.cx-row{display:grid;grid-template-columns:1.1fr 1.3fr 150px 120px 120px 34px;gap:8px;
  align-items:center}
.cx-head{font-size:var(--fs-label);text-transform:uppercase;letter-spacing:.05em;color:var(--faint);
  font-weight:var(--fw-bold)}
.cx-row input{width:100%}

/* the one question */
.panel.ask{border-left:4px solid var(--accent)}

/* ---------- address autosuggest ---------- */
.sugg-wrap{position:relative;display:inline-block}
/* .panel clips its corners, and the list opens below the field — past the
   bottom edge of a one-row form such as „Ihr Zuhause aufnehmen“. Any panel
   holding a suggest field lets it out, as .obj-card does. */
.panel:has(.sugg-wrap){overflow:visible}
.fld-in .sugg-wrap input{min-width:210px}
.sugg{position:absolute;z-index:40;left:0;right:0;top:calc(100% + 4px);background:var(--surface);
  border:1px solid var(--border-strong);border-radius:var(--r-md);box-shadow:var(--shadow-2);
  overflow:hidden;max-height:260px;overflow-y:auto;min-width:280px}
.sugg-item{display:flex;align-items:center;gap:10px;padding:8px 12px;cursor:pointer;font-size:var(--fs-small)}
.sugg-item .grow{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sugg-item .sub{font-size:var(--fs-label);white-space:nowrap;flex-shrink:0}
.sugg-item:hover,.sugg-item.on{background:var(--accent-soft)}

/* ---------- money lines: who pays us, whom we pay ---------- */
.ml{display:flex;flex-direction:column;gap:10px;margin-top:8px}
/* Auto-fill with no fixed trailing column: auto-fit plus a fixed trailing
   column once clipped the last field at the panel edge.
   The count follows the ROW's width, not the window's: the same rows sit in
   the wizard's 712px answer and across a full-width Angaben panel, and five
   columns keyed to the viewport left the wizard's fields 131px wide, and
   their words ran under the chevron. 160px is the floor every label,
   placeholder and option in the money rows is worded to fit — except the two
   category lists, which are shared with every other screen and span two
   columns instead (`.ml-wide`). */
.ml-row{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:8px 10px;
  align-items:end;padding:11px 12px;border:1px solid var(--border);border-radius:var(--r-md);
  background:var(--surface-2)}
.ml-row.out{background:var(--c-out-tint)}
/* A field's label is a label, not metadata: sentence case in the interface
   face like every other form label, so it reads as the same type as the
   field under it. Spaced capitals made the two look like different fonts. */
.ml-row > label{display:flex;flex-direction:column;gap:3px;font-size:var(--fs-caption);font-weight:var(--fw-semibold);
  color:var(--muted);min-width:0}
.ml-row > label input,.ml-row > label select{width:100%;font-size:var(--fs-small);
  height:32px;min-height:32px;padding:0 8px;font-weight:var(--fw-regular)}
/* The tighter padding above must not take the chevron's and the calendar's
   slot with it, or the text runs underneath them. */
.ml-row > label select{padding-right:26px;background-position:right 8px center}
.ml-row > label input.date-field{padding-right:30px;background-position:right 8px center}
.ml-row > label.ml-wide{grid-column:span 2}
.ml-row .ml-del{align-self:end;justify-self:start;height:32px;width:34px;padding:0}

/* ---------- per-section document upload ---------- */
.blk-up{background:var(--surface-2);border-radius:var(--r-md);padding:10px 12px;margin-bottom:14px}
.blk-up input[type=file]{font-size:var(--fs-caption)}
.sugbox{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-md);
  padding:8px 12px;margin-top:8px}

/* ---------- §5.5b: the Angaben page — every section, one screen ---------- */
/* Chips, not a stepper. The Übernahme is over; what is left is a long page and
   the wish to be somewhere specific on it, so these are borrowed from the
   wizard's sub-steps (`.substep`) and carry no progress of their own. */
.blkjump{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 16px}
.blockcard{margin-bottom:16px}
.blockcard .blk-count{font-family:var(--font-mono);font-size:var(--fs-label);color:var(--faint);
  font-weight:var(--fw-regular)}
.blockcard .blk-intro{margin:2px 0 12px}
/* The last row of a section should not draw a line into the save bar. */
.blockcard .blockform > .fld:last-child{border-bottom:0}
.blockcard .actions{border-top:1px solid var(--border);padding-top:14px}
/* Said AT the field the server refused, not recited as a list of labels. */
.fld.missing{background:var(--bad-tint);border-radius:var(--r-md);
  box-shadow:0 0 0 6px var(--bad-tint)}
.fld.missing > label{color:var(--bad);font-weight:var(--fw-bold)}

/* ---------- Übernahme: the stages as tabs (§5.5a) ---------- */
.onb-main{min-width:0}
.stepper{position:relative;margin:0 0 var(--gap-section)}
/* The list pages' tabs (`.seg`) on their hairline, scrolling sideways on a
   phone rather than wrapping into a second row of tabs. */
.stagebar{border-bottom:1px solid var(--border);overflow-x:auto;scrollbar-width:none}
.stagebar::-webkit-scrollbar{display:none}
.stage{display:inline-flex;align-items:baseline;gap:6px;white-space:nowrap}
/* The stages share the width of the panel under them, in equal parts — but
   never below their own label, so a long row still scrolls on a phone. */
.stagebar .stage{flex:1 1 0;min-width:max-content;justify-content:center}
.stage-done{color:var(--accent);font-weight:var(--fw-bold)}
.stage.on .seg-n{color:var(--muted)}
/* On a phone the count goes under its label, so all three tabs fit. */
@media (max-width:620px){
  .stage{flex-direction:column;align-items:center;gap:0;line-height:1.3}
  /* …and a label may wrap, down to its longest word, before the row scrolls. */
  .stagebar .stage{min-width:min-content;white-space:normal;text-align:center;padding-left:6px;padding-right:6px}
  /* Every count on one line at the foot of the bar; each label centred in the
     space above its own count, however many lines it took. */
  .stagebar .stage{display:grid;grid-template-rows:1fr auto;justify-items:center}
  .stagebar .stage > :first-child{align-self:center}
  .stage .seg-n{font-size:var(--fs-caption)}
}
.onb-nav{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:4px}
.onb-nav .btn[disabled]{opacity:.4;cursor:default}

/* a field the wizard filled from a document, until it is saved */
input.from-doc,select.from-doc{background-color:var(--info-tint);border-color:var(--info)}
.filled-note{margin-top:8px;font-size:var(--fs-small);line-height:1.5;background:var(--surface);
  border-left:3px solid var(--accent);border-radius:var(--r-sm);padding:9px 12px}
.blk-up{display:flex;flex-wrap:wrap;align-items:center;gap:10px}

/* §5.5c — a document being read holds the page: the question dims and nothing
 * that moves on can be pressed, while the note above says what is happening. */
.doc-reading{font-size:var(--fs-small);line-height:1.5;background:var(--surface);border-radius:var(--r-md);
  border-left:3px solid var(--accent-mid);padding:10px 12px}
.doc-reading .qe-progress{margin-top:8px}
.wizard.reading{opacity:.55;pointer-events:none;transition:opacity .15s}
.doc-reading-on .stepper,.doc-reading-on .substeps{pointer-events:none;opacity:.6}
/* A question the document answered: the field reads as pre-filled, with the
 * quote that answered it underneath. */
.wz-in.from-doc .wz-field{background-color:var(--info-tint);border-color:var(--info)}
.wz-fromdoc{margin:8px auto 0;max-width:560px;font-size:var(--fs-small);line-height:1.45;
  color:var(--muted);text-align:center}

/* ---------- Immobilien: cards, actions, per-object menu ---------- */
.page-head.with-actions{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}
/* Every action in ONE child, so `space-between` puts space between the title
   and the actions — not between the actions themselves, which is what pushed
   „Problem mit KI lösen“ and „+ Erfassen“ to opposite ends of the page.
   SHARED with the object header (`.objhead`), which had it first — so
   anything added here lands on both, and rules meant for only one of them
   must say which. */
.head-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  flex-shrink:0}
.objgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(310px,1fr));gap:16px;
  align-items:start}
/* .panel clips its corners, which would cut the dropdown off inside the card. */
.obj-card{overflow:visible}
/* Two deliberate rows: title + actions, then the pills. Letting one flex row
   wrap put the menu button on a line of its own and the dropdown with it. */
.obj-head{display:flex;align-items:center;gap:10px;padding:14px 18px 0}
.obj-title{flex:1;min-width:0;font-weight:var(--fw-bold);font-size:var(--fs-body);cursor:pointer;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.obj-actions{display:flex;align-items:center;gap:6px;flex-shrink:0}
.obj-pills{display:flex;flex-wrap:wrap;gap:6px;padding:8px 18px 0}
.obj-body{padding:0 18px 14px;cursor:pointer}
.obj-body .kv{margin:10px 0 0}
.btn.icon{padding:5px 10px;line-height:1;font-size:var(--fs-lead)}

.menu-wrap{position:relative;display:inline-block}
.menu{position:absolute;right:0;top:calc(100% + 6px);z-index:50;min-width:210px;
  background:var(--surface);border:1px solid var(--border-strong);border-radius:var(--r-card);
  box-shadow:var(--shadow-2);overflow:hidden;padding:4px}
.menu-item{display:block;width:100%;text-align:left;background:none;border:0;cursor:pointer;
  padding:8px 11px;font-size:var(--fs-body);font-family:inherit;color:var(--ink-2);border-radius:var(--r-sm)}
.menu-item:hover{background:var(--accent-soft);color:var(--ink)}
/* The one entry that cannot be undone: set apart by a rule, and coloured as
   what it is rather than hidden among the edits above it. */
.menu-sep{height:1px;background:var(--border);margin:4px 6px}
.menu-item.danger{color:var(--bad)}
.menu-item.danger:hover{background:var(--bad-tint);color:var(--bad)}

/* ---------- object page: underlined tab bar over one surface ---------- */
.tabpanel{overflow:visible;padding:0}
.tabbar{display:flex;align-items:center;gap:12px;padding:6px var(--inset) 0;
  border-bottom:1px solid var(--border)}
/* The strip scrolls when it must — the fade says so, instead of leaving a tab
   sliced down the middle where it meets the search field. */
/* The first tab's WORD starts on the inset, like the title above it: a tab's
   own padding used to push it 12px in, so nothing in the header shared an
   edge with the row of tabs under it. The gap carries the spacing instead. */
.tabs{display:flex;gap:24px;flex:1;min-width:0;overflow-x:auto;scrollbar-width:none}
/* …only while more tabs wait to the right (`more`, set in `public/app.js`).
   Always on, it greyed out the last tab even when the strip was scrolled to it. */
.tabs.more{mask-image:linear-gradient(to right,#000 calc(100% - 22px),transparent);
  -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 22px),transparent)}
.tabs::-webkit-scrollbar{display:none}
.tab{position:relative;background:none;border:0;cursor:pointer;font-family:inherit;
  font-size:var(--fs-lead);font-weight:var(--fw-semibold);color:var(--muted);padding:14px 0 15px;white-space:nowrap;
  display:flex;align-items:center;gap:7px;border-bottom:2px solid transparent;margin-bottom:-1px}
.tab:hover{color:var(--ink-2)}
.tab.on{color:var(--accent-strong);border-bottom-color:var(--accent)}
.tab-count{font-family:var(--font-mono);font-size:var(--fs-label);font-weight:var(--fw-semibold);
  background:var(--surface-2);color:var(--muted);border-radius:999px;padding:1px 7px}
.tab.on .tab-count{background:var(--accent-soft);color:var(--accent-strong)}

.tabsearch{position:relative;flex-shrink:0;display:flex;align-items:center}
.tabsearch-icon{position:absolute;left:12px;color:var(--faint);font-size:var(--fs-lead);pointer-events:none}
.tabsearch input{width:210px;padding:0 12px 0 32px;border-radius:var(--r-input);
  border:1px solid var(--border-strong);background:var(--surface-2);font-size:var(--fs-small);
  transition:width .15s ease}
.tabsearch input:focus{width:250px}
.tabsearch input:focus{background:var(--surface)}

.tab-meta{padding:var(--gap-section) var(--inset) 0;font-size:var(--fs-small);color:var(--muted);font-weight:var(--fw-medium)}
/* What a tab is about, in a sentence — above its list, never inside the
   list's own box, where it read as a row of the table. */
.tab-lead{max-width:var(--measure);font-size:var(--fs-small);color:var(--muted);
  margin-bottom:var(--gap-section)}
.tab-body{padding:12px 0 0}
/* Inside the tab surface a list is a SECTION of it, not a card again: a rule
   above, no box, flush to the surface's edges — so its first column starts on
   the same inset as the property's name. A box here put every table 20px
   further in than the header it belongs to (the box-in-a-box of §5.30). */
.tab-body > .panel{box-shadow:none;border:0;border-top:1px solid var(--border);margin-bottom:0}
/* Anything in a tab that is not such a section keeps the inset itself. */
.tab-body > :not(.panel){margin-left:var(--inset);margin-right:var(--inset)}
.tab-body > :not(.panel):last-child{margin-bottom:var(--inset)}
.tab-body > .frow{margin-top:var(--gap-section);margin-bottom:var(--gap-section)}
.tab-body .tbl-wrap{margin:0}
.tab-body table thead th{font-size:var(--fs-caption);color:var(--muted);font-weight:var(--fw-semibold)}
.tab-body table tbody tr:hover{background:var(--surface-2)}

/* ---------- Finanzen: filters, chart, breakdowns ---------- */
.filterline{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:6px 0}
.filterlabel{font-size:var(--fs-label);font-weight:var(--fw-bold);letter-spacing:.07em;text-transform:uppercase;
  color:var(--faint);flex-shrink:0}
.chips{display:flex;gap:6px;flex-wrap:wrap}
.chip{background:var(--surface-2);border:1px solid transparent;border-radius:999px;
  padding:5px 13px;font-size:var(--fs-small);font-weight:var(--fw-semibold);color:var(--muted);cursor:pointer;
  font-family:inherit}
.chip:hover{background:var(--border);color:var(--ink-2)}
.chip.on{background:var(--accent-soft);border-color:rgba(79,138,104,.4);color:var(--accent-strong)}

.chart-legend{display:flex;align-items:center;gap:16px;font-size:var(--fs-caption);color:var(--muted);
  margin-bottom:10px}
.chart-legend span{display:flex;align-items:center;gap:6px}
.chart-legend .sw{width:10px;height:10px;border-radius:var(--r-mark);display:inline-block}
.sw.in{background:var(--c-in)}.sw.out{background:var(--c-out)}
.sw.res{background:var(--c-res);border-radius:50%}
.pnl-chart{width:100%;height:250px;display:block}
.pnl-chart .bar-in{fill:var(--c-in)}
.pnl-chart .bar-out{fill:var(--c-out)}
.pnl-chart .axis{stroke:var(--border-strong);stroke-width:.15}
.pnl-chart .resline{fill:none;stroke:var(--c-res);stroke-width:.35;
  vector-effect:non-scaling-stroke;stroke-linejoin:round}
.pnl-chart .dot{fill:var(--c-res)}
.pnl-chart .dot.neg{fill:var(--bad)}
.chart-wrap{min-width:0}
.xlabs{display:grid;grid-template-columns:repeat(var(--cols),minmax(0,1fr));gap:0;margin-top:6px}
.xlab{font-size:var(--fs-axis);color:var(--faint);font-family:var(--font-mono);text-align:center;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pnl-chart rect:hover{opacity:.75}

.pnl-tables{display:grid;grid-template-columns:1fr 1fr;gap:16px;align-items:start}
@media(max-width:1100px){.pnl-tables{grid-template-columns:1fr}}
.share{display:inline-block;width:52px;height:5px;border-radius:var(--r-mark);background:var(--surface-2);
  vertical-align:middle;margin-right:6px;overflow:hidden}
.share i{display:block;height:100%;background:var(--c-out);border-radius:var(--r-mark)}

/* ================= navigation: dark rail, real icons, mobile drawer ======= */
/* The rail is the deep navy — the colour of trust carries the frame of every
   screen — and the selected item is the brand green on it, white on top. */
:root{ --nav-bg:var(--brand-navy); --nav-dim:#b7c4d4; --nav-ink:#ffffff; --nav-hover:#22406a;
  --nav-label:#8fa2bb; }

/* The SAME box in both states. It used to pad 6/8/16 when idle and 8 all
   round when active, so selecting it changed the element's height and every
   nav item below it moved. The space beneath belongs to the group, not to
   this one element, so it is a margin. */
.sidebar .whoami{color:var(--nav-dim);padding:8px;margin-bottom:8px;
  border-radius:var(--r-btn)}
.sidebar .whoami b{color:var(--nav-ink)}
.sidebar .whoami::before{background:rgba(255,255,255,.08);color:var(--nav-ink)}
/* Where the panel is the way to the Übersicht it also has to SHOW that it is
   the current one — the same green pill the nav item it replaced used, or the
   screen you are on stops being marked anywhere. Its own padding stays: the
   panel is taller than a nav row and shrinking it to match would move the
   name. */
/* The green fill carries white (`--on-fill`). Only the colours change here;
   the box does not. */
.sidebar .whoami.active{background:var(--accent-fill)}
.sidebar .whoami.active,.sidebar .whoami.active b{color:var(--on-fill)}
.sidebar .whoami.active::before{background:rgba(255,255,255,.18);color:var(--on-fill)}
.sidebar .whoami.active .whoami-caret{color:rgba(255,255,255,.8)}
.nav-section{color:var(--nav-label);padding:16px 12px 6px}
.nav-item{color:var(--nav-dim);gap:12px;padding:10px 12px;border-radius:var(--r-btn);font-size:var(--fs-body);line-height:21px;font-weight:var(--fw-medium)}
.nav-item:hover{background:var(--nav-hover);color:var(--nav-ink)}
.nav-item.active{background:var(--accent-fill);color:var(--on-fill);font-weight:var(--fw-semibold)}
.nav-ico{width:20px;height:20px;flex-shrink:0;display:flex;align-items:center;justify-content:center;
  color:inherit}
.nav-item.active .nav-ico,.nav-item:hover .nav-ico{color:inherit}
.nav-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ico{width:20px;height:20px;display:block}
.nav-item .badge{background:rgba(255,255,255,.12);color:var(--nav-ink)}
.nav-item .badge.blue{background:rgba(221,234,243,.2);color:var(--brand-blue)}
.nav-item.active .badge{background:rgba(255,255,255,.22);color:var(--on-fill)}
.sidebar-foot{color:var(--nav-label)}
.sidebar .logout{background:transparent;border:1px solid rgba(255,255,255,.14);color:var(--nav-dim)}
.sidebar .logout:hover{background:var(--nav-hover);color:var(--nav-ink)}

/* On a phone, a HEADER: a bar across the top with the product's name, and
   the menu button as a plain icon inside it — not a floating card with its
   own border and shadow. Desktop never sees either. */
/* Present for a screen reader, absent on the glass. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0,0,0,0);white-space:nowrap;border:0}
.mobilebar{display:none}
.mobilebar-brand{font-size:var(--fs-lead);font-weight:var(--fw-bold);color:var(--heading);
  background:none;border:0;padding:0;cursor:pointer}
/* the drawer trigger — desktop never sees it */
.navtoggle{display:none;position:fixed;top:14px;left:14px;z-index:45;width:42px;height:42px;
  align-items:center;justify-content:center;border-radius:var(--r-btn);border:1px solid var(--border);
  background:var(--surface);box-shadow:var(--shadow-1);color:var(--ink-2);cursor:pointer}
.navscrim{display:none;position:fixed;inset:0;background:rgba(24,49,83,.42);z-index:35}

/* ============================ responsive ================================= */
@media(max-width:1000px){
  .navtoggle{display:flex;top:6px;left:6px;width:40px;height:40px;border:0;
    background:transparent;box-shadow:none}
  .mobilebar{display:flex;align-items:center;position:fixed;top:0;left:0;right:0;height:52px;
    padding-left:56px;background:var(--bg);border-bottom:1px solid var(--border);z-index:30}
  /* Open, it becomes the close button — and moves off the drawer so it stops
     covering the profile row underneath it; on the scrim it needs a surface. */
  #app.nav-open .navtoggle{left:auto;right:8px;background:var(--surface)}
  .sidebar{transform:translateX(calc(-100% - 16px))}
  #app.nav-open .sidebar{transform:none;box-shadow:var(--shadow-2)}
  #app.nav-open .navscrim{display:block}
  .main{margin-left:0;padding:68px 16px 24px;max-width:100%}   /* 52px bar + 16 */
  .page-head.with-actions{flex-direction:column;align-items:stretch;gap:10px}
  /* SCOPED TO THE PAGE HEAD. Unscoped, these also hit `.objhead`'s actions —
     which stays a flex ROW until 620px — so the button block claimed the
     whole width, squeezed `.objhead-id` (min-width:0) to a column one word
     wide, and stretched „Bearbeiten“ across the title it was sitting next
     to. A rule that assumes its parent has stacked must name that parent. */
  .page-head.with-actions .head-actions{width:100%}
  /* Content-width: two half-screen slabs read as navigation tiles, not as
     actions, and gave them more weight than anything they sit above. */
  .head-actions{justify-content:flex-start}
  .tabbar{flex-direction:column;align-items:stretch;gap:0;padding:4px var(--inset) 0}
  .tabsearch{padding:0 0 10px}
  .tabsearch input{width:100%}
  .filterline{align-items:flex-start;flex-direction:column;gap:6px}
  /* label above field — 230px + a 210px input cannot both fit */
  .fld{grid-template-columns:1fr;gap:4px}
  .fld .sub,.fld .unlocks{grid-column:1}
  .fld-in{flex-wrap:wrap}
  .fld-in input,.fld-in select,.fld-in .sugg-wrap{min-width:0;width:100%;flex:1 1 200px}
  .pnl-tables{grid-template-columns:1fr}
  .objgrid{grid-template-columns:1fr}
  .kpis{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
}
@media(max-width:620px){
  .main{padding:64px 16px 20px}   /* the menu glyph's edge: one left line */
  .kpis{grid-template-columns:1fr 1fr}
  .ml-row{grid-template-columns:1fr}
  .ml-row > label.ml-wide{grid-column:auto}
  .frow{flex-wrap:wrap}
  .frow > *{flex:1 1 160px}
  /* The setup form's three fields do not fit in a row here: stacked, each
     label stays on one line and each field is wide enough to type in. */
  .start-form > *,.start-form > .start-field{flex:1 1 100%}
  /* The teaser's button under its text, where the eye already is — not
     pushed to the far edge by the spacer once the row has wrapped. */
  .buybar .spacer{display:none}
  .chips{width:100%}
}
/* Anything that can be wider than the viewport scrolls inside itself, never
   by taking the page with it. */
.tbl-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;max-width:100%}
.panel,.kpi,.tab-body,.onb-main{min-width:0}
table{min-width:max-content}

/* §5.20 — the invitation register fits the screen instead of scrolling
   sideways. It opts out of the rule above: the note is the one column that
   wraps, the figures and the status keep to one line, and a row's actions are
   „Link kopieren“ plus a ⋯ menu. No `.tbl-wrap`, and the panel does not clip,
   so the menu can hang below the last row. */
/* The Aufgaben list fits the screen too (v1.201): the task wraps (its
   Vorgang a grey line under it), the short columns keep to one line, and a
   narrow panel shows each task as a block — asked of the panel, as above. */
.task-panel{container-type:inline-size}
.task-table{min-width:0;width:100%}
.task-table td,.task-table th{white-space:nowrap}
.task-table td.task-text{white-space:normal;overflow-wrap:anywhere;min-width:12em;width:100%}
.task-sub{display:block;margin-top:2px;color:var(--muted);font-size:var(--fs-caption)}
@container (max-width:640px){
  .task-table thead{display:none}
  .task-table,.task-table tbody{display:block}
  .task-table tr{display:flex;flex-wrap:wrap;align-items:center;gap:6px 14px;
    padding:12px var(--inset);border-bottom:1px solid var(--surface-2)}
  .task-table tr:hover td{background:none}
  /* `.panel td:first-child` gives the outer cells the inset; the row carries it now. */
  .panel .task-table td{display:block;padding:0;border:0}
  .task-table td.task-text{flex:1 0 100%;min-width:0;font-weight:var(--fw-medium)}
  .task-table td:last-child{margin-left:auto}
}
.inv-panel{overflow:visible;container-type:inline-size}
.inv-table{min-width:0;width:100%}
.inv-table td,.inv-table th{white-space:nowrap}
.inv-table td.inv-note{white-space:normal;min-width:10em}
.inv-table td.inv-actions{text-align:right}
.inv-actions .menu-wrap{margin-left:6px;vertical-align:middle}
/* Where the columns no longer fit, an invitation is a block: the note as its
   title, then each figure under its own label, then the actions. Asked of the
   PANEL, not the window — beside the sidebar a 1050px window leaves less room
   than a 900px one without it. The header row would only describe columns
   that are no longer there. */
@container (max-width:820px){
  .inv-table thead{display:none}
  .inv-table,.inv-table tbody{display:block}
  .inv-table tr{display:grid;grid-template-columns:repeat(auto-fill,minmax(96px,1fr));gap:10px 16px;
    padding:14px 16px;border-bottom:1px solid var(--border)}
  .inv-table tr:last-child{border-bottom:0}
  /* A row-hover tint would paint each cell and leave the gaps between them. */
  .inv-table tr:hover td{background:none}
  .inv-table td{display:block;padding:0;border:0;white-space:normal}
  .inv-table td[data-label]::before{content:attr(data-label);display:block;
    font-family:var(--font-sans);font-size:var(--fs-caption);font-weight:var(--fw-semibold);
    color:var(--muted);margin-bottom:2px}
  .inv-table td.inv-note{grid-column:1 / -1;font-weight:var(--fw-semibold);min-width:0}
  .inv-table td.inv-note .sub{font-weight:var(--fw-regular)}
  .inv-table td.inv-actions{grid-column:1 / -1;text-align:left}
  .inv-table td.inv-actions:empty{display:none}
  .inv-actions .menu{right:auto;left:0}
}

/* ============ Finanzen: rank the numbers, and let them be read ============ */
/* Figures line up column-by-column. Proportional digits in a financial table
   are the visual equivalent of a wobbly signature. */
.stat-big,.hero-figure,.comp-figure,.openitem b,td.mono,.mono,.ylab,.xlab,.delta{
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1}

.result-row{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);gap:16px;
  align-items:stretch;margin-bottom:16px}
.panel.hero{padding:22px var(--inset);display:flex;flex-direction:column;gap:4px;margin:0}
/* On a page (not inside the P&L grid) the headline panel keeps the same gap to
   the next section as every other panel — it used to butt against it. And it
   reads from the left like everything else, rather than floating centred in a
   field of white. */
.main > .panel.hero{margin-bottom:var(--gap-section);align-items:stretch}
.hero-label{font-size:var(--fs-label);font-weight:var(--fw-semibold);letter-spacing:.09em;text-transform:uppercase;
  color:var(--muted)}
.hero-figure{font-size:var(--fs-figure);font-weight:var(--fw-bold);letter-spacing:-.025em;line-height:1.1;
  color:var(--ink)}
.hero-figure.neg{color:var(--bad)}
/* Room under the chip: the foot's rule used to sit directly on it. */
.hero-delta{min-height:20px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  margin:var(--gap-pair) 0 var(--gap-related)}
.hero-delta:empty{display:none}
/* What came in over the period, beside what went out: a line, not a second
   headline. */
.hero-received{font-size:var(--fs-small);font-weight:var(--fw-semibold);color:var(--ok);
  margin-bottom:var(--gap-related)}
td.amount-in{color:var(--ok);font-weight:var(--fw-semibold)}
.hero-foot{margin-top:auto;padding-top:12px;font-size:var(--fs-caption);color:var(--faint);
  border-top:1px solid var(--border)}
.components{display:grid;grid-template-rows:1fr 1fr;gap:16px;min-width:0}
.panel.comp{padding:14px 18px;margin:0;display:flex;flex-direction:column;gap:3px;
  justify-content:center}
.comp-label{display:flex;align-items:center;gap:8px;font-size:var(--fs-small);color:var(--muted);
  font-weight:var(--fw-semibold)}
.comp-figure{font-size:var(--fs-title);font-weight:var(--fw-bold);letter-spacing:-.015em}

/* align-self keeps the pill the width of its text — inside a column flexbox it
   would otherwise stretch into a full-width band of colour. */
.delta{display:inline-flex;align-self:flex-start;align-items:center;gap:3px;font-size:var(--fs-caption);
  font-weight:var(--fw-bold);border-radius:999px;padding:2px 9px;white-space:nowrap}
.delta.good{background:var(--ok-tint);color:var(--ok)}
.delta.bad{background:var(--bad-tint);color:var(--bad)}
.delta.flat,.delta.none{background:var(--surface-2);color:var(--muted);font-weight:var(--fw-semibold)}
.delta.up::before{content:"▲";font-size:var(--fs-glyph)}
.delta.down::before{content:"▼";font-size:var(--fs-glyph)}
.delta-ref{font-size:var(--fs-caption);color:var(--faint);align-self:center}
.panel.comp{align-items:flex-start}
.comp > .delta-ref{margin-top:-2px}

.openpanel{margin-bottom:16px}
.openhead{display:flex;align-items:baseline;gap:10px;font-weight:var(--fw-bold);font-size:var(--fs-body);
  margin-bottom:10px}
.opengrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:14px}
.openitem{display:flex;flex-direction:column;gap:2px;padding-left:12px;
  border-left:3px solid var(--border)}
.openitem b{font-size:var(--fs-card-title);font-weight:var(--fw-bold)}

/* One quiet line beside the button, on its centre line. */
.asof{flex-shrink:0;font-size:var(--fs-caption);color:var(--muted);white-space:nowrap;
  font-variant-numeric:tabular-nums}
.filtercard{background:transparent;box-shadow:none;border:1px solid var(--border)}

/* the chart gains a scale */
.chart-grid{display:grid;grid-template-columns:auto minmax(0,1fr);gap:10px;align-items:start}
.yaxis{display:flex;flex-direction:column;justify-content:space-between;height:250px;
  text-align:right}
.ylab{font-size:var(--fs-axis);color:var(--faint);font-family:var(--font-mono);line-height:1;
  transform:translateY(-.4em)}
.pnl-chart .grid{stroke:var(--border);stroke-width:.12;vector-effect:non-scaling-stroke}
.pnl-chart .grid.zero{stroke:var(--border-strong);stroke-width:.3}
@media(max-width:620px){
  .result-row{grid-template-columns:1fr}
  .yaxis{display:none}
  .chart-grid{grid-template-columns:1fr}
}

/* ============ data colours: green is for interaction, not for data =========
 * The accent green marks the selected nav item, the primary button and the
 * active tab. Using it for "income" too made "positive" and "selected" the
 * same signal — so the chart gets a restrained pair of its own, and the
 * result line takes the ink colour, because the result is the conclusion
 * rather than a third category. */
:root{
  --c-in:#2f5d8a;        /* steel blue — money in */
  --c-in-tint:#e3edf6;
  --c-out:#c0704f;       /* terracotta — money out */
  --c-out-tint:#f9eae1;
  --c-res:var(--brand-navy); /* navy — the result */
}

/* clicking a month */
.pnl-chart .hit{fill:transparent;cursor:pointer}
.pnl-chart .hit.off{cursor:default;pointer-events:none}
.pnl-chart .hit.top{fill:transparent}
.pnl-chart .hit:hover{fill:rgba(24,49,83,.045)}
.chip.drill{font-family:var(--font-mono);letter-spacing:.02em}

/* nothing outstanding is a result, not a gap */
.openitem.clear{border-left-color:var(--ok)}
.openitem.late{border-left-color:var(--bad)}
.settled{display:flex;align-items:center;gap:7px;color:var(--ok);font-size:var(--fs-lead);font-weight:var(--fw-bold)}
.settled .tick{width:17px;height:17px;flex-shrink:0}

/* ====== object page: breadcrumb over one card that carries the identity ====
 * Modelled on "My lists › …": the path is the page's opening line, the
 * parent stays a link, and the card below opens with a quiet qualifier, the
 * name, and the numbers worth knowing before any tab is chosen. */
.crumbbar{display:flex;align-items:center;gap:8px;margin-bottom:var(--gap-related);font-size:var(--fs-lead)}
.crumb-up{color:var(--muted);text-decoration:none;font-weight:var(--fw-medium);border-radius:var(--r-sm);
  padding:2px 6px;margin-left:-6px;transition:color .12s ease,background .12s ease}
.crumb-up:hover{color:var(--accent-strong);background:var(--accent-soft)}
.crumb-up:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.crumb-sep{width:15px;height:15px;color:var(--faint);flex-shrink:0}
.crumb-here{color:var(--ink);font-weight:var(--fw-bold);min-width:0;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}

.objhead{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;
  padding:var(--inset) var(--inset) 0}
.objhead-id{min-width:0}
.objhead-kicker{font-size:var(--fs-caption);font-weight:var(--fw-semibold);letter-spacing:.05em;color:var(--muted);
  text-transform:uppercase;margin-bottom:var(--gap-pair)}
.objhead-name{font-size:var(--fs-title);font-weight:var(--fw-bold);letter-spacing:-.02em;line-height:1.15;
  color:var(--ink)}
.objhead-path{font-size:var(--fs-caption);color:var(--faint);margin-top:var(--gap-pair)}

.objmetrics{display:flex;flex-wrap:wrap;gap:28px;padding:var(--gap-section) var(--inset) var(--gap-related)}
.metric{display:flex;flex-direction:column;gap:var(--gap-pair);min-width:0}
.metric-label{font-size:var(--fs-label);font-weight:var(--fw-semibold);letter-spacing:.05em;text-transform:uppercase;
  color:var(--faint)}
.metric-value{font-size:var(--fs-card-title);font-weight:var(--fw-bold);color:var(--ink);
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1}
.metric-value.good{color:var(--ok)}
.metric-value.bad{color:var(--bad)}

@media(max-width:620px){
  .objhead{flex-direction:column}
  .objmetrics{gap:16px 22px}
}

/* ============================================ §5.6 the three audiences */
/* The role chooser. It owns the whole viewport because it is the only
   question being asked — a sidebar of destinations here would be answering
   it prematurely. */
.chooser{min-height:100vh;display:flex;flex-direction:column;justify-content:center;
  align-items:center;padding:40px 20px;background:var(--bg)}
.chooser-head{text-align:center;max-width:640px;margin-bottom:34px}
/* §5.20 — the sign-up calculation above the first question, as wide as the
   cards under it. */
.chooser-carried{width:100%;max-width:860px;margin-bottom:34px;overflow:hidden}
.chooser-carried .buypanel{border-top:0}
.chooser-head h1{font-size:var(--fs-page);font-weight:var(--fw-bold);letter-spacing:-0.02em;margin:0 0 10px}
.chooser-head .sub{font-size:var(--fs-lead);color:var(--muted);line-height:1.55}
.chooser-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(232px,1fr));
  gap:16px;width:100%;max-width:860px}
.chooser-card{display:flex;flex-direction:column;align-items:flex-start;gap:7px;
  text-align:left;padding:24px 22px;border:1.5px solid var(--border);border-radius:var(--r-card);
  background:var(--surface);cursor:pointer;font:inherit;color:var(--ink);
  transition:border-color .12s,box-shadow .12s,transform .12s}
.chooser-card:hover{border-color:var(--accent);box-shadow:0 8px 26px rgba(24,49,83,.09);
  transform:translateY(-2px)}
.chooser-ico{width:46px;height:46px;border-radius:var(--r-sm);background:var(--accent-soft);
  color:var(--accent-strong);display:flex;align-items:center;justify-content:center;
  margin-bottom:6px}
.chooser-ico .ico{width:23px;height:23px}
.chooser-card b{font-size:var(--fs-card-title);font-weight:var(--fw-bold);letter-spacing:-0.01em}
.chooser-tag{font-size:var(--fs-small);color:var(--ink-2)}
.chooser-promise{font-size:var(--fs-small);color:var(--muted);line-height:1.5;flex:1}
.chooser-cta{margin-top:10px;font-size:var(--fs-small);font-weight:var(--fw-semibold);color:var(--accent-strong)}
.chooser-cta.done{color:var(--ok)}

/* Offered, not yet built. It must read as "not yet" rather than as broken:
   the card keeps its shape and its promise, loses only the lift and the
   pointer, and carries the one thing that is actionable — being told. */
.chooser-card.soon{cursor:default;background:var(--surface-2);border-style:dashed}
.chooser-card.soon:hover{border-color:var(--border);box-shadow:none;transform:none}
.chooser-card.soon .chooser-ico{background:var(--surface);color:var(--muted)}
.chooser-card.soon b,.chooser-card.soon .chooser-tag{color:var(--ink-2)}
.chooser-soon{font-size:var(--fs-caption);font-weight:var(--fw-semibold);letter-spacing:.04em;text-transform:uppercase;
  color:var(--muted);margin-top:8px}
.chooser-card.soon .btn{margin-top:8px}
.chooser-foot{margin-top:26px}
/* …except inside the app, where it is a page like any other (the start page of
   §5.12b, the Zuhause page with nothing on it). Centred in the viewport it
   began half a screen below where every other page's heading sits, so the
   eye went looking for content above it. It starts where a `.page-head` does. */
.main .chooser{min-height:0;justify-content:flex-start;padding:0;background:none}

/* The switcher, for someone who both rents a home and lets a flat. */
.whoami[role=button]{cursor:pointer;border-radius:var(--r-btn)}
.whoami[role=button]:hover{background:var(--surface-2)}
/* …but inside the sidebar the ground is dark and the name is white, so the
   light hover token painted a white card under white text and the person's
   own name vanished while they were pressing it. The nav items next to it
   already knew this; the header had been left behind. */
.sidebar .whoami[role=button]:hover{background:var(--nav-hover)}
/* The current screen stays marked while the pointer is over it. This has to
   sit AFTER the rule above, not beside the other `.active` declarations: both
   selectors carry the same specificity, so the later one wins, and while it
   lived earlier the green flipped to grey on hover — the highlight appearing
   to blink as the pointer crossed the panel. */
.sidebar .whoami.active:hover{background:var(--accent-fill-hover)}
.whoami-caret{margin-left:auto;color:var(--muted);font-size:var(--fs-label);display:flex;align-items:center}
.persp-menu{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-card);
  padding:6px;margin:-8px 4px 10px;box-shadow:0 10px 30px rgba(24,49,83,.14)}
.persp-menu-head{font-size:var(--fs-label);font-weight:var(--fw-bold);letter-spacing:.09em;text-transform:uppercase;
  color:var(--faint);padding:7px 10px 5px}
.persp-opt{display:block;width:100%;text-align:left;padding:8px 10px;border:0;border-radius:var(--r-sm);
  background:none;cursor:pointer;font:inherit;color:var(--ink-2)}
.persp-opt:hover{background:var(--surface-2)}
.persp-opt.on{background:var(--accent-soft)}
.persp-opt b{display:block;font-size:var(--fs-small);color:var(--ink)}
.persp-opt span{font-size:var(--fs-caption);color:var(--muted)}

/* The one number that answers "how am I doing". */
.hero{display:flex;flex-wrap:wrap;align-items:center;gap:26px;padding:24px 22px}
.hero-main{min-width:200px}
.hero-label{font-size:var(--fs-label);font-weight:var(--fw-semibold);letter-spacing:.07em;text-transform:uppercase;
  color:var(--muted)}
.hero-value{font-size:var(--fs-figure);font-weight:var(--fw-bold);letter-spacing:-0.03em;line-height:1.15;
  margin-top:var(--gap-pair);font-variant-numeric:tabular-nums}
.hero-value.good{color:var(--ok)}
.hero-value.bad{color:var(--bad)}
.hero-value.muted{color:var(--faint)}
/* The empty headline states its state — „Noch nicht berechnet“ — where the
   figure will stand, then says what would calculate it; the action comes
   last and is an ordinary primary button, never a stand-in for the value. */
.hero-empty{font-size:var(--fs-title);font-weight:var(--fw-semibold);color:var(--ink-2);
  line-height:1.2;margin-top:var(--gap-pair)}
.hero-cta{align-self:flex-start;margin-top:10px}
.hero-note{font-size:var(--fs-small);color:var(--muted);margin-top:var(--gap-pair);max-width:var(--measure)}
.hero-figs{margin:0;border:0;padding:0;background:none}

/* Data → insight → CTA. One row, one decision. */
/* ROWS, separated by a rule — not bordered cards inside the panel's border.
   Tone is a 3px mark at the row's start, only where it means something
   (something wrong, something due); an ordinary insight has none. */
.feed{display:flex;flex-direction:column;gap:0}

/* A list whose rows each OPEN something (§5.30): the row is the control. It
   has the panel's own inset (18px, the same line the panel headings start
   on), a title over a quieter line, the status small at the end, and a
   chevron that says "this goes somewhere" — plus the hover a control has. */
.linkrow{display:flex;align-items:center;gap:12px;padding:14px var(--inset);
  border-top:1px solid var(--border);cursor:pointer}
.linklist > .linkrow:first-child{border-top:0}

/* A list page's control row: its tabs on the left, its one primary action on
   the right. Tabs are FILTERS, so they look like tabs — quiet text, the
   selected one underlined in the accent — never like buttons (§5.30). */
.listbar{display:flex;align-items:flex-end;justify-content:space-between;gap:12px;
  flex-wrap:wrap;margin-bottom:var(--gap-related);border-bottom:1px solid var(--border)}
.listbar .head-actions{padding-bottom:8px}
/* No tabs, no rule to underline them with. */
.listbar.plain{border-bottom:0}
.segs{display:flex;gap:4px}
.seg{-webkit-appearance:none;appearance:none;background:none;border:0;cursor:pointer;
  padding:8px 10px;margin-bottom:-1px;border-bottom:2px solid transparent;
  font-size:var(--fs-body);font-weight:var(--fw-medium);color:var(--muted)}
.seg:hover{color:var(--ink)}
.seg.on{color:var(--ink);border-bottom-color:var(--accent-fill)}
.seg-n{color:var(--faint);font-weight:var(--fw-regular);font-variant-numeric:tabular-nums}

/* An empty list is a sentence and a way out (§5.12), not an empty table: a
   heading that names the absence, one line of what goes here, the action. */
.empty-state{padding:28px var(--inset);text-align:center}
.empty-title{font-size:var(--fs-lead);font-weight:var(--fw-bold)}
.empty-lead{color:var(--muted);max-width:var(--measure);margin:var(--gap-pair) auto var(--gap-section)}

/* A text button inside a sentence: no control height, no padding box. */
.btn.tertiary.inline{min-height:0;padding:0;margin-left:4px;vertical-align:baseline}
.btn.tertiary.inline:hover{background:none;text-decoration:underline}
.linkrow:hover{background:var(--surface-2)}
.linkrow:focus-visible{outline:2px solid var(--accent-mid);outline-offset:-2px}
.linkrow-main{flex:1;min-width:0}
.linkrow-title{font-size:var(--fs-lead);font-weight:var(--fw-semibold);color:var(--ink);line-height:1.35}
.linkrow-meta{font-size:var(--fs-small);color:var(--muted);margin-top:2px}
.linkrow-chev{color:var(--faint);font-size:var(--fs-card-title);line-height:1;flex-shrink:0}
/* The first line of the person's own note under a contact — theirs, so ink,
   not grey; one line, cut with an ellipsis. */
.linkrow-note{font-size:var(--fs-small);color:var(--ink-2);margin-top:4px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.linkrow-title .pill{margin-left:6px;vertical-align:1px}
/* §5.32 — the comment field: several lines, grows by hand, never a box that
   hides what was typed. */
/* §5.32 — a saved contact's trades, ticked in its edit form: a contact may
   hold several, so a select would drop all but one. */
.ct-trades{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));
  column-gap:var(--gap-related);margin:10px 0 0}
.ct-trades-h{grid-column:1 / -1;font-size:var(--fs-small);color:var(--ink-2)}
.ct-trades .keepdoc{margin:4px 0}
/* Kontakte's trade chips (§5.32): Kosten's period chips, over the list. */
.ct-chips{margin-bottom:var(--gap-related)}
@media (max-width:620px){.ct-trades{grid-template-columns:1fr 1fr}}
.ct-notes,.fb-field{display:block;margin:12px 0 6px}
.ct-notes textarea,.fb-field textarea{display:block;width:100%;min-height:120px;resize:vertical;margin-top:4px;
  padding:8px 11px;line-height:var(--lh-body)}
/* §5.32a — Kontakte aus Dokumenten übernehmen. Two ways out of an empty
   Kontakte page, side by side. */
.empty-actions{display:flex;gap:var(--gap-related);justify-content:center;flex-wrap:wrap}
/* Where files are chosen or dropped: the panel itself, outlined while a file
   is over it. */
.ci-pick.dragover{outline:2px dashed var(--accent);outline-offset:-6px}
.ci-pick-row{display:flex;align-items:center;gap:var(--gap-related);flex-wrap:wrap;margin-top:var(--gap-related)}
.ci-head{font-size:var(--fs-lead);font-weight:var(--fw-bold);margin:var(--gap-section) 0 var(--gap-related)}
.ci-lead{max-width:var(--measure);margin:0 0 var(--gap-related)}
/* A proposal: the tick, then the two things worth correcting, then how to
   reach it — each value with its own way out. */
.ci-row{align-items:flex-start}
.ci-row > input[type=checkbox]{margin-top:30px;flex-shrink:0}
.ci-fields{margin:0}
.ci-name{flex:1 1 240px}
.ci-trade{flex:0 1 220px}
.ci-reach{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0 4px}
.ci-val{display:inline-flex;align-items:center;gap:2px;padding:2px 4px 2px 8px;
  background:var(--surface-2);border-radius:var(--r-sm);font-size:var(--fs-small);color:var(--ink-2)}
.ci-x{-webkit-appearance:none;appearance:none;background:none;border:0;cursor:pointer;
  padding:0 4px;color:var(--faint);font-size:var(--fs-body);line-height:1;border-radius:var(--r-sm)}
.ci-x:hover{background:var(--bad-tint);color:var(--bad)}
.ci-same .linkrow-main{opacity:.6}
.ci-bad{color:var(--bad)}
.ci-actions{margin-top:var(--gap-section)}
/* A row that carries its own actions is not itself a control: no pointer, no
   hover — the actions at its end are what you press. */
.linkrow.static{cursor:default}
.linkrow.static:hover{background:none}
a.btn.tertiary{text-decoration:none}
/* A destructive text action: the red family's ink, the same quiet weight. */
.btn.tertiary.danger{color:var(--bad)}
.btn.tertiary.danger:hover{background:var(--bad-tint)}

/* §5.31 — "keep this document?" beside every upload. A plain checkbox and its
   sentence; ticked is the default. */
.keepdoc{display:inline-flex;align-items:center;gap:8px;font-size:var(--fs-caption);
  font-weight:var(--fw-regular);color:var(--ink-2);cursor:pointer;margin:var(--gap-related) 0}
.keepdoc input{width:16px;height:16px;accent-color:var(--accent-fill);margin:0}

/* §5.34 — the investment exposé: the form that makes one, and the page that
   holds its preview and links. Nothing here decides a size of its own. */
.ex-choose{display:flex;flex-wrap:wrap;gap:8px 18px;margin:4px 0 8px}
.ex-check{display:inline-flex;align-items:center;gap:8px;cursor:pointer;color:var(--ink-2)}
.ex-check input{width:16px;height:16px;accent-color:var(--accent-fill);margin:0}
.frow label.ex-w{flex:1 1 200px}
.frow label.ex-grow{flex:1 1 320px}
label.ex-block{display:flex;flex-direction:column;gap:3px;margin:10px 0}
label.ex-block textarea,.frow label.ex-grow textarea{width:100%}
.ex-note{align-items:flex-start}
.ex-photos{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:12px;margin:8px 0 12px}
.ex-photo{display:flex;flex-direction:column;gap:6px}
.ex-photo img{display:block;width:100%;aspect-ratio:4/3;object-fit:cover;background:var(--surface-2);
  border:1px solid var(--border);border-radius:var(--r-md)}
.ex-photo input:not([type=checkbox]){width:100%}
.ex-upload{cursor:pointer}
.ex-submit{display:flex;justify-content:flex-end;align-items:center;gap:12px;margin:8px 0 24px}
.ex-preview iframe{display:block;width:100%;height:78vh;border:0;background:var(--bg)}
.ex-foot{margin:8px 0 24px}
.ex-linkrow{cursor:default}
.ex-linkrow:hover{background:none}
.feedcard{display:flex;align-items:center;gap:16px;padding:14px 0;border-top:1px solid var(--border)}
.feed > .feedcard:first-child{border-top:0;padding-top:4px}
.feedcard.t-bad,.feedcard.t-warn{padding-left:12px}
.feedcard.t-bad{box-shadow:inset 3px 0 0 var(--bad)}
.feedcard.t-warn{box-shadow:inset 3px 0 0 var(--warn)}
.feedcard-body{flex:1;min-width:0}
.feedcard-body b{font-size:var(--fs-body);display:block;margin-bottom:var(--gap-pair)}
.feedcard-body .sub{font-size:var(--fs-small);line-height:1.5;max-width:var(--measure)}
.feedcard .btn{flex-shrink:0;white-space:nowrap}

/* The breakdown: a share is easier to read as a bar than as a percentage. */
.bar-row{display:flex;align-items:center;gap:12px;padding:7px 0}
.bar-label{width:190px;flex-shrink:0;font-size:var(--fs-small)}
.bar-track{flex:1;height:8px;border-radius:999px;background:var(--surface-2);overflow:hidden}
.bar-fill{display:block;height:100%;background:var(--accent-mid);border-radius:999px}
.bar-value{width:130px;text-align:right;font-size:var(--fs-small);font-variant-numeric:tabular-nums}
.bar-value i{color:var(--muted);font-style:normal;margin-left:8px;font-size:var(--fs-caption)}
.dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:9px;
  vertical-align:middle;background:var(--faint)}
.dot.d-bad{background:var(--bad)}
.dot.d-warn{background:var(--warn)}
.dot.d-info{background:var(--muted)}
@media (max-width:720px){
  .bar-label{width:110px}
  .bar-value{width:96px}
  .feedcard{flex-direction:column;align-items:flex-start;gap:10px}
  /* The action sits under the text it belongs to, starting where it starts —
     a text button stretched or centred on a line of its own reads as a
     separate section. */
  .feedcard .btn{align-self:flex-start}
}

/* The stage's own sections, one level below the three-stage promise. */
/* Under the stage tabs, whose hairline already separates them. */
.substeps{display:flex;flex-wrap:wrap;gap:6px;margin-top:var(--gap-related)}
.substep{padding:5px 11px;border-radius:999px;border:1px solid var(--border);
  background:var(--surface);cursor:pointer;font:inherit;font-size:var(--fs-small);color:var(--ink-2)}
.substep:hover{border-color:var(--accent)}
.substep.cur{background:var(--accent-soft);border-color:var(--accent);color:var(--ink);font-weight:var(--fw-semibold)}
.substep.done{color:var(--muted)}
.substep i{font-style:normal;color:var(--faint);margin-left:6px;font-size:var(--fs-label)}
.linkish{font-size:var(--fs-small);color:var(--muted);text-decoration:underline;
  text-underline-offset:3px;cursor:pointer}
.linkish:hover{color:var(--accent-strong)}

/* Three yields side by side, each with the sentence that defines it. */
/* Three abreast wherever three fit — the point of the panel is the descent
   from one figure to the next, and a wrapped column breaks the reading. */
.rendite{display:grid;grid-template-columns:repeat(auto-fit,minmax(165px,1fr));gap:18px}
.rendite-col{border-left:2px solid var(--border);padding-left:14px}
.rendite-col:first-child{border-left-color:var(--accent)}
.rendite-label{font-size:var(--fs-label);font-weight:var(--fw-bold);letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted)}
.rendite-value{font-size:var(--fs-page);font-weight:var(--fw-bold);letter-spacing:-0.02em;margin:3px 0 6px;
  font-variant-numeric:tabular-nums}
.rendite-value.bad{color:var(--bad)}
.rendite-note{font-size:var(--fs-caption);color:var(--muted);line-height:1.5}

/* ============================================ §5.7 the quick-entry dialog */
.modal.qe{max-width:560px}
.qe-doors{display:flex;flex-direction:column;gap:14px;margin-top:16px}
.qe-door{display:flex;flex-direction:column;align-items:flex-start;gap:5px;text-align:left;
  padding:18px 18px;border:1.5px dashed var(--border);border-radius:var(--r-card);background:var(--surface);
  cursor:pointer;font:inherit;color:var(--ink);transition:border-color .12s,background .12s}
.qe-door:hover{border-color:var(--accent);background:var(--accent-soft)}
.qe-door b{font-size:var(--fs-lead)}
.qe-door .sub{margin:0;font-size:var(--fs-caption);line-height:1.5}
.qe-ico{width:34px;height:34px;border-radius:var(--r-sm);background:var(--accent-soft);
  color:var(--accent-strong);display:flex;align-items:center;justify-content:center}
.qe-ico .ico{width:18px;height:18px}
.qe-or{text-align:center;font-size:var(--fs-caption);color:var(--faint);text-transform:uppercase;
  letter-spacing:.08em}
.qe-manual{display:flex;gap:10px}
.qe-manual .btn{flex:1;justify-content:center;font-size:var(--fs-body);padding:12px 10px;font-weight:var(--fw-semibold)}
/* Outlined, not filled: `.btn` on its own is solid accent, and a green
   "− Ausgabe" is both unreadable and the wrong colour for the thing. */
.btn.qe-out{background:transparent;border:1.5px solid var(--bad);color:var(--bad)}
.btn.qe-out:hover{background:var(--bad-tint)}
.btn.qe-in{background:transparent;border:1.5px solid var(--ok);color:var(--ok)}
.btn.qe-in:hover{background:var(--ok-tint)}
.qe-progress{height:4px;border-radius:999px;background:var(--surface-2);overflow:hidden;
  margin-top:14px}
.qe-progress span{display:block;height:100%;width:35%;background:var(--accent-mid);
  border-radius:999px;animation:qeslide 1.1s ease-in-out infinite}
@keyframes qeslide{0%{margin-left:-35%}100%{margin-left:100%}}
.qe-table{width:100%;border-collapse:collapse;margin-top:14px;font-size:var(--fs-small)}
.qe-table th{text-align:left;font-size:var(--fs-caption);color:var(--muted);padding:0 8px 6px 0;
  font-weight:var(--fw-semibold)}
.qe-table td{padding:7px 8px 7px 0;border-top:1px solid var(--border)}
.qe-sign{display:inline-flex;width:20px;height:20px;border-radius:var(--r-sm);align-items:center;
  justify-content:center;font-weight:var(--fw-bold);font-size:var(--fs-small)}
.qe-sign.in{background:var(--ok-tint);color:var(--ok)}
.qe-sign.out{background:var(--bad-tint);color:var(--bad)}
.hero-chips{margin-top:var(--gap-related)}
.hero-chips .chip{font-size:var(--fs-caption);padding:4px 11px}
/* Ordinary setup content, not a selected or successful thing: the neutral
   panel every section has, a heading that names the task (not the page's
   size), the home it is about underneath, and a line and a form that stop
   at a readable width. */
.startcard .body{padding:var(--inset)}
.start-title{font-size:var(--fs-lead);font-weight:var(--fw-bold);line-height:1.3}
.start-where{font-size:var(--fs-small);color:var(--muted);margin-top:var(--gap-pair)}
.start-lead{max-width:var(--measure);color:var(--ink-2);margin:var(--gap-related) 0 var(--gap-section)}
/* The button stands on the fields' line, not on the row's: a message under a
   field (`.frow:has(.field-msg)` top-aligns the row) must not lift it to the
   labels. So the row is always top-aligned and the button is pushed down by
   exactly a label's height. */
.start-form{max-width:760px;margin:0;align-items:flex-start}
.start-form > .btn{margin-top:calc(var(--fs-caption) * 1.5 + 3px)}
.start-field{flex:1 1 0;min-width:0}
.persp-opt.persp-new{border-top:1px solid var(--border);margin-top:4px;padding-top:9px}
.persp-opt.persp-new b{color:var(--accent-strong)}


/* ---- Ausgabenverlauf: twelve columns, one per month ------------------ */
/* Beyond about a year the bars need a floor rather than a share of the
   width, so the chart scrolls inside its own box and the page never does. */
/* One line, always present, so choosing an object cannot resize the card
   under the chips that were just clicked (§5.6a). */
.filternote{margin-top:8px}

.spendscroll{overflow-x:auto;overflow-y:hidden;margin:0 -2px;padding:0 2px;
  /* The bar is drawn by the OS on a trackpad and vanishes at rest, so it is
     not the affordance — the fade below is. */
  scrollbar-width:thin}
/* The wrapper exists only to hang the two edge affordances on: a fade that
   says the chart continues, and an arrow to follow it. Both are painted only
   while `can-l`/`can-r` say there is something on that side (wireScroll in
   app.js), because an arrow pointing at nothing is a promise. */
.scrollnote{position:relative;--fade-l:0px;--fade-r:0px;--fade-x-l:0px;--fade-x-r:0px}
/* The fade ends where the clipped bar ends — in the gap after it, never
   part way across a bar. A FIXED width cannot do that: the strip cut off at
   an edge is any width between nothing and a whole column, so a constant
   fade either leaves a stub of it solid or runs on over the neighbour and
   paints a gradient down the middle of a bar that is fully visible. Both
   read as a rendering fault rather than as a chart continuing. `wireScroll`
   measures the two edges on every scroll, so a bar is only ever entirely
   faded or entirely solid. */
.scrollnote::before,.scrollnote::after{content:"";position:absolute;top:0;bottom:0;
  pointer-events:none;opacity:0;transition:opacity .15s;z-index:1}
/* Anchored to the SCROLLER's edges, not the wrapper's. `.spendscroll` carries
   `margin:0 -2px` so its own padding does not shift the bars, which puts its
   left edge two pixels outside this box — and a fade pinned at `left:0` left
   exactly that much of the clipped bar standing there un-faded: a hairline of
   solid colour at each end. Measured rather than written as -2px, because the
   offset belongs to that rule and not to this one. */
.scrollnote::before{left:var(--fade-x-l);width:var(--fade-l);
  background:linear-gradient(to right,var(--surface),transparent)}
.scrollnote::after{right:var(--fade-x-r);width:var(--fade-r);
  background:linear-gradient(to left,var(--surface),transparent)}
.scrollnote.can-l::before,.scrollnote.can-r::after{opacity:1}
.scrollarrow{position:absolute;top:50%;transform:translateY(-50%);z-index:2;
  width:26px;height:26px;border-radius:50%;border:1px solid var(--border);
  background:var(--surface);color:var(--ink);font-size:var(--fs-lead);line-height:1;
  display:none;align-items:center;justify-content:center;cursor:pointer;
  box-shadow:0 1px 4px rgba(0,0,0,.12);padding:0}
.scrollarrow:hover{background:var(--accent-fill);color:var(--on-fill);border-color:transparent}
/* A fixed inset, not a share of the fade — that width now changes as you
   scroll, and an arrow sliding about under the cursor is its own problem.
   It carries its own ground and shadow, so it reads on a solid bar too. */
.scrollarrow.l{left:6px}
.scrollarrow.r{right:6px}
.scrollnote.can-l .scrollarrow.l,.scrollnote.can-r .scrollarrow.r{display:flex}
.spendchart{display:flex;align-items:flex-end;gap:6px;height:132px;padding:6px 0 0}
.spendchart.wide{min-width:max-content}
/* Wide enough for `2026-01` — an ellipsised axis is not an axis. */
.spendchart.wide .spendcol{flex:0 0 54px}
.spendcol{flex:1;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;
  gap:6px;height:100%;min-width:0}
.spendcol .sbar{width:100%;border-radius:var(--r-mark) var(--r-mark) 0 0;background:var(--accent-soft);
  transition:background .12s;min-height:2px;
  /* column-reverse so the first category sits at the BASE of the stack, which
     is where a reader looks for the biggest one. */
  display:flex;flex-direction:column-reverse;overflow:hidden}
/* A month's bars side by side on one baseline: its costs, and — where the
   period has any — what came in. The percentages on the bars are of THIS
   row's height, which is the column less its label. */
.spendcol .sbars{flex:1 1 auto;min-height:0;width:100%;display:flex;align-items:flex-end;gap:3px}
.spendcol .sbars .sbar{flex:1 1 0;min-width:0;width:auto}
/* What came in is a bar like the costs beside it: the SAME width, the same
   corners, one flat colour of its own. Navy, because it is the one chart
   colour no cost category can take (those are --c0 to --c4), so „Erhalten“
   never looks like a sixth kind of cost. It was a slim outlined sliver, which
   read as a stray mark at the chart's edge rather than as a figure. */
.spendcol .sbar-in{flex:1 1 0;min-width:0;min-height:2px;background:var(--brand-navy);
  border-radius:var(--r-mark) var(--r-mark) 0 0}
/* A month with nothing received keeps the slot, empty — every month is the
   same pair of places, so the cost bars stay one width and one rhythm. */
.spendcol .sbar-in.none{visibility:hidden;min-height:0}
/* Two bars to a month: the months stand further apart than the pair does. */
.spendchart.pairs{gap:14px}
.chart-legend .sw.rcv{background:var(--brand-navy)}
.spendcol.now .sbar{background:var(--accent-mid)}
.spendcol.zero .sbar{background:var(--surface-2);min-height:2px}
/* A segment's share of its month, by flex-grow on the cents themselves. */
.sseg{display:block;width:100%;min-height:0}
.sseg.c0{background:var(--c0)} .sseg.c1{background:var(--c1)}
.sseg.c2{background:var(--c2)} .sseg.c3{background:var(--c3)}
.sseg.c4{background:var(--c4)}
/* A stacked bar is unreadable without one, so the legend is not optional
   decoration — it appears exactly when there is more than one series. */
.chart-legend{display:flex;flex-wrap:wrap;gap:14px;margin:var(--gap-related) 0 var(--gap-pair)}
.chart-legend .lg{display:inline-flex;align-items:center;gap:6px;font-size:var(--fs-caption);
  color:var(--muted)}
.chart-legend .sw{width:10px;height:10px;border-radius:var(--r-mark);flex-shrink:0}
.chart-legend .sw.c0{background:var(--c0)} .chart-legend .sw.c1{background:var(--c1)}
.chart-legend .sw.c2{background:var(--c2)} .chart-legend .sw.c3{background:var(--c3)}
.chart-legend .sw.c4{background:var(--c4)}
/* Fixed height and never wrapping: the bars share a baseline only because
   every label below them occupies exactly one line. A label that grows a
   second line pushes its own bar up and the chart silently stops comparing. */
.spendcol .sm{font-size:var(--fs-axis);color:var(--faint);font-family:var(--font-mono);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;
  flex:0 0 auto;line-height:1.3;height:1.3em}
.spendcol:hover .sbar{background:var(--accent-mid)}
.chart-head{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:var(--gap-pair)}
.chart-head .big{font-size:var(--fs-title);font-weight:var(--fw-bold);letter-spacing:-0.02em}
.trend{font-size:var(--fs-caption);font-weight:var(--fw-semibold);border-radius:999px;padding:2px 9px}
.trend.up{background:var(--bad-tint);color:var(--bad)}
.trend.down{background:var(--ok-tint);color:var(--ok)}
.trend.flat{background:var(--surface-2);color:var(--muted)}
.morelinks{display:flex;gap:10px;flex-wrap:wrap;margin-top:var(--gap-related)}
/* Under a list of rows it belongs to the list: related, so the small gap. */
.feed > .morelinks{margin-top:var(--gap-related)}


/* ---- Fortschritt: the steps of a reach-out, and where each supplier is --
   Named `rstep`, not `step`: the Übernahme wizard once owned `.step` (with
   text-align:center and a column layout), and reusing the name made this
   timeline inherit a centred stack. */
.rsteps{display:flex;flex-direction:column;gap:0;margin:8px 0}
.rstep{display:flex;gap:11px;align-items:flex-start;padding:9px 0;position:relative;
  text-align:left}
.rstep:not(:last-child)::before{content:"";position:absolute;left:9px;top:26px;bottom:-9px;
  width:2px;background:var(--border)}
.rstep .dot{width:20px;height:20px;border-radius:50%;flex:none;border:2px solid var(--border);
  background:var(--surface);display:flex;align-items:center;justify-content:center;
  font-size:var(--fs-label);color:var(--muted);z-index:1}
.rstep.done .dot{background:var(--accent-fill);border-color:var(--accent-mid);color:var(--on-fill)}
.rstep.done .stitle{color:var(--muted)}
.rstep .sbody{flex:1;min-width:0}
.rstep .stitle{font-size:var(--fs-body);font-weight:var(--fw-semibold);display:block}
.rstep .sdetail{font-size:var(--fs-caption);color:var(--muted);display:block}
.rstep .sdrop{flex:none;background:none;border:0;color:var(--faint);cursor:pointer;
  font-size:var(--fs-lead);line-height:1;padding:2px 6px;border-radius:var(--r-sm)}
.rstep .sdrop:hover{background:var(--bad-tint);color:var(--bad)}
.suppliers{display:flex;flex-direction:column;gap:8px}
.suprow{display:flex;align-items:center;gap:10px;padding:9px 11px;border:1px solid var(--border);
  border-radius:var(--r-md);background:var(--surface)}
.suprow b{flex:1;font-size:var(--fs-body);text-align:left}

.btn:disabled{opacity:.45;cursor:not-allowed}

/* ---------------------------------------------------------------------- */
/* The Kosten summary: what was paid over the period, what it costs per    */
/* month, and what that month is made of — one block, because they are one */
/* answer. They stood in three panels, two of which printed the same       */
/* monthly figure, and the same number twice on a screen reads as a        */
/* question about whether it is the same number.                           */
/* ---------------------------------------------------------------------- */
.panel.costsum{margin-bottom:16px;padding:0;overflow:hidden}
.costsum-figures{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr)}
.costsum-fig{padding:22px var(--inset);display:flex;flex-direction:column;gap:4px;min-width:0}
/* With no contract figure beside it the period's figure takes the row — its
   foot rule used to stop halfway across an otherwise empty panel. */
.costsum-fig:only-child{grid-column:1 / -1}
/* The contract's figure is the quieter of the two: it is context for the
   number left of it, not a second headline competing with it. */
.costsum-fig.alt{background:var(--surface-2);border-left:1px solid var(--border)}
.costsum-fig.alt .hero-figure{font-size:var(--fs-page)}
.costsum-parts{padding:16px var(--inset) 20px;border-top:1px solid var(--border)}
.costsum-head{display:flex;align-items:center;gap:10px;margin-bottom:var(--gap-related)}
.costsum-head b{font-size:var(--fs-body)}
@media (max-width:820px){
  .costsum-figures{grid-template-columns:1fr}
  .costsum-fig.alt{border-left:0;border-top:1px solid var(--border)}
}

/* A pager only appears when there is more than one page, so it never has to
   render a disabled-but-pointless state. */
.pager{display:flex;align-items:center;gap:12px;justify-content:center;
  padding:12px 16px;border-top:1px solid var(--border)}
.pager .btn[disabled]{opacity:.4;pointer-events:none}

/* Said before the click, not discovered after it: what a correction does to a
   row that a schedule was generating. */
.notice{background:var(--edu);border:1px solid var(--edu-border);
  border-radius:var(--r-md);padding:11px 13px;font-size:var(--fs-small);color:var(--ink-2);
  line-height:1.5}

/* Ja / Nein — a two-state answer shown in full rather than hidden behind a
   dropdown. The value itself rides on a hidden input, so the form collector
   sees the same shape it always did. */
.yesno{display:inline-flex;border:1px solid var(--border);border-radius:var(--r-btn);
  overflow:hidden;background:var(--card,#fff)}
.yesno .yn{appearance:none;border:0;background:transparent;cursor:pointer;
  padding:7px 18px;font:inherit;font-size:var(--fs-body);font-weight:var(--fw-semibold);color:var(--muted);
  border-right:1px solid var(--border)}
.yesno .yn:last-child{border-right:0}
.yesno .yn:hover:not(:disabled){background:var(--surface-2);color:var(--ink)}
.yesno .yn.on{background:var(--accent-fill);color:var(--on-fill)}
.yesno.off{opacity:.5}
.yesno .yn:disabled{cursor:default}

/* §5.39 — „Mieterhöhung prüfen“: the verdict, then one line per rule with
   its status as a word, then the two dates that matter, then what it rests on. */
.rk-cta{margin-top:var(--gap-related)}
.rk-result{margin-top:14px;padding-top:14px;border-top:1px solid var(--border)}
.rk-verdict{font-size:var(--fs-lead);font-weight:var(--fw-bold);color:var(--ink)}
.rk-verdict.bad{color:var(--bad)}
.rk-checks{list-style:none;margin:12px 0 0;padding:0;display:grid;gap:10px}
.rk-checks li{display:grid;grid-template-columns:auto 1fr;gap:10px;align-items:start;
  font-size:var(--fs-small);line-height:1.5}
.rk-dates{margin-top:12px;font-size:var(--fs-small);font-weight:var(--fw-semibold);display:grid;gap:4px}
.rk-basis{margin-top:10px;font-size:var(--fs-caption);line-height:1.5}

/* ---------------------------------------------------------------------- */
/* §5.8d — the buy-instead question. Closed by default: most people open   */
/* Kosten to look at what they pay, and a mortgage panel unfolded at them  */
/* answers a question they did not ask.                                    */
/* ---------------------------------------------------------------------- */
.buybar{display:flex;align-items:center;gap:16px;flex-wrap:wrap;
  padding:14px var(--inset);border-top:1px solid var(--edu-border);background:var(--edu)}
.buybar b{font-size:var(--fs-body)}
.buybar .sub{max-width:var(--measure)}
/* Inside the headline panel it is a BAND to the panel's edges — a nested box
   with its own margins is the box-in-a-box this screen should not have. */
.panel.hero > .buybar,.panel.hero > .buypanel{margin:14px calc(-1 * var(--inset)) -22px}
/* The calculator is the soft blue: an educational section, set apart from
   the ledger it sits under. Its figures stay white cards on it. */
.buypanel{padding:18px var(--inset) 20px;border-top:1px solid var(--edu-border);background:var(--edu)}
.buyhead{display:flex;align-items:center;gap:10px;margin-bottom:14px}
.buyhead b{font-size:var(--fs-body)}
.buygrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
.buyfig{border:1px solid var(--edu-border);border-radius:var(--r-card);padding:13px 16px;min-width:0;
  background:var(--surface)}
/* The loan is the answer; the two rates are how it was reached. */
.buyfig.wide{grid-column:1 / -1}
/* §5.38 — the monthly mail's switch: a sentence and its one button. */
.buyfig.rate-update{display:flex;align-items:center;gap:12px 16px;flex-wrap:wrap;margin-top:14px}
.buyfig.rate-update .sub{flex:1 1 260px;font-size:var(--fs-small);line-height:1.5}
.buyval{font-size:var(--fs-title);font-weight:var(--fw-bold);letter-spacing:-.015em;
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1}
.buyval.big{font-size:var(--fs-figure);font-weight:var(--fw-bold);letter-spacing:-.025em}
/* v1.223 — the rate, its trend arrow and the link to the curve on one line.
   The arrow is a letterform, so it takes the DARK member of each family:
   up is dearer (--bad), down cheaper (--ok), sideways a move inside the band
   (--faint). One drawing, turned — the three can never disagree in shape.
   Not `.trend`: that is the Übersicht's pill, background and all. The link is
   the panel's own `.buylink`, as the ImmoScout one is. */
.buyrate{display:flex;align-items:center;flex-wrap:wrap;gap:4px 10px}
.buyrate .buylink{margin-top:0}
.rate-trend{display:inline-flex;line-height:0}
.rate-trend svg{width:1.6em;height:1.6em}
.rate-trend.up{color:var(--bad)}
.rate-trend.down{color:var(--ok)}
.rate-trend.down svg{transform:rotate(180deg)}
.rate-trend.flat{color:var(--faint)}
.rate-trend.flat svg{transform:rotate(90deg)}
.buynote{margin-top:14px;font-size:var(--fs-small);line-height:1.6;color:var(--muted)}
.buynote p{margin:0 0 8px}
.buynote p:last-child{margin-bottom:0}
.buynote b{color:var(--ink)}
@media (max-width:720px){ .buygrid{grid-template-columns:1fr} }

/* §5.8e — where the arithmetic stops and a person takes over. */
.advice{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-top:16px;
  padding:14px 16px;border:1px solid var(--border);border-radius:var(--r-card);
  background:var(--surface-2)}
.advice b{font-size:var(--fs-body)}
.advice.open{display:block}
.advice.done{display:block;border-color:var(--ok)}
.advice-choices{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:10px;margin:12px 0}
.advice-choice{display:flex;flex-direction:column;gap:3px;text-align:left;
  padding:12px 14px;border:1px solid var(--border);border-radius:var(--r-card);
  background:var(--surface);cursor:pointer;font:inherit;color:inherit}
.advice-choice:hover{border-color:var(--muted)}
.advice-choice.on{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}
.advice-choice .sub{font-size:var(--fs-caption);line-height:1.45}
.advice .actions{display:flex;align-items:center;gap:10px}
@media (max-width:720px){ .advice-choices{grid-template-columns:1fr} }

/* §5.7a — two money lists in one section, and one button to add to either. */
.mlsec + .mlsec{margin-top:26px;padding-top:22px;border-top:1px solid var(--border)}
.mlhead{margin:0 0 6px;font-size:var(--fs-body);font-weight:var(--fw-bold)}
.addbar{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-top:18px;
  padding:12px 14px;border:1px solid var(--border);border-radius:var(--r-md);
  background:var(--surface-2)}
.addbar-choices{display:flex;gap:10px;flex-wrap:wrap}

/* Question on the left, answer on the right — and the question is the half
   somebody can change, so it gets a field of its own rather than a number
   squeezed into a caption. */
/* The divider between the two halves lands on the CENTRE LINE of the panel —
   the same line the gap between Zinssatz and Jahresrate straddles. Two things
   are needed for that and neither is optional: the columns must be equal
   (1.15fr for the answer pushed the split left of centre), and the rule must
   sit in the middle of the space between them rather than on the left
   column's own edge. So the grid gap is zero and each half carries half of it
   as padding, which puts the border exactly at half the content width — and
   the content box is symmetric within the card, so that is the card's centre
   and therefore the panel's. */
.buysplit{display:grid;grid-template-columns:1fr 1fr;gap:0;align-items:start}
.buyask,.buyget{min-width:0}
.buyask{padding-right:14px;border-right:1px solid var(--border)}
.buyget{padding-left:14px}
/* §5.8h — the property search for the loan, as a text link under the figure
   it is made of: a link, not a button, so the loan's cell stays as short as
   the question beside it. */
.buylink{display:inline-block;margin-top:6px;font-size:var(--fs-small);color:var(--accent);
  text-decoration:none}
.buylink:hover{text-decoration:underline}
/* §5.8e — the financing request: a flush band across the bottom of the
   question-and-answer card, under both halves, so it adds one line to the
   card rather than two to the loan's cell. Answered, it is the same band with
   one line and a check. */
.buyfin{grid-column:1 / -1;display:flex;align-items:center;flex-wrap:wrap;gap:8px 16px;
  margin:14px -16px -13px;padding:10px 16px;border-top:1px solid var(--edu-border);
  border-radius:0 0 var(--r-card) var(--r-card);background:var(--surface-2)}
.buyfin-text{flex:1 1 260px;font-size:var(--fs-small);line-height:1.5}
.buyfin.done{flex-wrap:nowrap;align-items:flex-start;gap:8px;font-size:var(--fs-small);
  line-height:1.5;color:var(--ok)}
.buyfin.done .ico{flex:none;margin-top:2px}
.buyfin.done span{color:var(--ink-2)}
a.btn{text-decoration:none}
.buyedit{display:flex;align-items:baseline;gap:6px;margin:2px 0 6px}
.buyedit input{width:100%;min-width:0;padding:2px 0;
  font:inherit;font-size:var(--fs-page);font-weight:var(--fw-bold);letter-spacing:-.015em;
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1;
  color:inherit;background:transparent;border:0;
  border-bottom:2px dashed var(--border);border-radius:0}
.buyedit input:hover{border-bottom-color:var(--muted)}
.buyedit input:focus{outline:none;border-bottom:2px solid var(--accent)}
.buyunit{font-size:var(--fs-title);font-weight:var(--fw-bold);color:var(--muted)}
@media (max-width:720px){
  .buysplit{grid-template-columns:1fr}
  .buyask{padding-right:0;padding-bottom:16px;border-right:0;
    border-bottom:1px solid var(--border)}
  .buyget{padding-left:0;padding-top:16px}
}
/* Dimmed while the new figure is on its way — the old one stays readable
   rather than blanking, so the block does not jump. */
.buyval.busy{opacity:.45;transition:opacity .12s}
/* §5.8 — a Mietbeginn the history runs from while nobody has given one. */
.assumed-note{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  background:var(--warn-tint);border-radius:var(--r-md);padding:9px 12px;margin-bottom:10px;
  font-size:var(--fs-small);color:var(--ink-2)}
.assumed-note span{flex:1 1 260px}
.cal-typed{max-width:12em}

/* §5.8g — the one-time costs, on the `.buysplit` of the row above: the choices
   stacked on the left, the total and its parts on the right. */
.buyclose-pick{display:flex;flex-direction:column;gap:10px;margin-top:12px}
.buyclose-pick label{display:flex;flex-direction:column;gap:3px;font-size:var(--fs-label);
  font-weight:var(--fw-semibold);color:var(--muted)}
.buyclose-pick select{width:100%;min-width:0}
#buy-land-source{margin-top:-6px}
#buy-land-source:empty{display:none}
#buy-closing.busy{opacity:.45;transition:opacity .12s}
.buyclose-items{display:flex;flex-direction:column;margin-top:12px;
  border-top:1px solid var(--border)}
.buyclose-item{display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:12px;
  align-items:baseline;padding:7px 0;border-bottom:1px solid var(--border)}
.buyclose-item span:first-child{font-size:var(--fs-small);color:var(--ink-2);min-width:0}
.buyclose-pct{font-size:var(--fs-caption);color:var(--muted);font-variant-numeric:tabular-nums;
  text-align:right;min-width:48px}
.buyclose-item b{font-size:var(--fs-body);font-weight:var(--fw-bold);font-variant-numeric:tabular-nums;
  text-align:right;min-width:84px}
.buyclose-note{margin-top:8px}
.linkbtn{background:none;border:0;padding:0;font:inherit;color:var(--accent);
  text-decoration:underline;cursor:pointer}

/* §5.8f — the rent against the rate, over the years. Closed by default: it is
   a second question, and the panel it sits in is already an answer. */
/* The panel lives inside the two-column card, so it must be told to span it —
   otherwise the chart is squeezed into the left half under the input. (The
   toggle that opens it is in the panel header now, not in this grid.) */
.fcpanel{grid-column:1 / -1;margin-top:18px;padding-top:16px;
  border-top:1px solid var(--border)}
.fchead{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:10px}
.fchead b{font-size:var(--fs-body)}
.fcnote{line-height:1.55;margin:8px 0}
.fclegend{display:flex;gap:16px;font-size:var(--fs-caption);color:var(--muted);margin:12px 0 6px}
.fclegend .sw{display:inline-block;width:9px;height:9px;border-radius:var(--r-mark);margin-right:5px}
/* Two series, and they must not be read as "more green is better" — the rent
   is the one that grows, so it takes the warmer colour. */
/* The rent is the series that GROWS, so it takes the warmer hue — not
   because warm means bad, but because two series that differ only in
   lightness stop being two series in a greyscale print. */
.fclegend .sw.rent,.fcbar.rent{background:var(--c4)}
.fclegend .sw.rent{box-shadow:inset 0 0 0 2px var(--c4-soft)}
.fclegend .sw.rate,.fcbar.rate{background:var(--c0)}
/* The columns share the width, and a PAIR of bars fills each one. The cap
   used to be 9px, which on a wide panel drew ten pairs of threads with a
   hand's width of nothing between them — the chart read as scattered marks
   rather than as two series to compare. 26px is wide enough to be a bar and
   still narrow enough that thirty years fit: the 44 % width takes over long
   before the cap does. */
.fcchart{display:flex;align-items:flex-end;gap:4px;height:170px;
  padding:0 2px;border-bottom:1px solid var(--border)}
.fccol{flex:1;min-width:0;display:flex;flex-direction:column;justify-content:flex-end;
  height:100%}
.fcbars{display:flex;align-items:flex-end;justify-content:center;gap:3px;
  height:100%;min-height:0}
.fcbar{width:44%;max-width:26px;min-width:2px;border-radius:var(--r-mark) var(--r-mark) 0 0;display:block}
/* A FIXED height, not a minimum. Only every fifth year carries a number, and
   a labelled cell grew to the text's line box (4px + ~15px) while an empty one
   stayed at 14px — so the bars above a number stood 5px higher than their
   neighbours and the chart had no common floor. */
.fcx{font-size:var(--fs-axis);line-height:16px;height:20px;flex:none;box-sizing:border-box;
  color:var(--faint);text-align:center;padding-top:4px;font-variant-numeric:tabular-nums}
.fcsum{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;
  margin-top:16px}
.fcsum > div{display:flex;flex-direction:column;gap:1px;min-width:0}
.fcsum b{font-size:var(--fs-card-title);font-weight:var(--fw-bold);letter-spacing:-.01em;
  font-variant-numeric:tabular-nums}
@media (max-width:720px){ .fcsum{grid-template-columns:repeat(2,minmax(0,1fr))} }

/* ====================== §5.5a — one question per screen ====================
   Laid out like the role chooser the product opens with: a question, room to
   answer it, and three ways forward. The old form put thirteen labels on one
   page and judged them all at the bottom. */
/* As wide as the page, like the stepper above it and every other panel: a
   760px card centred in a wide window left a margin either side that matched
   nothing on the page. The ANSWER keeps its width (`.wz-in`). */
.panel.wizard{padding:0;overflow:hidden}
.wz-top{display:flex;align-items:center;gap:10px;padding:14px 24px 10px;
  font-size:var(--fs-caption);color:var(--muted)}
.wz-block{font-weight:var(--fw-semibold);color:var(--ink-2)}
.wz-count{font-variant-numeric:tabular-nums}
.wz-rail{height:3px;background:var(--surface-2)}
.wz-rail i{display:block;height:100%;background:var(--accent-mid);
  transition:width .25s ease}

/* ONE HEIGHT FOR EVERY STEP. A panel that grows and shrinks between questions
   makes the buttons move under the cursor, and the reader re-finds them every
   time. `min-height` is set to what the tallest ordinary answer needs — the
   three role cards — so nothing below the body shifts; a step with more in it
   (the money lines) simply grows past it.

   Centred, because the content is one question and one answer: left-aligned,
   a 56px input sat in the corner of an 800px panel with nothing beside it. */
/* Sized to the QUESTION, with a floor rather than a target.
   It was 545px — the height the calendar needs — which held every step to
   the tallest one and pushed the buttons below the fold: on a laptop you had
   to scroll past an empty half-panel to reach „Weiter“. Steadiness is worth
   something, but not the price of the action being off screen.
   250px is what a heading, a help line and one field occupy, so the ordinary
   steps still agree with each other and the buttons sit just under the field;
   a calendar or a list of money lines simply grows past it. */
.wz-body{padding:30px 24px 26px;min-height:250px;
  display:flex;flex-direction:column;align-items:center;text-align:center}
.wz-q{font-size:var(--fs-title);font-weight:var(--fw-bold);letter-spacing:-.02em;line-height:1.25;
  margin:0 0 8px}
.wz-req{color:var(--accent);font-weight:var(--fw-bold)}
.wz-help{font-size:var(--fs-body);color:var(--muted);line-height:1.55;margin-bottom:var(--gap-section);
  max-width:52ch}
/* The answer takes the panel's width up to what it took when the panel was
   760px — a field is easier to aim at than to admire, but an address field
   across a whole wide screen is neither. Centred by `.wz-body`. */
.wz-in{width:100%;max-width:712px;margin-bottom:var(--gap-section)}
/* The question whose answer was refused: the answer area itself is marked,
   and `.wz-error` under it says why. */
.wz-in.bad input:not([type=hidden]),.wz-in.bad .cal,.wz-in.bad .wz-card{border-color:var(--bad)}
.wz-in.bad input.wz-field{box-shadow:0 0 0 3px var(--bad-tint)}
/* §5.8 — an assumed Mietbeginn, said where the real one is asked for. */
.wz-assumed{background:var(--warn-tint);color:var(--ink-2);border-radius:var(--r-md);
  padding:10px 14px;font-size:var(--fs-body);line-height:1.5;margin:0 auto 14px;max-width:60ch}
.wz-unlocks{font-size:var(--fs-small);color:var(--accent-strong);line-height:1.5;
  margin-top:12px;max-width:60ch}
.wz-opt{display:inline-flex;align-items:center;gap:7px;font-size:var(--fs-small);
  color:var(--muted);cursor:pointer;margin:0 8px}

/* A choice is a card you press, not a line in a dropdown. */
.wz-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));
  gap:12px;width:100%;text-align:left}
.wz-cards.two{grid-template-columns:repeat(2,minmax(0,1fr));max-width:420px;
  margin:0 auto}
.wz-card{display:flex;flex-direction:column;gap:5px;text-align:left;
  -webkit-appearance:none;appearance:none;
  padding:18px 18px;border:1.5px solid var(--border-strong);border-radius:var(--r-card);
  background:var(--surface);cursor:pointer;font:inherit;color:inherit;
  transition:border-color .12s,box-shadow .12s}
.wz-card:hover{border-color:var(--accent);box-shadow:0 6px 20px rgba(24,49,83,.07)}
.wz-card.on{border-color:var(--accent);background:var(--accent-soft)}
.wz-card b{font-size:var(--fs-card-title);font-weight:var(--fw-bold)}
.wz-card span{font-size:var(--fs-small);color:var(--muted);line-height:1.45}

/* One value, given the room of a headline — it is the only thing on screen. */
/* Full width by default — an address needs the room, and a field that ends
   halfway across the panel looks like it was cut off. A number is capped,
   because a 56px-tall box stretched to 700px for four digits reads as an
   error. */
.wz-one{display:flex;align-items:center;gap:10px;width:100%;margin:0 auto}
.wz-one.narrow{max-width:320px}
.wz-one .sugg-wrap{flex:1;min-width:0;display:block}
input.wz-field{text-align:center}
.wz-one.narrow input.wz-field{text-align:right}
/* `width:100%`, not just `flex:1`: the base control rule sets `width:auto`,
   and inside the address field's `.sugg-wrap` the input is not a flex item of
   `.wz-one` at all — so the address box stopped a third of the way across the
   panel while every other field filled it. */
input.wz-field{flex:1;width:100%;height:56px;min-height:56px;font-size:var(--fs-title);font-weight:var(--fw-bold);
  letter-spacing:-.015em;padding:0 14px;font-variant-numeric:tabular-nums}
.wz-unit{font-size:var(--fs-title);font-weight:var(--fw-bold);color:var(--muted)}
.wz-lines{margin:0 -4px}

/* The calendar, open by default. A date typed into three tiny segments is a
   date typed wrongly; a month you can look at is not. */
.cal{max-width:360px;margin:0 auto;text-align:left;
  border:1px solid var(--border-strong);border-radius:var(--r-card);
  padding:12px;background:var(--surface)}
.cal.off{opacity:.45;pointer-events:none}
/* The title is the way in to the month/year chooser, so it has to look
   pressable — but it is still the calendar's heading, so it stays a heading
   first and a control second. */
.cal-title{-webkit-appearance:none;appearance:none;border:1px solid transparent;
  background:transparent;font:inherit;font-size:var(--fs-lead);font-weight:var(--fw-bold);
  color:var(--ink);cursor:pointer;padding:4px 10px;border-radius:var(--r-sm);
  display:inline-flex;align-items:center;gap:6px}
.cal-title:hover{background:var(--accent-soft);border-color:var(--border-strong)}
.cal-title i{font-style:normal;font-size:var(--fs-label);color:var(--muted)}
.cal-title[aria-expanded="true"]{background:var(--accent-soft);
  border-color:var(--accent-mid)}
.cal-title[aria-expanded="true"] i{transform:rotate(180deg)}

.cal-pick{padding:6px 0 2px}
.cal-year{display:flex;align-items:center;gap:8px;margin-bottom:12px}
.cal-year input{flex:1;text-align:center;font-weight:var(--fw-bold);
  font-variant-numeric:tabular-nums}
.cal-months{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
.cal-month{-webkit-appearance:none;appearance:none;border:1px solid var(--border-strong);
  background:var(--surface);border-radius:var(--r-btn);padding:10px 0;
  font:inherit;font-size:var(--fs-small);font-weight:var(--fw-semibold);color:var(--ink);cursor:pointer}
.cal-month:hover{background:var(--accent-soft);border-color:var(--accent-mid)}
.cal-month.on{background:var(--accent-fill);border-color:var(--accent-fill);
  color:var(--on-fill)}

.cal-head{display:flex;align-items:center;justify-content:space-between;gap:8px;
  margin-bottom:10px}
.cal-head b{font-size:var(--fs-lead);font-weight:var(--fw-bold)}
.cal-dow{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;
  font-size:var(--fs-label);color:var(--faint);text-align:center;margin-bottom:4px}
.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}
.cal-pad{display:block}
.cal-day{-webkit-appearance:none;appearance:none;border:0;background:transparent;
  font:inherit;font-size:var(--fs-body);font-weight:var(--fw-bold);color:var(--ink);
  height:38px;border-radius:var(--r-sm);cursor:pointer;font-variant-numeric:tabular-nums}
.cal-day:hover{background:var(--accent-soft)}
.cal-day.today{box-shadow:inset 0 0 0 1.5px var(--border-strong)}
.cal-day.on{background:var(--accent-fill);color:var(--on-fill);font-weight:var(--fw-bold)}
.cal-foot{display:flex;align-items:center;gap:8px;margin-top:10px;
  padding-top:10px;border-top:1px solid var(--border)}

.wz-nav{display:flex;align-items:center;gap:10px;padding:16px 24px;
  border-top:1px solid var(--border);background:var(--surface-2)}
@media (max-width:600px){
  .wz-body{padding:22px 16px}
  .wz-nav{padding:14px 16px;flex-wrap:wrap}
  .wz-cards{grid-template-columns:1fr}
}

/* Links under a chart or a list sit where the eye lands after reading it —
   at the end of the line, not back at the left margin. */
.morelinks.right{justify-content:flex-end}
/* NOT `.fcbar` — that is already the chart's individual bar, 9px wide with a
   max-width to match, and reusing the name squeezed this whole row into nine
   pixels at the left edge. It sits inside the two-column grid, so it is also
   told to span it. */
.fcopen{grid-column:1 / -1;display:flex;justify-content:flex-end;flex-wrap:wrap;gap:10px;margin-top:16px}
/* §5.8g — a button that opens a section and closes it again. Closed it is an
   ordinary green button; open it takes the darker green as its surface, so
   it reads as pressed. */
.btn.toggle.is-on{background:var(--accent-strong);border-color:var(--accent-strong);color:var(--surface)}
.btn.toggle.is-on:hover{background:var(--accent);border-color:var(--accent)}

/* §5.14 — announced, and honest about not existing yet. */
.ai-modal{max-width:560px}
.ai-ico{display:inline-flex;align-items:center;justify-content:center;
  width:44px;height:44px;border-radius:var(--r-sm);background:var(--accent-soft);
  color:var(--accent-strong);margin-bottom:var(--gap-section)}
.ai-ico .ico{width:22px;height:22px}
/* Three blocks — what it does, that it is not here yet, what to do — with a
   section's gap between them and more before the buttons: at 14px each the
   pill sat as close to the paragraph above as to the buttons below, and the
   three read as one crowded stack. */
.ai-modal{padding:28px}
.ai-modal .sub{margin-bottom:0}
.ai-what{margin:var(--gap-section) 0 20px;font-size:var(--fs-body);line-height:1.6;color:var(--muted)}
.ai-what p{margin:0 0 12px}
.ai-what p:last-child{margin:0}
.ai-what b{color:var(--ink)}
.ai-soon{display:inline-block;font-size:var(--fs-small);font-weight:var(--fw-semibold);
  color:var(--accent-strong);background:var(--accent-soft);
  padding:6px 11px;border-radius:999px}
.ai-done{font-size:var(--fs-small);font-weight:var(--fw-semibold);color:var(--accent-strong)}
/* §5.20a — `#ai`: the state, its sentence and its one action, stacked. */
.ai-status{display:flex;flex-direction:column;align-items:flex-start;gap:var(--gap-related);
  padding-top:var(--gap-section);border-top:1px solid var(--border)}
.ai-status .sub{margin:0;max-width:var(--measure)}
.ai-emergency{margin-top:var(--gap-section)}
.ai-modal .actions{margin-top:28px;align-items:center}

/* §5.40 — the intake of „Problem mit KI lösen“. It is built from what every
   dialog already has (fields, buttons, the keep-file checkbox, .kv, .notice);
   these rules only place the parts that are its own: the row of doors, the
   photos, the line that says where words came from, and a question with its
   „Weiß ich nicht“. */
.pr-modal{max-width:620px}
.pr-modal .ai-ico{margin-bottom:var(--gap-related)}
.pr-wide{flex:1}
.pr-note{font-size:var(--fs-small);color:var(--muted);margin:-4px 0 var(--gap-related)}
.pr-doors{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin:var(--gap-related) 0}
.pr-busy{font-size:var(--fs-small);color:var(--muted)}
.pr-photos{display:flex;gap:var(--gap-related);flex-wrap:wrap;margin:var(--gap-related) 0}
.pr-photo{position:relative;width:76px;height:76px;border:1px solid var(--border);
  border-radius:var(--r-md);overflow:hidden;background:var(--surface-2)}
.pr-photo img{width:100%;height:100%;object-fit:cover;display:block}
.pr-photo button{position:absolute;top:3px;right:3px;width:22px;height:22px;padding:0;border:0;
  border-radius:50%;background:var(--surface);color:var(--ink);font-size:var(--fs-caption);
  line-height:1;cursor:pointer}
.pr-hint{font-size:var(--fs-small);color:var(--muted);margin-top:var(--gap-related)}
/* What the intake refuses: the attention colour as a rail, never as a button. */
.pr-stop{border-left:3px solid var(--warn);background:var(--warn-tint);
  border-radius:var(--r-md);padding:12px 14px;margin:var(--gap-section) 0}
.pr-stop p{margin:4px 0 10px;font-size:var(--fs-small);line-height:1.5}
.pr-stop p:last-child{margin-bottom:0}
.pr-desc{white-space:pre-wrap;overflow-wrap:anywhere;background:var(--surface-2);border-radius:var(--r-md);
  padding:12px 14px;margin:var(--gap-related) 0 var(--gap-section);line-height:1.5}

/* §5.40.3 — the intake as a page (v1.193). It borrows the wizard of §5.5a
   whole — `.panel.wizard`, `.wz-*` — so describing a problem looks like taking
   on a home: three stages, one question with room to answer it, a rail that
   fills. Only what that wizard has no word for is added here. */
.pr-stages .seg:disabled{cursor:default;color:var(--faint)}
/* A question with no line of help under it still stands clear of its answer. */
.pr-flow .wz-q + .wz-in{margin-top:var(--gap-related)}
/* Two or four choices stand as a square, not as three and an orphan. */
.pr-flow .wz-cards.even{grid-template-columns:repeat(2,minmax(0,1fr))}
/* The description is the one long answer: a field to write in, left-aligned,
   in a panel whose other answers are centred. */
.pr-area{display:block;width:100%;min-height:168px;text-align:left;resize:vertical;
  font-size:var(--fs-lead);line-height:var(--lh-body);padding:14px 16px}
.wz-body .pr-note{margin:8px 0 0;text-align:left}
.wz-body .pr-doors,.wz-body .pr-photos{justify-content:center}
.wz-body .keepdoc{justify-content:center}
.wz-body .pr-stop{width:100%;max-width:712px;text-align:left;margin-top:0}
/* The trade is one of sixteen, already proposed: a select, given the same
   room as every other answer. */
select.wz-field{width:100%;height:56px;font-size:var(--fs-lead);font-weight:var(--fw-semibold);padding-left:16px}
/* The date that goes with „Bis zum“ / „Ab dem“: a date field at a size its
   own calendar button fits beside, not a headline figure. */
.wz-one.pr-date{max-width:260px;margin-top:var(--gap-related)}
.wz-one.pr-date input.wz-field{text-align:left;font-size:var(--fs-lead);font-weight:var(--fw-semibold);
  letter-spacing:0}
.wz-card:disabled{opacity:.5;cursor:default;box-shadow:none;border-color:var(--border)}
.wz-body .wz-error[hidden]{display:none}
/* The review: what will be kept, a line each, with the way back to the
   screen that says it. Left-aligned — it is read, not answered. */
.pr-review{width:100%;max-width:712px;text-align:left}
.pr-review .pr-desc{margin:0 0 var(--gap-related)}
/* §5.41 — Unterlagen. A screen with several small answers (a contact, the
   flat, the people who moved in) is ordinary fields under their labels, never
   the big one-answer box; the base control style does the rest (§5.13). */
.pp-form{width:100%;max-width:712px;display:grid;grid-template-columns:1fr 1fr;gap:12px 16px;text-align:left}
.pp-form label{display:flex;flex-direction:column;gap:6px;font-size:var(--fs-small);
  font-weight:var(--fw-semibold);color:var(--muted)}
.pp-form .wide{grid-column:1 / -1}
.pp-form .pp-row{grid-column:1 / -1;display:grid;grid-template-columns:1fr 1fr auto;gap:12px 16px;align-items:end}
.pp-form textarea{min-height:96px;resize:vertical}
.pp-hint{grid-column:1 / -1;font-size:var(--fs-small);color:var(--muted);line-height:1.5}
.pp-pad{position:relative;width:100%;max-width:712px;border:1px solid var(--border-strong);border-radius:var(--r-input);
  background:var(--surface);touch-action:none}
.pp-pad canvas{display:block;width:100%;height:150px;cursor:crosshair}
.pp-pad .pp-line{position:absolute;left:16px;right:16px;bottom:34px;border-top:1px solid var(--border)}
.pp-padbar{width:100%;max-width:712px;display:flex;justify-content:space-between;align-items:center;gap:12px;
  margin:6px 0 var(--gap-section);font-size:var(--fs-small);color:var(--muted)}
.pp-status{display:flex;flex-direction:column;gap:var(--gap-related)}
/* §5.41.5 — draw, or type the name in a handwriting font. */
.sig-modes{width:100%;max-width:712px;margin:0 0 var(--gap-related)}
.sig-type{width:100%;max-width:712px;display:flex;flex-direction:column;gap:var(--gap-related);margin:0 0 var(--gap-related);text-align:left}
.sig-type label{display:flex;flex-direction:column;gap:6px;font-size:var(--fs-small);font-weight:var(--fw-semibold);color:var(--muted)}
.sig-fonts{display:flex;flex-wrap:wrap;gap:var(--gap-related)}
.sig-font{appearance:none;background:var(--surface);border:1px solid var(--border-strong);border-radius:var(--r-btn);
  padding:6px 16px;font-size:var(--fs-title);color:var(--ink);cursor:pointer;line-height:1.3}
.sig-font.on{border-color:var(--accent-fill);box-shadow:0 0 0 3px var(--accent-soft)}
.sig-font-a{font-family:"Herr Von Muellerhoff",cursive}
.sig-font-b{font-family:"Dancing Script",cursive}
@media (max-width:640px){.pp-form,.pp-form .pp-row{grid-template-columns:1fr}}
/* §5.41.4 — an emergency: the calls first, one under the other, each with its
   number readable for a desk without a telephone. */
.em-stop{width:100%;max-width:712px;text-align:left}
.em-calls{display:flex;flex-direction:column;align-items:flex-start;gap:var(--gap-related);margin:12px 0}
.em-calls .btn{gap:4px 10px;height:auto;min-height:var(--control-h);padding-top:8px;padding-bottom:8px;
  flex-wrap:wrap;justify-content:flex-start;text-align:left;white-space:normal}
@media (max-width:620px){
  /* A thumb's width: the call is the whole row, its number on a line of its own. */
  .em-calls{align-items:stretch}
  .em-calls .btn{flex-direction:column;align-items:flex-start}
}
.em-num{font-family:var(--font-mono);font-weight:var(--fw-regular)}
.em-add{display:flex;align-items:flex-end;gap:var(--gap-related);margin:0 0 12px}
.em-add label{display:flex;flex-direction:column;gap:6px;font-size:var(--fs-small);color:var(--ink);flex:1}
.em-now ul{margin:4px 0 0;padding-left:18px}
.em-now li{font-size:var(--fs-small);line-height:1.5}
.em-then{display:flex;flex-wrap:wrap;gap:var(--gap-related);justify-content:center;margin-top:var(--gap-section)}
.pr-rv{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.3fr) auto;gap:4px 16px;
  align-items:baseline;padding:10px 0;border-top:1px solid var(--border)}
.pr-rv.head{border-top:0;padding-top:0}
.pr-rv-k{color:var(--muted);font-size:var(--fs-small)}
.pr-rv-v.none{color:var(--faint)}
@media (max-width:600px){
  .pr-rv{grid-template-columns:minmax(0,1fr) auto}
  .pr-rv-k{grid-column:1 / -1}
  .pr-area{min-height:140px}
  /* On a phone the way forward is the full width of the thumb. */
  .pr-flow .wz-nav > .btn:not(.ghost){flex:1 1 100%}
}

/* §5.40.13 — the Verlauf: what went out, to whom, when, and what came of it.
   Drawn after design/5.40-verlauf/. One point is four parts on a line — when,
   a mark, who and what, the outcome — and the mark's colour says what kind of
   point it is: green an offer, the attention colour something that waits for
   the person, blue a promise, dark grey a refusal, pale everything that is
   simply a step. What is planned stands on top, on a dashed line with hollow
   marks. Built from tokens only. */
.vl{padding:4px 0 8px}
.vl-kpis .kpi{cursor:default}
.vl-kpis .kpi:hover{background:var(--surface)}
.vl-band,.vl-day{font-size:var(--fs-caption);color:var(--faint);padding:14px var(--inset) 4px 156px}
.vl-day{border-top:1px solid var(--border);margin-top:10px;padding-top:16px}
.vl-list{position:relative}
.vl-list::before{content:"";position:absolute;left:133px;top:6px;bottom:6px;border-left:2px solid var(--border-strong)}
.vl-list.dashed::before{border-left-style:dashed}
.vl-row{position:relative;display:grid;grid-template-columns:104px 20px minmax(0,1fr) auto 18px;
  column-gap:12px;align-items:start;padding:10px var(--inset)}
.vl-row.opens{cursor:pointer}
.vl-row.opens:hover{background:var(--surface-2)}
.vl-row.opens:focus-visible{outline:2px solid var(--accent-mid);outline-offset:-2px}
.vl-when{text-align:right;font-size:var(--fs-small);line-height:1.35}
.vl-when b{display:block;font-weight:var(--fw-medium)}
.vl-when span{display:block;color:var(--faint)}
.vl-mark{width:12px;height:12px;border-radius:50%;margin:4px 0 0 4px;background:var(--border-strong);
  box-shadow:0 0 0 3px var(--surface);position:relative;z-index:1}
.vl-mark.t-offer{background:var(--brand-green)}
.vl-mark.t-needs{background:var(--brand-orange)}
.vl-mark.t-promise{background:var(--live)}
.vl-mark.t-decline{background:var(--faint)}
.vl-row.planned .vl-mark{background:var(--surface);border:2px solid var(--border-strong);box-sizing:border-box}
.vl-row.planned .vl-mark.t-promise{border-color:var(--live)}
.vl-who{font-size:var(--fs-body);line-height:1.35}
.vl-arrow{color:var(--faint)}
.vl-sub{display:block;color:var(--muted);font-size:var(--fs-small);line-height:1.45}
.vl-out{display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end;
  font-size:var(--fs-small);max-width:420px;text-align:right}
.vl-quiet{color:var(--faint)}
.vl-chev{color:var(--faint);text-align:right;line-height:1.5}
.vl-now{display:flex;align-items:center;gap:10px;padding:8px var(--inset) 4px;font-size:var(--fs-caption)}
.vl-now span{width:104px;text-align:right;font-weight:var(--fw-semibold);color:var(--heading)}
.vl-now i{width:8px;height:8px;border-radius:50%;background:var(--brand-navy);margin:0 6px 0 16px;
  box-shadow:0 0 0 3px var(--surface);position:relative;z-index:1}
.vl-now b{font-weight:var(--fw-regular);color:var(--muted)}
.vl-now u{flex:1;border-top:1px solid var(--border-strong);height:0}
.vl-open{margin:2px var(--inset) 12px 156px;padding:14px 16px;background:var(--surface-2);
  border-radius:var(--r-md);max-width:620px}
.vl-open .kv{margin:0}
.vl-src{margin-top:10px;padding-top:10px;border-top:1px solid var(--border);
  font-size:var(--fs-caption);color:var(--muted)}
.vl-empty{padding:18px var(--inset) 18px 156px;color:var(--muted);font-size:var(--fs-small)}
.vl-foot{padding:10px var(--inset);font-size:var(--fs-caption);color:var(--faint);border-top:1px solid var(--border)}
.vl-end{display:flex;justify-content:flex-end;margin-bottom:var(--gap-section)}
/* The one thing that asks: the attention colour as a rail, never as a button. */
.vl-ask{display:flex;align-items:center;gap:16px;justify-content:space-between;
  padding:16px var(--inset);border-left:3px solid var(--warn)}
.vl-ask-text p{margin:4px 0 0;color:var(--ink-2);font-size:var(--fs-small);max-width:var(--measure)}
/* The button keeps its words on one line; it is the text beside it that gives. */
.vl-ask > .btn{flex:none;white-space:nowrap}
.vl-ask-more{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}
/* Nach Betrieb: one row per firm; a closed one stands last, its name greyed. */
.vf-row{display:flex;align-items:center;gap:12px;padding:14px var(--inset);border-top:1px solid var(--border);cursor:pointer}
.vf-row:first-child{border-top:0}
.vf-row:hover{background:var(--surface-2)}
.vf-row:focus-visible{outline:2px solid var(--accent-mid);outline-offset:-2px}
.vf-main{flex:1;min-width:0}
.vf-main b{margin-right:8px}
.vf-line{color:var(--muted);font-size:var(--fs-small);margin-top:2px}
.vf-row.closed b{color:var(--faint)}
.vf-open{padding:0 0 10px;border-top:1px solid var(--border)}
.vf-open .vl-list::before{left:165px}
.vf-open .vl-row{grid-template-columns:136px 20px minmax(0,1fr) auto 18px}
.vf-acts{display:flex;gap:8px;flex-wrap:wrap;align-items:center;padding:6px var(--inset) 0 188px}
.vf-time{display:inline-flex;gap:8px;align-items:center;font-size:var(--fs-small)}
/* The review, before anything is sent. */
.vr-firm{display:flex;gap:10px;align-items:flex-start;padding:10px 0;border-top:1px solid var(--border);cursor:pointer}
.vr-firm:first-of-type{border-top:0}
.vr-firm input{width:16px;height:16px;accent-color:var(--accent-fill);margin:4px 0 0}
.vr-left{margin-top:12px;font-size:var(--fs-small);color:var(--muted)}
.vr-left summary{cursor:pointer}
.vr-trail{padding:8px 0;border-top:1px solid var(--border)}
@media(max-width:620px){
  .vl-row,.vf-open .vl-row{grid-template-columns:20px minmax(0,1fr) 18px;row-gap:2px}
  .vl-when{grid-column:2;grid-row:1;text-align:left}
  .vl-when b,.vl-when span{display:inline;margin-right:6px}
  .vl-mark{grid-column:1;grid-row:1}
  .vl-body{grid-column:2;grid-row:2}
  .vl-out{grid-column:2;grid-row:3;justify-content:flex-start;text-align:left;margin-top:4px}
  .vl-chev{grid-column:3;grid-row:1}
  .vl-list::before,.vf-open .vl-list::before{left:calc(var(--inset) + 9px)}
  .vl-band,.vl-day,.vl-empty{padding-left:calc(var(--inset) + 32px)}
  .vl-now span{width:auto}
  .vl-now i{margin:0 4px}
  .vl-open{margin-left:calc(var(--inset) + 32px)}
  .vl-ask{flex-direction:column;align-items:flex-start}
  .vf-acts{padding-left:var(--inset)}
}
/* The icon inside a button needs the button's own gap, not a margin. */
.btn .ico{width:16px;height:16px}

/* The document-fill offer belongs to the SECTION, not to whichever field is
   on screen — and inside the step body it made step one taller than every
   other, so the buttons moved the moment you answered the first question. */
.wz-upload{margin:0 0 var(--gap-section)}

/* Said on the question it is about, not collected into a toast at the end of
   the section — by which point the reader is a dozen screens past the first
   of them and has to work out which question each label belonged to. */
.wz-error{margin-top:12px;font-size:var(--fs-small);font-weight:var(--fw-semibold);color:var(--bad);
  max-width:52ch;line-height:1.5}

/* A form in a card breathes like the rest of them: the panel itself carries
   no padding — `.body` does — and these two were built without one, so the
   fields and the save button sat against the card's edge. The extra room at
   the foot is for the button, which otherwise reads as falling out of the
   card it belongs to. */
.formcard > .body{padding:var(--inset)}
/* A card's heading is a heading: the interface face, sentence case, navy —
   not a line of grey monospace capitals, which read as a database label and
   left nobody sure which fields it was the title of. */
.form-title{font-size:var(--fs-lead);font-weight:var(--fw-bold);line-height:1.3;margin-bottom:var(--gap-related)}
.form-lead{font-size:var(--fs-small);color:var(--muted);max-width:var(--measure);margin-bottom:var(--gap-section)}
.formcard .form-title + .frow{margin-top:0}
.formcard .frow:last-of-type{margin-bottom:0}
/* A value that is shown, not edited: the label every field has, and the value
   in the body face under it. */
.form-read{display:flex;flex-direction:column;gap:3px;margin-bottom:var(--gap-section)}
.form-read > span{font-size:var(--fs-caption);color:var(--muted);font-weight:var(--fw-semibold)}
.form-read > b{font-weight:var(--fw-medium);overflow-wrap:anywhere}
/* The line under a field that says what it is for — part of the label, so
   it sits against its field and not a section's gap below it. */
.field-hint{font-size:var(--fs-caption);font-weight:var(--fw-regular);color:var(--muted);margin-top:2px}
.form-msg{font-size:var(--fs-small);color:var(--bad);font-weight:var(--fw-medium)}
.form-msg:not(:empty){margin-top:var(--gap-related)}
.tab-more{padding:var(--gap-related) var(--inset) var(--gap-section)}
.bk-notes{list-style:disc;padding-left:calc(var(--inset) + 18px);font-size:var(--fs-small);color:var(--muted);
  border-top:1px solid var(--border)}
.bk-notes li + li{margin-top:var(--gap-pair)}
.form-check{display:flex;gap:8px;align-items:center;font-size:var(--fs-body);font-weight:var(--fw-regular);
  color:var(--ink);cursor:pointer}
.formcard .section-title:first-child{margin-top:10px}
.formcard .actions{display:flex;align-items:center;gap:10px;margin-top:var(--gap-section)}
.formcard .frow{margin:var(--gap-section) 0}
.formcard .section-title + *{margin-top:0}   /* outweighs the line above */
/* Konto löschen: what stays and what goes, as two short lists. */
.erase-lists{display:grid;gap:var(--gap-section);margin:0 0 var(--gap-related)}
.erase-head{font-size:var(--fs-body);font-weight:var(--fw-semibold);color:var(--ink);margin-bottom:var(--gap-pair)}
.erase-lists ul{margin:0;padding-left:20px;font-size:var(--fs-body);line-height:1.55;color:var(--ink-2)}

/* ---------- Impressum / Datenschutz (§5.17) ---------- */
/* Two static pages served beside the SPA. They share the tokens, the
   sign-in page's language switch and the app's typeface — which is bundled
   (see the @font-face block at the top), so rendering a privacy page never
   phones a third party. They used to fall back to the system font for
   exactly that reason, while the fonts still came from Google. */
.legal{max-width:760px;margin:0 auto;padding:32px 24px 56px}
.legal-head{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:28px}
.legal-brand{font-size:var(--fs-title);font-weight:var(--fw-bold);letter-spacing:-0.02em;color:var(--ink);text-decoration:none}
.legal .landing-lang{position:static}
.legal article{background:var(--surface);border-radius:var(--r-panel);border:1px solid var(--border);padding:36px 40px;line-height:1.65;font-size:var(--fs-lead)}
.legal article[hidden]{display:none}
.legal h1{font-size:var(--fs-page);font-weight:var(--fw-bold);letter-spacing:-0.02em;margin-bottom:6px}
.legal .stand{color:var(--muted);font-size:var(--fs-small);margin-bottom:22px}
.legal h2{font-size:var(--fs-card-title);font-weight:var(--fw-bold);margin:26px 0 8px;letter-spacing:-0.01em}
.legal h3{font-size:var(--fs-body);font-weight:var(--fw-bold);margin:16px 0 6px}
.legal p,.legal ul,.legal table{margin-bottom:10px}
/* Variable and cookie names in the privacy policy: the product's mono, on the
   scale — not the browser's own monospace at 13px. */
.legal code{font-family:var(--font-mono);font-size:var(--fs-small)}
.legal ul{padding-left:20px}
.legal li{margin-bottom:4px}
.legal a{color:var(--accent-strong);text-decoration:underline;text-underline-offset:2px}
/* `table{min-width:max-content}` above is for the APP, where every table sits
   in a `.tbl-wrap` that scrolls: max-content is what stops columns squishing
   and lets the wrapper scroll instead. These pages have no wrapper, so that
   same rule pushed the table straight past the article and the browser clipped
   it — a privacy policy with its right-hand column cut off, which is a
   compliance problem and not a cosmetic one. It showed up in English first,
   because English runs longer than the German it was laid out with.

   Here the table may shrink, and the text wraps. `anywhere` is for the cells
   that carry a cookie name or a URL: one unbreakable token is enough to push a
   column back out again, and a legal page must never hide a word. */
.legal table{width:100%;min-width:0;border-collapse:collapse;font-size:var(--fs-small)}
.legal th,.legal td{text-align:left;padding:6px 8px;border-bottom:1px solid var(--border);vertical-align:top;overflow-wrap:anywhere}
.legal th{color:var(--muted);font-weight:var(--fw-semibold);font-size:var(--fs-caption)}
.legal .note{color:var(--muted);font-size:var(--fs-small)}
/* A blank the operator has to fill before going live. Lemon Icing behind the
   text and the dark member of the family as the ink — visible on purpose, so
   it cannot be shipped unnoticed. `data-fill` is what the boot check counts. */
/* The operator's own details are NOT highlighted. They used to be, which was
   right while every one of them was an empty bracket and wrong the moment they
   were filled: a real company name in a warning-coloured box reads as a
   mistake on a page whose whole job is to be believed. The `data-fill` marker
   stays — it is what the boot check counts — but it is now invisible to a
   reader, and only the server says how many are still open. */
.legal-foot{display:flex;gap:18px;justify-content:center;margin-top:28px;font-size:var(--fs-small)}
.legal-foot a{color:var(--muted);text-decoration:none}
.legal-foot a:hover{color:var(--ink);text-decoration:underline;text-underline-offset:2px}
@media (max-width:600px){.legal{padding:20px 14px 40px}.legal article{padding:24px 20px}}

/* ---------- Preise (§5.35) ---------- */
/* A centred lead, then two cards: what is free today and what is coming. The
   plans are cards because they are offers a person weighs side by side; the
   lines inside them are a check list, not ruled rows. The legal pages' header
   and foot, without their article panel. */
.pricing-wrap{max-width:760px;margin:0 auto;padding:32px 24px 56px}
.pricing-wrap .landing-lang{position:static;margin:0;max-width:none}
.pricing-hero{text-align:center;padding:40px 0 48px}
.pricing-hero h1{font-size:var(--fs-display);font-weight:var(--fw-bold);letter-spacing:-0.03em;line-height:1.1}
.pricing-lead{font-size:var(--fs-lead);color:var(--muted);line-height:var(--lh-body);max-width:var(--measure);margin:14px auto 22px}
.pricing-chip{display:inline-flex;align-items:center;gap:8px;padding:6px 16px;border-radius:999px;
  background:var(--surface);border:1px solid var(--border);font-size:var(--fs-small);font-weight:var(--fw-medium)}
.pricing-chip .ico{width:16px;height:16px;color:var(--accent)}
/* Stacked on a narrow screen; side by side from 960px, so both offers are in
   view at once. Each card is a column whose action sits at its foot, so the
   two buttons line up whatever the lists above them hold. */
.pricing-plans{display:grid;gap:32px;margin-bottom:32px}
.pricing-plan{position:relative;display:flex;flex-direction:column;background:var(--surface);
  border:1px solid var(--border);border-radius:var(--r-card);padding:40px 36px 32px}
/* The label sits ON the top border, as the name tab of the offer. */
.pricing-badge{position:absolute;top:0;left:32px;transform:translateY(-50%)}
.pricing-plan h2{font-size:var(--fs-title);font-weight:var(--fw-bold);letter-spacing:-0.02em;margin:0 0 10px}
.pricing-plan.later{background:transparent}
.pricing-price{font-size:var(--fs-figure);font-weight:var(--fw-bold);letter-spacing:-.025em;line-height:1.1;color:var(--heading)}
.pricing-price-sub{color:var(--muted);margin:6px 0 24px}
.pricing-sub{color:var(--muted);margin:-4px 0 24px}
.pricing-list{list-style:none;padding:0;margin:0 0 28px;display:grid;gap:16px}
.pricing-list li{display:flex;gap:14px;align-items:flex-start}
.pricing-list .ico{flex:none;margin-top:1px;color:var(--accent)}
.pricing-plan.later .pricing-list .ico{color:var(--muted)}
.pricing-list b{display:block;font-weight:var(--fw-semibold);color:var(--ink)}
.pricing-list span{display:block;color:var(--muted);font-size:var(--fs-small);line-height:var(--lh-body)}
/* `a:visited` outweighs `.btn`, so the link-as-button says its own ink. */
.pricing-cta,.pricing-cta:visited{display:flex;width:100%;margin-top:auto;color:var(--on-fill);text-decoration:none}
.pricing-notify{margin-top:auto;border-top:1px solid var(--border);padding-top:22px}
.pricing-form{display:flex;flex-wrap:wrap;gap:var(--gap-related);margin:12px 0 8px}
.pricing-form input{flex:1 1 200px;min-width:0}
.pricing-err{color:var(--bad);font-size:var(--fs-small)}
.pricing-done{font-weight:var(--fw-semibold);color:var(--ok);margin:12px 0 8px}
.pricing-note{color:var(--muted);font-size:var(--fs-small)}
.pricing-note a{color:var(--muted)}
/* Broad: both cards above the fold of an ordinary laptop (1440×900) — so the
   lead takes one line and the rhythm tightens, rather than the type shrinking. */
@media (min-width:960px){
  .pricing-wrap{max-width:1200px;padding-top:24px}
  .pricing-wrap .legal-head{margin-bottom:8px}
  .pricing-hero{padding:0 0 36px}
  .pricing-lead{max-width:none;margin:10px auto 18px}
  .pricing-plans{grid-template-columns:1fr 1fr}
  .pricing-plan{padding:36px 36px 28px}
  .pricing-price-sub{margin-bottom:20px}
  .pricing-list{gap:12px;margin-bottom:24px}
}
@media (max-width:600px){.pricing-wrap{padding:20px 14px 40px}.pricing-hero{padding:24px 0 40px}
  .pricing-plan{padding:32px 20px 24px}.pricing-badge{left:20px}}


/* Keyboard focus is always visible — on anything focusable, not only the
   one element that used to have it. Mouse clicks keep their clean look. */
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.nav-item:focus-visible,.whoami:focus-visible{outline-color:var(--accent-fill)}

/* A plain link takes the accent ink instead of the browser's own blue and
   visited purple; places that style their links keep doing so. */
a{color:var(--accent)}
a:visited{color:var(--accent)}

/* §5.33 — progress you can see. Quiet by construction: ink, never a tint on a
 * letterform, no new sizes. */
.wz-unlocks.made,.fld .unlocks.made{color:var(--ok)}
/* Belongs to the stage tabs above it, so RELATED to them and a SECTION away
   from what follows. */
.step-next{font-size:var(--fs-small);color:var(--muted);margin-top:var(--gap-related)}
.allclear{color:var(--ok);font-size:var(--fs-body);padding:14px 0;border-top:1px solid var(--border);
  border-bottom:1px solid var(--border);margin-bottom:var(--gap-section)}
.allclear .sub{color:var(--muted);margin-left:6px}
.record{margin-bottom:var(--gap-section);font-size:var(--fs-small);line-height:var(--lh-body)}
.record .sub{color:var(--muted)}
.review .kpi{cursor:default}
.review .kpi:hover{background:var(--surface)}
.review-kpis{margin-bottom:var(--gap-related)}
.review-sub{font-size:var(--fs-body);font-weight:var(--fw-semibold);margin:var(--gap-related) 0 4px}
.review-rows{display:flex;flex-direction:column}
.review-row{display:flex;align-items:baseline;gap:12px;padding:10px 0;border-top:1px solid var(--border)}
.review-row:first-child{border-top:0}
.review-row > span:first-child{flex:1}
.review-row .sub{display:block;color:var(--muted);font-size:var(--fs-small)}
.review p{max-width:var(--measure);margin:0 0 8px}
.review-note{color:var(--muted);font-size:var(--fs-small);max-width:var(--measure)}

/* §5.43 — „Jahr & Steuer“. The note stands ABOVE the figures, never in a
   footnote: these are figures to check, not advice. */
.tax-disclaimer{margin:var(--gap-related) var(--inset);padding:10px 14px;border-radius:var(--r-md);
  background:var(--edu);border:1px solid var(--edu-border);color:var(--edu-ink);font-size:var(--fs-small);max-width:var(--measure)}
/* Closeable once read (§5.43.10): the × sits at the note's end, in its ink. */
.tax-disclaimer{display:flex;align-items:flex-start;gap:12px}
.note-close{-webkit-appearance:none;appearance:none;background:none;border:0;padding:0 2px;margin-left:auto;
  cursor:pointer;color:inherit;font-size:var(--fs-lead);line-height:1;border-radius:var(--r-sm)}
.note-close:hover{color:var(--ink)}
/* A block that closes as a whole (§5.44.2's dates): the × in its corner, the
   text kept clear of it. */
.panel.closeable{position:relative}
.panel.closeable > .note-close{position:absolute;top:12px;right:12px;color:var(--muted)}
.panel.closeable > .body{padding-right:calc(var(--inset) + 24px)}
.tax-warn{margin:var(--gap-related) var(--inset);padding:10px 14px;border-radius:var(--r-md);
  background:var(--warn-tint);color:var(--warn);font-size:var(--fs-small);max-width:var(--measure)}
.tax-notes{margin:var(--gap-related) var(--inset);padding-left:18px;color:var(--muted);
  font-size:var(--fs-small);max-width:var(--measure)}
.tax-actions{margin-left:auto;display:flex;gap:8px;flex-wrap:wrap}
.linkrow.static > .btn.tertiary{margin-left:auto}
.tax-doc{display:inline-block;font-size:var(--fs-small)}

/* §5.44 — Verkauf vorbereiten. A row is a checkbox (what to ask for), the
   item with its state and papers, and its actions; the share wizard's file
   list is grouped like the checklist. */
.sale-row{align-items:flex-start;gap:4px 12px;flex-wrap:wrap}
/* The actions on a line of their own under the item, so its words keep the width. */
.sale-row > .tax-actions{flex-basis:100%;margin-left:28px;justify-content:flex-start}
.sale-row .sale-pick{width:16px;height:16px;margin-top:3px;flex:none;accent-color:var(--accent-fill)}
.sale-pick-gap{width:16px;flex:none}
.sale-main{flex:1;min-width:0}
.sale-main .pill{margin-left:6px}
.sale-doc{font-size:var(--fs-small)}
.sale-source{margin-top:6px;padding:8px 12px;border-radius:var(--r-md);background:var(--edu);color:var(--edu-ink);max-width:var(--measure)}
.sale-ask .frow{margin-top:var(--gap-related);align-items:flex-end}
.sale-pickgroup{margin-top:var(--gap-related);display:flex;flex-direction:column;gap:6px}
.sale-pickgroup .mlhead{display:flex;align-items:center;gap:8px}
.sale-pickgroup .keepdoc{font-size:var(--fs-body)}
.sale-review{margin:var(--gap-related) 0;padding-left:18px}
.modal .sw-note{display:flex;flex-direction:column;gap:4px;margin-top:var(--gap-related)}

/* §5.45 — the running year's Nebenkosten. The consequence is one sentence,
   at reading size; a Nachzahlung reads in the attention colour. */
.nk-year .nk-result{max-width:var(--measure);font-size:var(--fs-body);margin:0}
.nk-year .nk-result.due{color:var(--warn)}
.nk-year .nk-notes{padding-left:calc(var(--inset) + 18px);color:var(--muted);font-size:var(--fs-small)}
/* The keep-the-document tick inside a field row stays one line, box first. */
.frow label.keepdoc{flex-direction:row;align-items:center;gap:8px}
.frow .keepdoc input{width:16px;flex:none}

/* §5.43.4 — the Nebenkostenabrechnung on „Jahr & Steuer“. */
.nka .nka-unit{margin:var(--gap-related) var(--inset) 0;font-size:var(--fs-lead)}
.nka .nka-result{margin-top:4px;font-weight:var(--fw-semibold)}
.nka .nka-result.due{color:var(--warn)}
.nka .nka-date{width:auto}
.modal > label.keepdoc{display:flex;margin-top:var(--gap-related)}
/* §5.43.7 — the statement's positions are a table of nine fields: the dialog
   takes the room, and each field the width of its column. */
.modal.tax-stmt-modal{max-width:1120px}
.tax-pos{min-width:0;width:100%}
.tax-pos td{padding:4px 4px}
.tax-pos td input,.tax-pos td select{width:100%;min-width:72px}
.tax-pos td:first-child input{min-width:150px}
