/* Eider design: the house design of Amt Eider, as EiderDo has it.
 *
 * Taken over from EiderDo (core/static/core/css) with unchanged class names
 * and values, in its order: tokens, base, components, layout. After them the
 * few pieces EiderDo keeps in its modules and every module of Eider needs.
 * Only what the pages use is here, a later module brings its parts along
 * from EiderDo word for word. Module stylesheets (inventar.css) only read
 * the variables defined here.
 *
 * Every color, shadow, radius and timing is a token in this first part.
 * Rules only read var(--x). A hex value inside a rule is the value someone
 * forgets in dark mode, so the design tests reject it outside custom
 * property lines.
 *
 * Dark mode is declared twice with identical values: once for the system
 * preference and once for an explicit choice through data-theme on <html>.
 * Change both blocks together, a test compares them.
 */

/* ---------------------------------------------------------------- Tokens */

:root {
  color-scheme: light;

  /* Amtsblau carries the whole look. There is no second accent. */
  --primary: rgb(0, 104, 177);
  --primary-dark: #00517f;
  --primary-hover: var(--primary-dark);
  --primary-soft: #e8f2fa;
  --accent: var(--primary);
  --on-primary: #ffffff;
  /* Inset edge on --primary fills that mark a state (active tab, chosen
     day). Amtsblau stays Amtsblau in dark mode, where it reaches only
     2.8:1 against --surface, below the 3:1 a state needs (WCAG 1.4.11).
     The --accent edge carries the state there. */
  --selected-edge: transparent;

  --ink: #1a2530;
  --ink-soft: #5b6b7a;
  /* --line is decoration only (1.3:1): cards, dividers, table rules. The
     boundary of a control (input, select, textarea, date field, combobox,
     switch track) needs 3:1 against the surface (WCAG 1.4.11), hence
     --line-strong. It reaches 3.4:1 on --surface and 3.1:1 on --bg, but
     stays just below 3:1 on --surface-3, so no bordered control sits there. */
  --line: #dde5ec;
  --line-strong: #7f8e9c;

  --bg: #f2f6f9;
  --surface: #ffffff;
  --surface-2: #fbfdfe;
  --surface-3: #eef1f4;
  --header-bg: rgba(255, 255, 255, .88);

  /* Status colors mean status and nothing else. */
  --green: #1e7f43;
  --green-bg: #e8f6ee;
  --red: #b3362c;
  --red-bg: #fbeceb;
  --amber-ink: #8a5c00;
  --amber-bg: #fdf4e3;
  --danger-ink: #ffffff;

  /* The states of a check in Status, as EiderMonitor showed them: up, slow,
     down, maintenance, unknown. A word always stands next to the dot. Both
     sit on the page and on a card, so each reaches 4.5:1 on --bg and
     --surface. Orange for slow is the one colour only Status has, as far
     from red as from amber. The others follow the status colors above in
     light and dark. */
  --status-up: var(--green);
  --status-slow: #c43e00;
  --status-down: var(--red);
  --status-maint: var(--amber-ink);
  --status-unknown: var(--ink-soft);

  /* Opaque on purpose: a translucent ring fails the 3:1 focus contrast. */
  --focus-ring: #0068b1;
  --backdrop: rgba(13, 35, 54, .55);
  --tooltip-bg: #1a2530;
  --tooltip-ink: #ffffff;

  --shadow: 0 1px 2px rgba(16, 42, 67, .06), 0 8px 24px rgba(16, 42, 67, .08);
  --shadow-sm: 0 1px 2px rgba(16, 42, 67, .08);
  --shadow-lg: 0 2px 4px rgba(16, 42, 67, .06), 0 18px 48px rgba(16, 42, 67, .16);

  /* A coat of arms on dark ground is unreadable, so dark mode puts it on a
     white plate. In light mode the plate is invisible. */
  --logo-bg: transparent;
  --logo-pad: 0;

  /* System fonts only: a font from elsewhere reports every visitor's IP. */
  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "Cascadia Mono", "SF Mono", Menlo, Consolas, monospace;

  --radius: 14px;
  --radius-md: 12px;
  --radius-control: 10px;
  --radius-sm: 8px;
  --radius-xs: 7px;
  --radius-round: 999px;

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-14: 56px;

  /* Three widths, no more: forms, running text, overviews. */
  --width-form: 640px;
  --width-prose: 900px;
  --width-wide: 1080px;
  --header-height: 64px;

  --ease: ease;
  --ease-out: cubic-bezier(.2, .8, .2, 1);
  --ease-pop: cubic-bezier(.2, .9, .3, 1.15);
  --dur-press: .12s;
  --dur-hover: .15s;
  --dur-input: .18s;
  --dur-move: .2s;
  --dur-toast: .22s;
  --dur-theme: .25s;

  --z-sticky: 10;
  --z-header: 20;
  --z-header-popup: 30;
  --z-popover: 60;
  --z-skip: 200;
  --z-mascot: 250;
  --z-toast: 300;

  /* Tones for colors people choose (labels, workspaces, avatars, hub
     icons). Twelve pale pairs, background and ink always set together, ink
     at least 4.5:1 on its background and on every surface in both themes.
     A tone is a category, never a status: there is no green, red or amber
     tone, so a label color is never mistaken for done, error or attention. */
  --tone-blue-bg: #e7f0fb;
  --tone-blue-ink: #3556b0;
  --tone-brown-bg: #f3ebe4;
  --tone-brown-ink: #7a4f2e;
  --tone-indigo-bg: #e9eef8;
  --tone-indigo-ink: #4a5a8a;
  --tone-moss-bg: #e9f2e1;
  --tone-moss-ink: #4d6b1f;
  --tone-ochre-bg: #fdf1dc;
  --tone-ochre-ink: #92600a;
  --tone-pink-bg: #fceaf2;
  --tone-pink-ink: #b03572;
  --tone-plum-bg: #f6e8f6;
  --tone-plum-ink: #8a3a8a;
  --tone-rust-bg: #fbebe3;
  --tone-rust-ink: #a3471f;
  --tone-sky-bg: #e1f2fa;
  --tone-sky-ink: #0b6080;
  --tone-slate-bg: #eef1f4;
  --tone-slate-ink: #475569;
  --tone-teal-bg: #e2f4f2;
  --tone-teal-ink: #0f766e;
  --tone-violet-bg: #efe9fb;
  --tone-violet-ink: #6b3fbf;

  /* The local color pair components read. A .tone-<key> class replaces it,
     without one an element falls back to the neutral pair. --c-solid is the
     mark of dots and swatches, a tone maps it to its ink. */
  --c-bg: var(--surface-3);
  --c-ink: var(--ink-soft);
  --c-solid: var(--line-strong);

  /* The camera picture is dark in both themes, so the scanner on top of it
     has colors of its own, the same in light and dark. */
  --scan-bg: #0b0d10;
  --scan-frame: rgba(255, 255, 255, .9);
  --scan-veil: rgba(0, 0, 0, .35);
  --scan-status-bg: rgba(0, 0, 0, .6);
  --scan-status-ink: #ffffff;
  --scan-found: #4ec97f;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --primary-dark: #3d9bd6;
    /* #1b7dc4 from the Musterbogen gives 4.4:1 with white text. */
    --primary-hover: #1878be;
    --primary-soft: rgba(61, 155, 214, .14);
    --accent: #58aede;
    --on-primary: #ffffff;
    --selected-edge: var(--accent);

    --ink: #e7edf3;
    --ink-soft: #97a8b7;
    --line: #2b3946;
    --line-strong: #667a8e;

    --bg: #0f161d;
    --surface: #182230;
    --surface-2: #1d2a3a;
    --surface-3: #243244;
    --header-bg: rgba(24, 34, 48, .88);

    --green: #4ec97f;
    --green-bg: rgba(46, 160, 92, .16);
    --red: #ef8078;
    --red-bg: rgba(211, 84, 74, .16);
    --amber-ink: #e0b566;
    --amber-bg: rgba(201, 138, 27, .16);
    --status-slow: #f98b3a;
    /* White on the light dark-mode red is 2.6:1, dark text is 7.2:1. */
    --danger-ink: #14100f;

    --focus-ring: #58aede;
    --backdrop: rgba(2, 8, 14, .68);
    --tooltip-bg: #e7edf3;
    --tooltip-ink: #0f161d;

    --shadow: 0 1px 2px rgba(0, 0, 0, .35), 0 8px 24px rgba(0, 0, 0, .35);
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
    --shadow-lg: 0 2px 4px rgba(0, 0, 0, .4), 0 18px 48px rgba(0, 0, 0, .5);

    --logo-bg: #ffffff;
    --logo-pad: 4px 8px;

    --tone-blue-bg: rgba(84, 120, 214, .18);
    --tone-blue-ink: #9cb2ea;
    --tone-brown-bg: rgba(170, 120, 80, .18);
    --tone-brown-ink: #d2ae8e;
    --tone-indigo-bg: rgba(110, 130, 200, .16);
    --tone-indigo-ink: #a8b6e6;
    --tone-moss-bg: rgba(120, 170, 60, .16);
    --tone-moss-ink: #a8cc7a;
    --tone-ochre-bg: rgba(220, 160, 40, .16);
    --tone-ochre-ink: #e0b566;
    --tone-pink-bg: rgba(214, 84, 140, .16);
    --tone-pink-ink: #e598bd;
    --tone-plum-bg: rgba(190, 100, 190, .16);
    --tone-plum-ink: #dba4db;
    --tone-rust-bg: rgba(220, 110, 60, .16);
    --tone-rust-ink: #eb9f7c;
    --tone-sky-bg: rgba(56, 170, 220, .16);
    --tone-sky-ink: #7cc6e6;
    --tone-slate-bg: rgba(148, 163, 184, .16);
    --tone-slate-ink: #aebccb;
    --tone-teal-bg: rgba(20, 160, 150, .16);
    --tone-teal-ink: #63c9c0;
    --tone-violet-bg: rgba(148, 108, 230, .18);
    --tone-violet-ink: #bda2ec;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --primary-dark: #3d9bd6;
  /* #1b7dc4 from the Musterbogen gives 4.4:1 with white text. */
  --primary-hover: #1878be;
  --primary-soft: rgba(61, 155, 214, .14);
  --accent: #58aede;
  --on-primary: #ffffff;
  --selected-edge: var(--accent);

  --ink: #e7edf3;
  --ink-soft: #97a8b7;
  --line: #2b3946;
  --line-strong: #667a8e;

  --bg: #0f161d;
  --surface: #182230;
  --surface-2: #1d2a3a;
  --surface-3: #243244;
  --header-bg: rgba(24, 34, 48, .88);

  --green: #4ec97f;
  --green-bg: rgba(46, 160, 92, .16);
  --red: #ef8078;
  --red-bg: rgba(211, 84, 74, .16);
  --amber-ink: #e0b566;
  --amber-bg: rgba(201, 138, 27, .16);
  --status-slow: #f98b3a;
  /* White on the light dark-mode red is 2.6:1, dark text is 7.2:1. */
  --danger-ink: #14100f;

  --focus-ring: #58aede;
  --backdrop: rgba(2, 8, 14, .68);
  --tooltip-bg: #e7edf3;
  --tooltip-ink: #0f161d;

  --shadow: 0 1px 2px rgba(0, 0, 0, .35), 0 8px 24px rgba(0, 0, 0, .35);
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow-lg: 0 2px 4px rgba(0, 0, 0, .4), 0 18px 48px rgba(0, 0, 0, .5);

  --logo-bg: #ffffff;
  --logo-pad: 4px 8px;

  --tone-blue-bg: rgba(84, 120, 214, .18);
  --tone-blue-ink: #9cb2ea;
  --tone-brown-bg: rgba(170, 120, 80, .18);
  --tone-brown-ink: #d2ae8e;
  --tone-indigo-bg: rgba(110, 130, 200, .16);
  --tone-indigo-ink: #a8b6e6;
  --tone-moss-bg: rgba(120, 170, 60, .16);
  --tone-moss-ink: #a8cc7a;
  --tone-ochre-bg: rgba(220, 160, 40, .16);
  --tone-ochre-ink: #e0b566;
  --tone-pink-bg: rgba(214, 84, 140, .16);
  --tone-pink-ink: #e598bd;
  --tone-plum-bg: rgba(190, 100, 190, .16);
  --tone-plum-ink: #dba4db;
  --tone-rust-bg: rgba(220, 110, 60, .16);
  --tone-rust-ink: #eb9f7c;
  --tone-sky-bg: rgba(56, 170, 220, .16);
  --tone-sky-ink: #7cc6e6;
  --tone-slate-bg: rgba(148, 163, 184, .16);
  --tone-slate-ink: #aebccb;
  --tone-teal-bg: rgba(20, 160, 150, .16);
  --tone-teal-ink: #63c9c0;
  --tone-violet-bg: rgba(148, 108, 230, .18);
  --tone-violet-ink: #bda2ec;
}

@media (max-width: 640px) {
  :root { --header-height: 56px; }
}

/* Paper is white. Dark mode never reaches the printer. */
@media print {
  :root,
  :root[data-theme="dark"],
  :root:not([data-theme="light"]) {
    color-scheme: light;
    --ink: #000000;
    --ink-soft: #333333;
    --line: #999999;
    --line-strong: #666666;
    --bg: #ffffff;
    --surface: #ffffff;
    --surface-2: #ffffff;
    --surface-3: #f2f2f2;
    --accent: #000000;
    --shadow: none;
    --shadow-sm: none;
    --shadow-lg: none;
  }
}

/* ------------------------------------------------------------------ Base */

/* Sizes are in rem so browser text zoom works. The one fixed pixel font
   size (16px on inputs) is in the fields below. */

*, *::before, *::after { box-sizing: border-box; }

/* `hidden` must win over any display value set by a component class. */
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* Keyboard focus and anchor jumps must not end up under the sticky header
     (WCAG 2.4.11). */
  scroll-padding-top: calc(var(--header-height) + 12px);
  tab-size: 4;
}

body {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  font-family: var(--font);
  font-size: 1rem;
  line-height: 1.55;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  transition: background-color var(--dur-theme) var(--ease), color var(--dur-theme) var(--ease);
}

h1, h2, h3, h4 { margin: 0 0 var(--space-2); line-height: 1.25; font-weight: 700; color: var(--ink); }
h1 {
  font-size: clamp(1.25rem, 4vw, 1.5rem);
  letter-spacing: -.01em;
  text-wrap: balance;
  overflow-wrap: anywhere;
}
h2 { font-size: 1.05rem; }
h3 { font-size: 1rem; }
h4 { font-size: .95rem; }
h1 > .bi, h2 > .bi, h1 > svg, h2 > svg { color: var(--accent); flex: none; }

p { margin: 0 0 var(--space-3); }
p:last-child { margin-bottom: 0; }

a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: .18em;
}
a:hover { text-decoration-thickness: 2px; }

small { font-size: .85rem; }
strong, b { font-weight: 700; }

img, svg, video { max-width: 100%; }
img { height: auto; }

ul, ol { margin: 0 0 var(--space-3); padding-left: 1.4em; }

code, kbd, samp, pre { font-family: var(--font-mono); font-size: .85rem; }
code {
  padding: .1em .35em;
  border-radius: var(--radius-xs);
  background: var(--surface-3);
  color: var(--ink);
}

::selection { background: var(--primary-soft); }

fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
legend { padding: 0; }

/* The transparent outline is what remains visible in Windows contrast
   themes, where box-shadow is removed. The 2 px surface gap keeps the ring
   readable around filled Amtsblau buttons. */
:focus-visible {
  outline: 3px solid transparent;
  outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 5px var(--focus-ring);
}
/* Elements that only receive focus from script (headings, main) are not
   controls and need no ring. */
[tabindex="-1"]:focus { outline: none; box-shadow: none; }

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* WCAG 2.4.1. Off screen until focused, never display: none, which would
   remove it for everyone. */
.skip-link {
  position: absolute;
  left: 12px;
  top: -80px;
  z-index: var(--z-skip);
  padding: 10px 16px;
  border-radius: 0 0 var(--radius-control) var(--radius-control);
  background: var(--primary);
  color: var(--on-primary);
  font-weight: 600;
  text-decoration: none;
  transition: top var(--dur-hover) var(--ease);
}
.skip-link:focus { top: 0; }

.muted { color: var(--ink-soft); font-size: .92rem; }
.hint { color: var(--ink-soft); font-size: .8rem; }
.num { font-variant-numeric: tabular-nums; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.lead { color: var(--ink-soft); font-size: 1.02rem; max-width: 62ch; }

/* Section label with a hairline after it. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-3);
  color: var(--ink-soft);
  font-size: .78rem;
  font-weight: 700;
}
.eyebrow::after { content: ""; flex: 1; height: 1px; background: var(--line); }
/* A legend shrinks to its text, the hairline needs the whole width. */
legend.eyebrow { width: 100%; }

/* Capitals only where the house design guide uses them: small eyebrow
   labels (.09em) and table head cells (.04em). Tight capitals read as a
   block, hence the tracking. Never for sentences, headings, buttons, links,
   tabs or names people typed. The source text stays in normal case, so
   screen readers and copied text get the real word. CSS turns "ß" into
   "SS", so a label with ß gets another word. */
.eyebrow {
  text-transform: uppercase;
  letter-spacing: .09em;
}

.stack-sm { display: flex; flex-direction: column; gap: var(--space-2); }
.cluster-sm { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.grid-auto { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: var(--space-4); }

.mt-4 { margin-top: var(--space-4); }
.mt-6 { margin-top: var(--space-6); }
.mb-0 { margin-bottom: 0; }
.mb-4 { margin-bottom: var(--space-4); }

/* Exactly four keyframes for the app chrome. Somebody works with this app
   eight hours a day, and what charms once is in the way the fiftieth time. */
@keyframes fade-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}
@keyframes pop-in {
  from { opacity: 0; transform: scale(.94); }
  to { opacity: 1; transform: none; }
}
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  75% { transform: translateX(5px); }
}

/* Not negotiable (WCAG 2.3.3). A personal setting can render
   data-motion="reduce" on <html> and triggers the same rules. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}
:root[data-motion="reduce"] *,
:root[data-motion="reduce"] *::before,
:root[data-motion="reduce"] *::after {
  animation-duration: .01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: .01ms !important;
  scroll-behavior: auto !important;
}

@media (forced-colors: active) {
  :focus-visible { outline-color: Highlight; }
  .skip-link:focus { outline: 3px solid Highlight; }
}

@page { margin: 18mm 16mm; }

@media print {
  body { background: none; display: block; }
  .no-print { display: none !important; }
  a { color: inherit; text-decoration: underline; }
}

/* ------------------------------------------------------------ Components */

/* Interactive states are styled through their ARIA attribute ([aria-current],
   [aria-invalid]) and not through twin classes: if the attribute is wrong,
   the look is wrong too, so the bug gets seen. Hit areas below 44 px are
   extended with a transparent ::before, because ::after carries tooltips. */

/* Buttons */

.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: 10px 20px;
  border: 1.5px solid transparent;
  border-radius: var(--radius-control);
  font: inherit;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  transition:
    background-color var(--dur-input) var(--ease),
    border-color var(--dur-input) var(--ease),
    color var(--dur-input) var(--ease),
    transform var(--dur-press) var(--ease),
    box-shadow var(--dur-input) var(--ease);
}
.btn:active { transform: scale(.98); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .55; cursor: not-allowed; transform: none; }
.btn .bi { font-size: 1rem; line-height: 1; }

.btn-primary { background: var(--primary); color: var(--on-primary); }
.btn-primary:hover { background: var(--primary-hover); }
.btn-secondary { background: var(--surface); color: var(--accent); border-color: var(--accent); }
.btn-secondary:hover { background: var(--primary-soft); }
.btn-ghost { background: transparent; color: var(--ink-soft); border-color: var(--line); }
.btn-ghost:hover { color: var(--accent); border-color: var(--accent); }
.btn-danger { background: var(--red); color: var(--danger-ink); }
.btn-danger:hover { filter: brightness(.92); }
/* Looks like a link, behaves like a button: for actions inside text. */
.btn-link {
  min-height: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--accent);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: .18em;
}
.btn-link:active { transform: none; }

.btn-small { min-height: 36px; padding: 6px 14px; font-size: .88rem; }
.btn-small::before { content: ""; position: absolute; inset: -4px 0; }
.btn-block { width: 100%; }

.btn-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: var(--space-4); }

/* Icon buttons */

.icon-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--ink-soft);
  font: inherit;
  font-size: 1.05rem;
  text-decoration: none;
  cursor: pointer;
  transition:
    color var(--dur-hover) var(--ease),
    border-color var(--dur-hover) var(--ease),
    background-color var(--dur-hover) var(--ease),
    transform var(--dur-hover) var(--ease);
}
.icon-btn::before { content: ""; position: absolute; inset: -3px; }
.icon-btn:hover { color: var(--accent); border-color: var(--accent); }
.icon-btn:active { transform: scale(.94); }
.icon-btn-plain { border-color: transparent; }
.icon-btn-plain:hover { background: var(--primary-soft); border-color: transparent; }
.icon-btn-small { width: 32px; height: 32px; border-radius: var(--radius-sm); font-size: .95rem; }
.icon-btn-small::before { inset: -6px; }

/* Tooltips repeat the accessible name for sighted pointer and keyboard
   users. They appear on hover and focus, hide on Esc (menu.js sets
   .tips-off) and stay while the pointer is on them, as WCAG 1.4.13 asks.
   Generated content is not read twice because every tooltip owner carries
   an aria-label. */
[data-tip] { position: relative; }
/* Not rendered until needed: a hidden tooltip near the right edge would
   still widen the page and cause a horizontal scrollbar. The delay keeps
   tooltips from flickering while the pointer passes over a row of buttons.
   The tooltip touches its owner, so moving onto it keeps the owner hovered.
   menu.js keeps a click on the tooltip from running the owner's action. */
[data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  top: 100%;
  left: 50%;
  translate: -50% 0;
  z-index: var(--z-popover);
  display: none;
  width: max-content;
  max-width: 260px;
  padding: 5px 9px;
  border-radius: var(--radius-sm);
  background: var(--tooltip-bg);
  color: var(--tooltip-ink);
  font-size: .78rem;
  font-weight: 600;
  line-height: 1.3;
  text-align: center;
  white-space: normal;
  pointer-events: auto;
}
/* At the right edge of the header the tooltip grows to the left. */
[data-tip-pos="end"]::after { left: auto; right: 0; translate: none; text-align: right; }
/* Hidden during the delay, so a tooltip nobody sees yet catches neither
   the pointer nor a click meant for what lies below it. */
[data-tip]:hover::after, [data-tip]:focus-visible::after {
  display: block;
  transition: opacity var(--dur-hover) var(--ease-out) .35s, visibility 0s linear .35s;
}
@starting-style {
  [data-tip]:hover::after, [data-tip]:focus-visible::after { opacity: 0; visibility: hidden; }
}
.tips-off [data-tip]::after { display: none; }
@media (hover: none) { [data-tip]:hover:not(:focus-visible)::after { display: none; } }

/* Cards */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: var(--space-8);
}
.card + .card, .card + .card-section, .card-section + .card { margin-top: var(--space-6); }
/* In a grid or a row the gap does the spacing. */
:is(.grid-auto, .hub-grid, .cluster, .stat-grid) > .card + .card { margin-top: 0; }
.card-flush { padding: 0; overflow: clip; }
.card-tight { padding: var(--space-5) var(--space-6); }
.card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.card-head-text { min-width: 0; flex: 1; }
.card-head h2, .card-head h3 { display: flex; align-items: center; gap: var(--space-2); margin: 0; }
.card-head .muted { margin: 4px 0 0; }
.card-head-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }

/* Fields */

.field { margin-bottom: 18px; min-width: 0; }
.field:last-child { margin-bottom: 0; }
/* Only direct children: `.field label` would also hit option rows of
   a select and reset their layout. */
/* Checkbox labels are the control itself and keep their own layout. */
.field > label:not(.checkbox-label):not(.switch), .field-label, .field > fieldset > legend, fieldset.field > legend {
  display: block;
  margin-bottom: 6px;
  font-size: .92rem;
  font-weight: 600;
  color: var(--ink);
}

/* input[type="date"] is not in EiderDo's list, it has a date field of its
   own. Until Eider has that too, the native one looks like every field. */
input[type="text"], input[type="email"], input[type="password"], input[type="number"],
input[type="url"], input[type="tel"], input[type="search"], input[type="time"],
input[type="datetime-local"], input[type="date"], input:not([type]), textarea, select {
  width: 100%;
  min-height: 48px;
  padding: 12px 14px;
  border: 1.5px solid var(--line-strong);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  /* Anything smaller makes iOS zoom in on tap. The only px font size. */
  font-size: 16px;
  line-height: 1.4;
  transition:
    border-color var(--dur-input) var(--ease),
    box-shadow var(--dur-input) var(--ease),
    background-color var(--dur-theme) var(--ease);
}
textarea { min-height: 120px; resize: vertical; }
input::placeholder, textarea::placeholder { color: var(--ink-soft); opacity: 1; }
input:hover:not(:disabled):not(:focus), textarea:hover:not(:disabled):not(:focus), select:hover:not(:disabled):not(:focus) {
  border-color: var(--ink-soft);
}
/* Border to accent AND a 3 px ring. Both, not one of them. */
input:focus, textarea:focus, select:focus {
  outline: 3px solid transparent;
  outline-offset: 0;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}
/* ASP.NET marks a field it could not read with .input-validation-error. */
input[aria-invalid="true"], textarea[aria-invalid="true"], select[aria-invalid="true"],
input.input-validation-error, textarea.input-validation-error { border-color: var(--red); }
input:disabled, textarea:disabled, select:disabled { background: var(--bg); color: var(--ink-soft); cursor: not-allowed; }
input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
input[type="number"] { font-variant-numeric: tabular-nums; }

/* --accent is Amtsblau in light mode and the lighter blue in dark mode,
   where a box filled with Amtsblau reaches only 2.8:1 on --surface. */
input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); }

.field-hint { margin: 6px 0 0; color: var(--ink-soft); font-size: .85rem; line-height: 1.45; }
/* ASP.NET gives the error text of a field a class of its own, -error with
   an error and -valid without one. It writes the empty -valid one for
   every field, so that one stays hidden. */
.field-error, .field-validation-error {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin: 6px 0 0;
  color: var(--red);
  font-size: .85rem;
  font-weight: 600;
  line-height: 1.45;
}
.field-error .bi { flex: none; margin-top: 2px; }
.field-validation-valid { display: none; }

/* Four column grid for fields side by side. */
.fields-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0 var(--space-4); }
.fields-grid > * { grid-column: span 4; min-width: 0; }
.fields-grid > .f-threequarter { grid-column: span 3; }
.fields-grid > .f-half { grid-column: span 2; }
.fields-grid > .f-quarter { grid-column: span 1; }
/* Labels in multi-column rows get room for two lines, so neighbouring
   inputs line up whatever the label length. */
.fields-grid > .f-half > label:first-child,
.fields-grid > .f-quarter > label:first-child,
.fields-grid > .f-threequarter > label:first-child { min-height: 2.6em; }

/* A field with an icon inside. */
.input-wrap { position: relative; display: flex; align-items: center; }
.input-wrap > input { flex: 1; }
.input-wrap > .input-icon {
  position: absolute;
  left: 14px;
  color: var(--ink-soft);
  pointer-events: none;
}
.input-wrap > .input-icon + input { padding-left: 40px; }

/* Checkbox: here the label is the control and sits next to it. */
.checkbox-label {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  min-height: 44px;
  padding: 10px 0;
  font-weight: 500;
  cursor: pointer;
}
.checkbox-label input[type="checkbox"], .checkbox-label input[type="radio"] {
  flex: none;
  width: 20px;
  height: 20px;
  margin: 1px 0 0;
}

/* Radio pills. The native radio is tiny and cannot be styled. The input
   stays (hidden but focusable), the label is the whole hit area. */
.radio-pills { display: flex; flex-wrap: wrap; gap: 10px; }
.radio-pills input[type="radio"], .radio-pills input[type="checkbox"] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}
.radio-pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 44px;
  padding: 9px 18px;
  border: 1.5px solid var(--line-strong);
  border-radius: var(--radius-round);
  background: var(--surface);
  font-weight: 500;
  cursor: pointer;
  user-select: none;
  transition:
    border-color var(--dur-input) var(--ease),
    background-color var(--dur-input) var(--ease),
    color var(--dur-input) var(--ease);
}
.radio-pill:hover { border-color: var(--accent); }
.radio-pills input:checked + .radio-pill {
  border-color: var(--accent);
  background: var(--primary-soft);
  color: var(--accent);
  font-weight: 600;
}
.radio-pills input:focus-visible + .radio-pill { box-shadow: 0 0 0 2px var(--surface), 0 0 0 5px var(--focus-ring); }
.radio-pills input:disabled + .radio-pill { cursor: not-allowed; }
/* Not in EiderDo, where every disabled pill is dimmed: a form locked for
   reading still shows its value, so the chosen pill keeps its full
   contrast and only the others step back. */
.radio-pills input:disabled:not(:checked) + .radio-pill { opacity: .55; }

/* Switch. A checkbox that looks like one, for something that is on or off. */
.switch {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  cursor: pointer;
  user-select: none;
}
.switch-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}
.switch-track {
  position: relative;
  flex: none;
  width: 44px;
  height: 26px;
  border: 1.5px solid var(--line-strong);
  border-radius: var(--radius-round);
  background: var(--surface-3);
  transition: background-color var(--dur-hover) var(--ease), border-color var(--dur-hover) var(--ease);
}
.switch-track::after {
  content: "";
  position: absolute;
  top: 2.5px;
  left: 2.5px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--ink-soft);
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur-hover) var(--ease-out), background-color var(--dur-hover) var(--ease);
}
.switch-input:checked + .switch-track { background: var(--accent); border-color: var(--accent); }
.switch-input:checked + .switch-track::after { transform: translateX(18px); background: var(--on-primary); }
.switch-input:focus-visible + .switch-track { box-shadow: 0 0 0 2px var(--surface), 0 0 0 5px var(--focus-ring); }
.switch-input:disabled + .switch-track, .switch-input:disabled ~ .switch-label { opacity: .55; cursor: not-allowed; }
.switch-label { font-weight: 500; }
.switch-text { display: flex; flex-direction: column; }
.switch-text small { color: var(--ink-soft); font-size: .85rem; }

/* Menu. Markup: div.menu[data-component="menu"] > button[data-menu-trigger]
   + div.menu-list[data-menu]. data-menu-type="disclosure" for link lists.
   menu.js places the panel in the top layer (Popover API) or as a fixed
   element through the CSSOM. The values below are the resting state. */
.menu { position: relative; display: inline-block; }
.menu-caret { transition: transform var(--dur-hover) var(--ease); }
[aria-expanded="true"] > .menu-caret { transform: rotate(180deg); }
.menu-list {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: var(--z-popover);
  min-width: 220px;
  max-width: min(320px, calc(100vw - 16px));
  max-height: min(70vh, 520px);
  margin: 0;
  padding: 6px;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-lg);
  animation: fade-in .14s var(--ease-out) both;
}
.menu[data-menu-align="end"] > [data-menu] { left: auto; right: 0; }
.menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: .95rem;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.menu-item:hover, .menu-item:focus-visible { background: var(--primary-soft); }
.menu-item:focus-visible { outline: 3px solid transparent; box-shadow: inset 0 0 0 2px var(--accent); }
.menu-item > .bi { flex: none; width: 1.1em; color: var(--accent); }
.menu-item[aria-current="page"] { color: var(--accent); font-weight: 600; }
.menu-item-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.menu-item-label { font-weight: 600; }
.menu-separator {
  height: 1px;
  margin: 6px 8px;
  border: 0;
  background: var(--line);
}
.menu-head { padding: 8px 12px 10px; }
.menu-head strong { display: block; }
.menu-head small { color: var(--ink-soft); }
.menu-label { padding: 8px 12px 4px; color: var(--ink-soft); font-size: .78rem; font-weight: 700; }
.menu form { margin: 0; }

/* Tabbar. The active tab is filled with Amtsblau, not underlined.
   Navigation tabs are links with aria-current. */
.tabbar {
  display: flex;
  gap: 6px;
  max-width: 100%;
  margin-bottom: var(--space-5);
  padding: 5px;
  overflow-x: auto;
  scrollbar-width: thin;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
}
.tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: none;
  min-height: 44px;
  padding: 9px 15px;
  border: 0;
  border-radius: 9px;
  background: transparent;
  color: var(--ink-soft);
  font: inherit;
  font-size: .92rem;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--dur-hover) var(--ease), color var(--dur-hover) var(--ease);
}
.tab:hover { color: var(--accent); background: var(--primary-soft); }
.tab[aria-current="page"] { background: var(--primary); color: var(--on-primary); }
/* The fill alone is too weak a state cue in dark mode (see --selected-edge).
   The focus ring is listed with the edge, else it would replace it. */
.tab[aria-current]:not([aria-current="false"]) {
  box-shadow: inset 0 0 0 2px var(--selected-edge);
}
.tab[aria-current]:not([aria-current="false"]):focus-visible {
  box-shadow: inset 0 0 0 2px var(--selected-edge), 0 0 0 2px var(--surface), 0 0 0 5px var(--focus-ring);
}

/* Chips */

.chip-group { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 36px;
  padding: 6px 14px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-round);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: .88rem;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
}
button.chip, a.chip { cursor: pointer; transition: border-color var(--dur-hover) var(--ease), background-color var(--dur-hover) var(--ease); }
button.chip::before, a.chip::before { content: ""; position: absolute; inset: -4px 0; }
button.chip:hover, a.chip:hover { border-color: var(--accent); }
.chip[aria-pressed="true"], .chip[aria-current="page"] {
  border-color: var(--accent);
  background: var(--primary-soft);
  color: var(--accent);
}

/* Tags, badges, counts. .tag is round and loud, for the main state of a
   row. Text always goes with the color, color never stands alone. */
.tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 11px;
  border-radius: var(--radius-round);
  background: var(--surface-3);
  color: var(--ink-soft);
  font-size: .82rem;
  font-weight: 700;
  line-height: 1.4;
  white-space: nowrap;
}
.tag .bi { font-size: .82rem; }
.tag-yes { background: var(--green-bg); color: var(--green); }
.tag-no { background: var(--red-bg); color: var(--red); }
.tag-info { background: var(--primary-soft); color: var(--accent); }
.tag-off { background: var(--surface-3); color: var(--ink-soft); }
.tag[class*="tone-"] { background: var(--c-bg); color: var(--c-ink); }

/* .fbadge is angular and quiet, for properties that stand side by side. */
.fbadge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border-radius: var(--radius-xs);
  background: var(--c-bg);
  color: var(--c-ink);
  font-size: .74rem;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
}
.fbadge-warn { background: var(--amber-bg); color: var(--amber-ink); }
.fbadge-done { background: var(--green-bg); color: var(--green); }

.dot {
  display: inline-block;
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--c-solid);
}

/* Plain counter. */
.count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 22px;
  padding: 0 7px;
  border-radius: var(--radius-round);
  background: var(--surface-3);
  color: var(--ink-soft);
  font-size: .78rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Avatars */

.avatar {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--c-bg);
  color: var(--c-ink);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: .02em;
  user-select: none;
}
.avatar-md { width: 40px; height: 40px; font-size: 1rem; }
/* Without a chosen color a digit of the account picks a stable one, so a
   colleague keeps the same color everywhere. */
.avatar[data-seed="0"]:not([class*="tone-"]) { --c-bg: var(--tone-blue-bg); --c-ink: var(--tone-blue-ink); }
.avatar[data-seed="1"]:not([class*="tone-"]) { --c-bg: var(--tone-teal-bg); --c-ink: var(--tone-teal-ink); }
.avatar[data-seed="2"]:not([class*="tone-"]) { --c-bg: var(--tone-violet-bg); --c-ink: var(--tone-violet-ink); }
.avatar[data-seed="3"]:not([class*="tone-"]) { --c-bg: var(--tone-ochre-bg); --c-ink: var(--tone-ochre-ink); }
.avatar[data-seed="4"]:not([class*="tone-"]) { --c-bg: var(--tone-pink-bg); --c-ink: var(--tone-pink-ink); }
.avatar[data-seed="5"]:not([class*="tone-"]) { --c-bg: var(--tone-sky-bg); --c-ink: var(--tone-sky-ink); }
.avatar[data-seed="6"]:not([class*="tone-"]) { --c-bg: var(--tone-moss-bg); --c-ink: var(--tone-moss-ink); }
.avatar[data-seed="7"]:not([class*="tone-"]) { --c-bg: var(--tone-indigo-bg); --c-ink: var(--tone-indigo-ink); }
.avatar[data-seed="8"]:not([class*="tone-"]) { --c-bg: var(--tone-rust-bg); --c-ink: var(--tone-rust-ink); }
.avatar[data-seed="9"]:not([class*="tone-"]) { --c-bg: var(--tone-plum-bg); --c-ink: var(--tone-plum-ink); }

/* Empty state */

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-8) var(--space-4);
  text-align: center;
  animation: fade-in .45s var(--ease-out) both;
}
.empty-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  margin-bottom: var(--space-2);
  border-radius: 50%;
  background: var(--primary-soft);
  color: var(--accent);
  font-size: 1.6rem;
}
.empty-title { margin: 0; font-size: 1rem; font-weight: 700; }
.empty-text { max-width: 44ch; margin: 0; color: var(--ink-soft); }
.empty-state .btn-row { justify-content: center; margin-top: var(--space-3); }

/* Banners */

.banner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 14px;
  margin-bottom: var(--space-4);
  padding: 12px 16px;
  border-radius: var(--radius-md);
  background: var(--primary-soft);
  color: var(--ink);
  font-size: .92rem;
  font-weight: 600;
  animation: fade-in .3s var(--ease-out) both;
}
.banner > .bi { flex: none; font-size: 1.05rem; color: var(--accent); }
.banner-text { flex: 1; min-width: min(100%, 220px); }
.banner-warn { background: var(--amber-bg); color: var(--amber-ink); }
.banner-warn > .bi { color: var(--amber-ink); }
.banner-error { background: var(--red-bg); }
.banner-error > .bi { color: var(--red); }

/* Hub tiles */

.hub-section + .hub-section { margin-top: var(--space-6); }
.hub-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); gap: 14px; }
.hub-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow);
  color: var(--ink);
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: border-color var(--dur-hover) var(--ease), transform var(--dur-hover) var(--ease), box-shadow var(--dur-hover) var(--ease);
}
.hub-card:hover { border-color: var(--accent); transform: translateY(-2px); }
.hub-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 46px;
  height: 46px;
  border-radius: 13px;
  background: var(--primary-soft);
  color: var(--accent);
  font-size: 1.3rem;
}
.hub-icon[class*="tone-"] { background: var(--c-bg); color: var(--c-ink); }
.hub-card-body { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.hub-card-body strong { color: var(--ink); }
.hub-card-body small { color: var(--ink-soft); line-height: 1.4; }
.hub-chevron { flex: none; color: var(--ink-soft); transition: transform var(--dur-hover) var(--ease); }
.hub-card:hover .hub-chevron { transform: translateX(2px); color: var(--accent); }

/* Tables */

.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table { width: 100%; border-collapse: collapse; font-size: .92rem; }
.table th, .table td { padding: 10px 12px; border-bottom: 1px solid var(--line); text-align: left; vertical-align: top; }
/* Column heads in capitals with .04em tracking, as in the house design
   guide. A row head holds content (a title, a name), so it stays in normal
   case. */
.table th { color: var(--ink-soft); font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; white-space: nowrap; }
.table th[scope="row"] { color: var(--ink); font-size: inherit; font-weight: 600; text-transform: none; letter-spacing: normal; white-space: normal; }
.table td { overflow-wrap: anywhere; }
.table tbody tr { transition: background-color var(--dur-hover) var(--ease); }
.table tbody tr:hover { background: var(--primary-soft); }
.table tbody tr:last-child > * { border-bottom: 0; }

.pagination-list {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin: var(--space-5) 0 0;
  padding: 0;
  list-style: none;
}
.pagination-status { color: var(--ink-soft); font-size: .88rem; }

/* Disclosure */

.disclosure { border-radius: var(--radius-control); }
.disclosure-head {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 10px 12px;
  border-radius: var(--radius-control);
  font-weight: 600;
  list-style: none;
  cursor: pointer;
}
.disclosure-head::-webkit-details-marker { display: none; }
.disclosure-head::before {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  margin: 0 4px;
  border-right: 2px solid var(--ink-soft);
  border-bottom: 2px solid var(--ink-soft);
  transform: rotate(-45deg);
  transition: transform var(--dur-hover) var(--ease);
}
.disclosure[open] > .disclosure-head::before { transform: rotate(45deg); }
.disclosure-head:hover { background: var(--primary-soft); }
.disclosure-body { padding: 4px 12px 12px 36px; }

/* Toasts. Bottom right. The stack is not a live region: the message is
   announced through #sr-status, so a button label never gets read out. */
.ui-toast-wrap {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-2);
  width: min(380px, calc(100vw - 32px));
  pointer-events: none;
}
/* While a toast shows, focus moved with Tab stops above the stack instead
   of behind it (WCAG 2.4.11). */
:where(html:has(#toast-stack > .ui-toast)) { scroll-padding-bottom: 6rem; }
.ui-toast {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 52px;
  padding: 8px 8px 8px 14px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-lg);
  font-size: .92rem;
  font-weight: 600;
  pointer-events: auto;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--dur-toast) var(--ease), transform var(--dur-toast) var(--ease-out);
}
.ui-toast.is-visible { opacity: 1; transform: none; }
.ui-toast-icon { flex: none; font-size: 1.05rem; color: var(--accent); }
.ui-toast-ok .ui-toast-icon { color: var(--green); }
.ui-toast-warn .ui-toast-icon { color: var(--amber-ink); }
.ui-toast-text { flex: 1; min-width: 0; padding: 4px 0; overflow-wrap: anywhere; }

/* Rows inside one card. No dividers and no zebra stripes: hover and
   spacing carry the structure. */
.row-list { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }
.row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 56px;
  padding: 8px 12px 8px 4px;
  border-radius: var(--radius-control);
  transition: background-color var(--dur-hover) var(--ease);
}
.row:hover { background: var(--primary-soft); }
/* The label makes the 22 px checkbox a 44 by 44 target. */
.row-check {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 44px;
  height: 44px;
  cursor: pointer;
}
.row-check input { width: 22px; height: 22px; margin: 0; cursor: pointer; }
.row-main { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.row-title { flex: 1; min-width: 0; color: var(--ink); font-weight: 600; text-decoration: none; overflow-wrap: anywhere; }
.row-title:hover { color: var(--accent); text-decoration: underline; }
.row-meta { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 6px; }
/* Actions stay visible at low emphasis, never only on hover: touch and
   keyboard users cannot hover. */
.row-actions { flex: none; }

/* Tones. A tone sets the local pair. Tones are categories and never mean a
   status, so none of them is green, red or amber. Dots draw their mark
   (--c-solid) in the ink, which reaches 4.5:1 on every surface. */
.tone-blue   { --c-bg: var(--tone-blue-bg); --c-ink: var(--tone-blue-ink); --c-solid: var(--tone-blue-ink); }
.tone-indigo { --c-bg: var(--tone-indigo-bg); --c-ink: var(--tone-indigo-ink); --c-solid: var(--tone-indigo-ink); }
.tone-moss   { --c-bg: var(--tone-moss-bg); --c-ink: var(--tone-moss-ink); --c-solid: var(--tone-moss-ink); }
.tone-ochre  { --c-bg: var(--tone-ochre-bg); --c-ink: var(--tone-ochre-ink); --c-solid: var(--tone-ochre-ink); }
.tone-sky    { --c-bg: var(--tone-sky-bg); --c-ink: var(--tone-sky-ink); --c-solid: var(--tone-sky-ink); }
.tone-slate  { --c-bg: var(--tone-slate-bg); --c-ink: var(--tone-slate-ink); --c-solid: var(--tone-slate-ink); }
.tone-violet { --c-bg: var(--tone-violet-bg); --c-ink: var(--tone-violet-ink); --c-solid: var(--tone-violet-ink); }

/* Swatches keep their color in Windows contrast themes, with a border so
   they stay visible. */
@media (forced-colors: active) {
  .dot, .avatar, .fbadge, .hub-icon { forced-color-adjust: none; border: 1px solid CanvasText; }
  .switch-track::after { background: CanvasText; }
  .switch-input:checked + .switch-track { background: Highlight; }
  .tab[aria-current="page"] { outline: 2px solid Highlight; }
}

@media print {
  .btn, .icon-btn, .menu, .ui-toast-wrap, .banner, .tabbar { display: none !important; }
  .card, .hub-card { box-shadow: none; break-inside: avoid; }
  .row, tr { break-inside: avoid; }
}

@media (max-width: 860px) {
  .card { padding: var(--space-6) var(--space-5); }
}

@media (max-width: 640px) {
  .card { padding: var(--space-5) var(--space-4); border-radius: var(--radius-md); }
  .card + .card { margin-top: var(--space-4); }
  .btn-row:not(.btn-row-inline) { flex-direction: column; align-items: stretch; }
  .btn-row:not(.btn-row-inline) > .btn { width: 100%; }
  .fields-grid > .f-half, .fields-grid > .f-quarter, .fields-grid > .f-threequarter { grid-column: span 4; }
  .fields-grid > .f-half > label:first-child,
  .fields-grid > .f-quarter > label:first-child,
  .fields-grid > .f-threequarter > label:first-child { min-height: 0; }
  .tabbar { position: sticky; top: calc(var(--header-height) + 8px); z-index: var(--z-sticky); box-shadow: var(--shadow); }
  /* Not as in EiderDo: the name goes under the icon instead of beside it.
     So all tabs of a service fit on a 360 px phone with their full names,
     and nobody has to guess what an icon means. A name is never cut or
     shortened. If the names still do not fit (large text), only the bar
     scrolls sideways and tabbar.js keeps the current tab in view. */
  .tab { flex: 1 0 auto; flex-direction: column; justify-content: center; gap: 2px; min-width: 44px; padding: 5px 4px; font-size: .72rem; line-height: 1.2; }
  .tab > .bi { font-size: 1.1rem; }
  /* Focus and jumps stop below the header and a sticky tabbar (8 px gap,
     60 px bar with the names under the icons, 8 px air), not behind them
     (WCAG 2.4.11). */
  html { scroll-padding-top: calc(var(--header-height) + 76px); }

  .ui-toast-wrap { right: 12px; left: 12px; bottom: 12px; width: auto; align-items: stretch; }

  .row { flex-wrap: wrap; }
  .row-meta { order: 3; width: 100%; justify-content: flex-start; padding-left: 56px; }

  /* Rows become stacked cards. The column header moves in front of each
     value through data-label, else there is a column of bare values.
     The head is hidden only visually: display: none would take the column
     headers out of the accessibility tree. The explicit table roles in the
     markup keep the table a table. */
  .stack-table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .stack-table, .stack-table tbody, .stack-table tr, .stack-table td, .stack-table th[scope="row"] { display: block; width: 100%; }
  .stack-table tr {
    margin-bottom: var(--space-3);
    padding: 6px 0;
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    background: var(--surface-2);
  }
  .stack-table td, .stack-table th[scope="row"] { display: flex; gap: var(--space-3); align-items: baseline; padding: 6px 14px; border: 0; text-align: left; }
  /* The label stands in for the column head and looks like one. */
  .stack-table td[data-label]::before, .stack-table th[data-label]::before {
    content: attr(data-label);
    flex: 0 0 38%;
    max-width: 38%;
    color: var(--ink-soft);
    font-size: .76rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
  }
  /* Not in EiderDo: a long tag such as "Inventar: Administrator" wraps in
     its card instead of pushing the card wider than the phone. */
  .stack-table .tag { white-space: normal; }
}

@media (max-width: 380px) {
  .card { padding: var(--space-4) var(--space-3); }
}

/* ---------------------------------------------------------------- Layout */

/* Every page has the same frame, the second recognition mark after the
   color: the 3 px Amtsblau edge under the header, the skip link, the
   footer. Breakpoints are 860, 640 and 380 px. */

.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: var(--header-bg);
  border-bottom: 3px solid var(--primary);
  -webkit-backdrop-filter: saturate(1.6) blur(10px);
  backdrop-filter: saturate(1.6) blur(10px);
}
@media (prefers-reduced-transparency: reduce) {
  .site-header { background: var(--surface); -webkit-backdrop-filter: none; backdrop-filter: none; }
}
.header-inner {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  max-width: var(--width-wide);
  height: var(--header-height);
  margin: 0 auto;
  padding: 0 var(--space-5);
}
.header-spacer { flex: 1; min-width: 0; }
.header-context { display: flex; align-items: center; min-width: 0; }
.header-actions { display: flex; align-items: center; gap: 10px; flex: none; }
/* Without JavaScript the way to Mein Konto and out, side by side in the
   height of the header. A link next to a form would stack. */
.header-noscript { display: flex; align-items: center; gap: var(--space-2); }

.logo-link {
  display: inline-flex;
  align-items: center;
  flex: none;
  min-height: 44px;
  border-radius: var(--radius-control);
  color: var(--ink);
  text-decoration: none;
  transition: opacity var(--dur-hover) var(--ease);
}
.logo-link:hover { opacity: .82; }
.logo-default { display: inline-flex; align-items: center; gap: 10px; }
.logo-mark { display: block; flex: none; width: 34px; height: 34px; }
.logo-mark-bg { fill: var(--primary); }
.logo-mark-line { fill: none; stroke: var(--on-primary); stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
.logo-mark-wave { fill: none; stroke: var(--on-primary); stroke-width: 2; stroke-linecap: round; opacity: .7; }
.logo-word { font-size: 1.12rem; font-weight: 750; letter-spacing: -.015em; white-space: nowrap; }

/* The service switcher: a menu trigger with the colour square of the
   service, as the workspace switcher of EiderDo. */
.context-switch {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  max-width: 260px;
  height: 40px;
  padding: 0 10px 0 6px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: .92rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: border-color var(--dur-hover) var(--ease);
}
.context-switch::before { content: ""; position: absolute; inset: -2px; }
.context-switch:hover, .context-switch[aria-expanded="true"] { border-color: var(--accent); }
.context-square {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-sm);
  background: var(--c-bg);
  color: var(--c-ink);
  font-size: .9rem;
}
.context-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.service-switcher .menu-list { min-width: 260px; }
.service-switcher .menu-item[aria-current="page"] { background: var(--primary-soft); font-weight: 700; }

.avatar-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  border: 0;
  border-radius: var(--radius-round);
  background: transparent;
  color: var(--ink-soft);
  font: inherit;
  cursor: pointer;
}
.avatar-btn:hover .avatar, .avatar-btn[aria-expanded="true"] .avatar { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent); }
.avatar-btn .avatar { transition: box-shadow var(--dur-hover) var(--ease); }
.avatar-caret { font-size: .75rem; }
.header-account .menu-list { left: auto; right: 0; min-width: 260px; }
/* Entries of the account menu that stand in for header buttons on a phone. */
.only-narrow { display: none !important; }

.main {
  flex: 1 0 auto;
  width: 100%;
  margin: 0 auto;
  padding: var(--space-8) var(--space-5) var(--space-14);
  animation: fade-in .35s var(--ease-out) both;
}
.container-prose { max-width: var(--width-prose); }
.container-wide { max-width: var(--width-wide); }
.container-auth { max-width: 480px; }

.page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-3) var(--space-4);
  margin-bottom: var(--space-6);
}
.page-head-text { flex: 1; min-width: min(100%, 240px); }
.page-head h1 { display: flex; align-items: center; gap: 10px; margin: 0; }
.page-kicker { margin: 0 0 4px; color: var(--ink-soft); font-size: .85rem; font-weight: 600; }
.page-subtitle { margin: 6px 0 0; color: var(--ink-soft); }
.page-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }

.site-footer {
  margin-top: auto;
  border-top: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink-soft);
  font-size: .85rem;
}
.footer-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 4px 24px;
  max-width: var(--width-wide);
  margin: 0 auto;
  padding: var(--space-2) var(--space-5);
}
.footer-links { display: flex; flex-wrap: wrap; gap: var(--space-4); }
.footer-links a { display: inline-flex; align-items: center; min-height: 44px; color: var(--ink-soft); }
.footer-brand { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; }

/* Auth layout */

.auth-shell { display: flex; flex-direction: column; gap: var(--space-6); padding-top: 4vh; }
.auth-brand { display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; }
.auth-brand .logo-mark { width: 56px; height: 56px; }
.auth-brand-name { margin: 0; font-size: 1.3rem; font-weight: 750; letter-spacing: -.015em; }
.auth-card h1 { font-size: 1.3rem; margin-bottom: var(--space-2); }
.auth-after { text-align: center; color: var(--ink-soft); font-size: .92rem; }

/* Error layout */

.error-shell { display: flex; flex-direction: column; align-items: center; padding-top: 6vh; text-align: center; }
.error-card { width: 100%; max-width: 560px; }
.error-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  margin-bottom: var(--space-4);
  border-radius: 20px;
  background: var(--primary-soft);
  color: var(--accent);
  font-size: 1.8rem;
}
.error-icon-warn { background: var(--amber-bg); color: var(--amber-ink); }
.error-icon-danger { background: var(--red-bg); color: var(--red); }
.error-card .btn-row { justify-content: center; }

@media (max-width: 860px) {
  .main { padding: var(--space-6) var(--space-4) var(--space-10); }
  .header-inner { padding: 0 var(--space-4); }
  .context-switch { max-width: 200px; }
}

@media (max-width: 640px) {
  .main { padding: var(--space-4) var(--space-3) var(--space-10); }
  .header-inner { gap: var(--space-2); padding: 0 var(--space-3); }
  .header-actions { gap: 8px; }
  .logo-mark { width: 30px; height: 30px; }
  /* The theme toggle moves into the account menu. The login has none,
     there the toggle stays. */
  .header-actions:has(.header-account) > .theme-toggle { display: none; }
  .only-narrow { display: flex !important; }
  .context-switch { max-width: 150px; }
  .page-head { margin-bottom: var(--space-5); }
  .footer-inner { flex-direction: column; align-items: flex-start; padding: var(--space-3); }
}

/* Without JavaScript the menus in the header cannot open. On a phone their
   buttons give way to the fallback with Mein Konto and Abmelden, which
   does not fit beside them. The logo still leads to the services. */
@media (scripting: none) and (max-width: 640px) {
  .header-context, .header-account { display: none; }
}

/* Not as in EiderDo: the switcher keeps the name of the service. Without
   the word mark there is room for it, and a coloured square alone would
   need explaining. */
@media (max-width: 380px) {
  .logo-word { display: none; }
  .header-actions { gap: 4px; }
}

@media print {
  .site-header, .site-footer, .skip-link { display: none !important; }
  .main { padding: 0; animation: none; }
}

/* ------------------------------------------------- From EiderDo's modules */

/* EiderDo keeps these in its administration and tasks stylesheets. Every
   module of Eider needs them, so they live here. */

/* The way back above a heading. 44 px target without pushing the heading
   down, as .btn-small does. */
.page-kicker a {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 32px;
  color: var(--ink-soft);
  text-decoration: none;
}
.page-kicker a::before { content: ""; position: absolute; inset: -6px 0; }
.page-kicker a:hover { color: var(--accent); text-decoration: underline; }

/* A note above a list of results. */
.result-note { margin: 0 0 var(--space-3); color: var(--ink-soft); font-size: .92rem; }

/* Danger zone. In Eider it holds a disclosure, whose head takes the red. */
.card-danger { border-color: var(--red); }
.card-danger .card-head h2, .card-danger .card-head h2 .bi, .card-danger .disclosure-head { color: var(--red); }

/* The whole row of a list is a link: the link in the first cell stretches
   over it. The link stays the actual way, the row is a shortcut for the
   pointer. In the lists of the Inventar and the accounts of the Verwaltung. */
tr:has(.row-link) { position: relative; }
.row-link { display: flex; flex-direction: column; color: inherit; text-decoration: none; }
.row-link::after { content: ""; position: absolute; inset: 0; }

/* The second line of a row. */
.row-sub { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; color: var(--ink-soft); font-size: .8rem; }

/* Settings of the own account, one per row with its button (account.css in EiderDo). */
.setting-list { list-style: none; margin: 0; padding: 0; }
.setting-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-top: 1px solid var(--line);
}
.setting-row:first-child { border-top: 0; padding-top: 0; }
.setting-text { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: min(100%, 220px); }
.setting-text small { color: var(--ink-soft); }
/* Not in EiderDo: a switch and a select at the end of the row, as a Lizenz
   and its role. On a phone they wrap below the text. */
.setting-controls { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.setting-controls select { width: auto; min-width: 12rem; }
/* A line under the whole row, such as what the chosen role may do. */
.setting-row > .setting-hint { flex-basis: 100%; margin-top: 0; }
/* A line of a setting that needs doing, such as few recovery codes left
   (text-danger in EiderDo). The icon goes with the text. */
.setting-text .setting-warn { color: var(--amber-ink); }

/* Labels and values side by side, stacked on a phone (admin.css in EiderDo). */
.facts {
  display: grid;
  grid-template-columns: minmax(8rem, max-content) 1fr;
  gap: var(--space-2) var(--space-5);
  margin: 0;
}
.facts dt { color: var(--ink-soft); font-size: .92rem; }
.facts dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
@media (max-width: 640px) {
  .facts { grid-template-columns: 1fr; gap: 2px; }
  .facts dd { margin-bottom: var(--space-3); }
}

/* A value shown only once, such as a start password, with its copy
   button (admin.css in EiderDo). */
.secret {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  background: var(--surface-2);
}
.secret code {
  flex: 1 1 14rem;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 1.05rem;
  overflow-wrap: anywhere;
  user-select: all;
}

/* The setup of the second factor: the QR code next to the key and its facts,
   below each other on a phone (account.css in EiderDo). */
.totp-grid { display: flex; flex-wrap: wrap; gap: var(--space-5); align-items: flex-start; }
.totp-grid .facts { flex: 1; min-width: min(100%, 260px); }
.totp-qr {
  width: 200px;
  height: 200px;
  flex: none;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  /* The QR code keeps its own white margin in both themes: scanners need it. */
  image-rendering: pixelated;
}

/* A code from the app, digits that are easy to tell apart. */
.code-input { font-family: var(--font-mono); letter-spacing: .08em; }

/* The recovery codes, shown once, side by side as wide as there is room
   (account.css in EiderDo). */
.code-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
  gap: var(--space-2) var(--space-4);
  margin: 0 0 var(--space-4);
  padding: var(--space-4) var(--space-4) var(--space-4) var(--space-8);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface-2);
}
.code-list code { font-family: var(--font-mono); font-size: 1.05rem; letter-spacing: .06em; background: none; }
@media print {
  .code-list { border-color: currentColor; }
}

/* Not in EiderDo: a form shown only for reading keeps its values, locked.
   An empty locked field shows nothing, its example would look like a
   value. */
input:disabled::placeholder, textarea:disabled::placeholder { color: transparent; }

/* The search above a list (.task-search in EiderDo). */
.list-search { display: flex; align-items: center; gap: var(--space-2); max-width: 560px; }
.list-search .input-wrap { flex: 1; min-width: 0; }

/* Fields and a button in one row (.checklist-add in EiderDo). A number
   field keeps a width that fits a number. */
.inline-add { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.inline-add input[type="text"] { flex: 1 1 220px; }
.inline-add input[type="number"] { flex: 0 0 110px; }

/* The dashed area of a file upload. */
.upload-form { padding: var(--space-4); border: 1.5px dashed var(--line-strong); border-radius: var(--radius-md); }

/* Button text that gives way to the icon on a phone (from EiderSkill).
   The word stays for screen readers, display: none would take the
   button's name with it. */
@media (max-width: 640px) {
  .btn-text {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }
}
