/* Zeiterfassung – mobile first, no inline styles (CSP).
 *
 * The look is the office stamp: ink-dark clock card, paper-grey ground,
 * violet stamp ink for everything the employee does, brass for the day's
 * own goal. Times are the content, so every figure is set in tabular
 * numerals and the clock is the largest thing on the page (D-137).
 */

:root {
  color-scheme: light dark;
  /* Paper and ink */
  --bg: #f5f5f3;
  --surface: #ffffff;
  --text: #092229;
  --muted: #5a6a6d;
  --border: #e1e4e3;
  --card-border: #e3ce8a;
  --sunken: #fafaf8;
  /* Stamp ink: the colour of every action */
  --primary: #12545f;
  --primary-text: #ffffff;
  --primary-soft: #e8eeef;
  --secondary: #e9ecec;
  /* The ink card the clock sits on */
  --ink: #092229;
  --ink-text: #f5f7f7;
  --ink-muted: #9fb0b3;
  --ink-line: #143139;
  --ink-stamp: #fad25f;
  /* The day's own goal, and a settled day */
  --brass: #d4af37;
  --brass-ink: #092229;
  --brass-soft: #fbf6e7;
  --moss: #1c6b62;
  --moss-soft: #e5f0ee;
  /* Signals */
  --danger: #b3261e;
  --danger-bg: #fdecea;
  --notice-bg: #e8eeef;
  --info: #12545f;
  --info-bg: #e8eeef;
  --warning: #8a5100;
  --warning-bg: #fdf0dc;
  --critical: #b3261e;
  --critical-bg: #fdecea;
  --pending: #6b5b00;
  --pending-bg: #fdf6d4;
  --focus: #092229;
  --shadow: 0 1px 2px rgba(9, 34, 41, 0.05), 0 10px 30px rgba(9, 34, 41, 0.05);
}

/* The theme follows the system unless the employee chose one; the server
   writes `data-theme` on <html>, so the first paint is already right
   (D-122). All three blocks list the same variables so none can drift. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #071619;
    --surface: #092229;
    --text: #edf1f1;
    --muted: #93a5a8;
    --border: #1b3b44;
    --card-border: #c9b072;
    --sunken: #061417;
    --primary: #9ecfd6;
    --primary-text: #05181c;
    --primary-soft: #0f3640;
    --secondary: #12313a;
    --ink: #0c2b33;
    --ink-text: #f5f7f7;
    --ink-muted: #93a5a8;
    --ink-line: #23444c;
    --ink-stamp: #fad25f;
    --brass: #fad25f;
    --brass-ink: #fad25f;
    --brass-soft: #12333b;
    --moss: #6fc9bc;
    --moss-soft: #0f3029;
    --danger: #ff8a80;
    --danger-bg: #3a1f1d;
    --notice-bg: #0f3640;
    --info: #9ecfd6;
    --info-bg: #0f3640;
    --warning: #ffcf6b;
    --warning-bg: #3a2d10;
    --critical: #ff8a80;
    --critical-bg: #3a1f1d;
    --pending: #ffe27a;
    --pending-bg: #3a3310;
    --focus: #fad25f;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 10px 30px rgba(0, 0, 0, 0.4);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #071619;
  --surface: #092229;
  --text: #edf1f1;
  --muted: #93a5a8;
  --border: #1b3b44;
  --focus: #fad25f;
  --card-border: #c9b072;
  --sunken: #061417;
  --primary: #9ecfd6;
  --primary-text: #05181c;
  --primary-soft: #0f3640;
  --secondary: #12313a;
  --ink: #0c2b33;
  --ink-text: #f5f7f7;
  --ink-muted: #93a5a8;
  --ink-line: #23444c;
  --ink-stamp: #fad25f;
  --brass: #fad25f;
  --brass-ink: #fad25f;
  --brass-soft: #12333b;
  --moss: #6fc9bc;
  --moss-soft: #0f3029;
  --danger: #ff8a80;
  --danger-bg: #3a1f1d;
  --notice-bg: #0f3640;
  --info: #9ecfd6;
  --info-bg: #0f3640;
  --warning: #ffcf6b;
  --warning-bg: #3a2d10;
  --critical: #ff8a80;
  --critical-bg: #3a1f1d;
  --pending: #ffe27a;
  --pending-bg: #3a3310;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 10px 30px rgba(0, 0, 0, 0.4);
}

:root[data-theme="light"] {
  color-scheme: light;
  --bg: #f5f5f3;
  --surface: #ffffff;
  --text: #092229;
  --muted: #5a6a6d;
  --border: #e1e4e3;
  --card-border: #e3ce8a;
  --sunken: #fafaf8;
  --primary: #12545f;
  --primary-text: #ffffff;
  --primary-soft: #e8eeef;
  --secondary: #e9ecec;
  --ink: #092229;
  --ink-text: #f5f7f7;
  --ink-muted: #9fb0b3;
  --ink-line: #143139;
  --ink-stamp: #fad25f;
  --brass: #d4af37;
  --brass-ink: #092229;
  --brass-soft: #fbf6e7;
  --moss: #1c6b62;
  --moss-soft: #e5f0ee;
  --danger: #b3261e;
  --danger-bg: #fdecea;
  --notice-bg: #e8eeef;
  --info: #12545f;
  --info-bg: #e8eeef;
  --warning: #8a5100;
  --warning-bg: #fdf0dc;
  --critical: #b3261e;
  --critical-bg: #fdecea;
  --pending: #6b5b00;
  --pending-bg: #fdf6d4;
  --focus: #092229;
  --shadow: 0 1px 2px rgba(9, 34, 41, 0.05), 0 10px 30px rgba(9, 34, 41, 0.05);
}

* { box-sizing: border-box; }
/* ------------------------------------------------------------------ */
/* The frame: header, page column, cards, buttons, badges, footer      */
/* The draft's language: hairlines instead of frames, quiet surfaces,  */
/* an 8 px grid, 16 px radius outside and 10-12 px inside, gold as     */
/* line, ring and dot only - never as type on a light ground.          */
/* ------------------------------------------------------------------ */

/* The header stays put, so an anchor or an htmx `show:top` swap must not
   land underneath it - scrollIntoView honours the scrollport's padding.
   The header is two rows of 44 px (D-256). */
html { font-size: 100%; scroll-padding-top: 7.25rem; }

/* The global `*` rule does not reach pseudo elements, and the word mark's
   sign below is drawn from two of them. */
*::before, *::after { box-sizing: border-box; }

/* Header: two fixed rows at every width (D-256) – the word mark and the
   module on the first, the sections on the second, their gold underline
   resting on the header's own hairline. Left to wrap wherever the row
   ran out, the module stood beside four sections and under seven, and
   the header changed its height and its shape from page to page. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0 2rem;
  min-height: 4.5rem;
  padding: 0.5rem 1rem 0;
  background: var(--surface);
  /* The champagne hairline closes the header the way it closes every
     other container. */
  border-bottom: 1px solid var(--card-border);
}
/* Without a session the header is the word mark alone. */
.topbar > .brand:only-child { margin-bottom: 0.5rem; }

.brand {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  min-height: 2.75rem;
  font-weight: 650;
  font-size: 1rem;
  letter-spacing: -0.02em;
  text-decoration: none;
  color: var(--text);
}

/* The gold sign beside the word mark. The template carries no element
   for it and must not be touched, so the tile and the clock on it are
   drawn from the two pseudo elements. */
.brand::before {
  content: "";
  flex: none;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 0.5rem;
  background: var(--brass);
}

/* The dial and its hand. `--ink` is the one variable that is petrol in
   both themes, which is what a mark on gold needs; `--brass-ink` turns
   gold in the dark theme and would vanish here. */
.brand::after {
  content: "";
  position: absolute;
  top: 50%;
  /* Centred on the 1.75rem tile: (1.75 - 0.8) / 2. */
  left: 0.475rem;
  width: 0.8rem;
  height: 0.8rem;
  border: 0.125rem solid var(--ink);
  border-radius: 999px;
  transform: translateY(-50%);
  background-image: linear-gradient(var(--ink) 0 100%);
  background-size: 0.1rem 0.275rem;
  background-position: 50% 0;
  background-repeat: no-repeat;
  /* Clipped to the padding box, so the hand starts inside the dial and
     ends in its centre. */
  background-origin: padding-box;
  background-clip: padding-box;
}

/* The sections take the second row whole, from the left edge – the
   word mark's edge. */
.topnav {
  order: 2;
  flex: 1 0 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 1.25rem;
}

.user-name {
  color: var(--muted);
  font-size: 0.85rem;
  font-weight: 600;
  min-width: 0;
  /* A long name is not ours to shorten; it wraps rather than pushing the
     header off a phone. */
  overflow-wrap: break-word;
}

/* Sections step back; the page one is on steps forward. Each is a full
   touch target even though it reads as plain text. */
.nav-link {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  text-decoration: none;
  font-weight: 600;
  font-size: 0.92rem;
  padding: 0 0.1rem;
  border-bottom: 2px solid transparent;
  color: var(--muted);
}
.nav-link:hover { color: var(--text); }

/* The page you are on is the one that is underlined - and gold carries
   the line, while the text stays ink and readable. */
.topnav .nav-link.active {
  color: var(--text);
  border-bottom-color: var(--brass);
}

/* Name, preferences and sign-out are one quiet module, as in the draft:
   sunk surface, hairline, 12 px radius, 44 px tall. Inside it the three
   parts are separated by hairlines rather than by air – that is what
   makes them read as one group instead of three loose links. */
.nav-right {
  order: 1;
  display: flex;
  align-items: stretch;
  min-width: 0;
  min-height: 2.75rem;
  margin-left: auto;
  border: 1px solid var(--border);
  border-radius: 0.75rem;
  background: var(--sunken);
}
.nav-right > * {
  display: flex;
  align-items: center;
  padding-inline: 0.75rem;
  border-inline-start: 1px solid var(--border);
}
.nav-right > :first-child { border-inline-start: 0; }
.nav-right .nav-link { border-bottom: 0; }
/* Inside the module nothing shouts: signing out is a way out, not the
   action of the page – and it is set like "Mein Konto" beside it. */
.nav-right .btn-link { color: var(--muted); text-decoration: none; padding-inline: 0; font-size: 0.92rem; }
.nav-right .btn-link:hover { color: var(--text); text-decoration: underline; }

/* Der Name mit seinem Zeichen – dieselben Initialen wie an der
   Terminalwand, damit dieselbe Person überall gleich abgekürzt wird. */
.nav-user { gap: 0.5rem; min-width: 0; }
.nav-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 1.75rem;
  height: 1.75rem;
  border: 1px solid var(--card-border);
  border-radius: 999px;
  background: var(--surface);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--brass-ink);
}

/* Der Designschalter: ein Knopf, der aussieht wie ein Schalter, und ein
   Formular, das ohne JavaScript auskommt. Die Stellung steht in
   aria-pressed – daran hängt auch die Farbe, nicht an einem style. */
.theme-form { margin: 0; }
.theme-switch {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 2.5rem;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  cursor: pointer;
}
.theme-switch-label {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}
.theme-switch-track {
  position: relative;
  display: block;
  width: 2.5rem;
  height: 1.5rem;
  flex: none;
  border: 1px solid var(--card-border);
  border-radius: 999px;
  background: var(--secondary);
}
.theme-switch-knob {
  position: absolute;
  top: 0.125rem;
  left: 0.125rem;
  width: 1.125rem;
  height: 1.125rem;
  border: 1px solid var(--card-border);
  border-radius: 999px;
  background: var(--surface);
}
.theme-switch[aria-pressed="true"] .theme-switch-track { background: var(--brass); border-color: var(--brass); }
.theme-switch[aria-pressed="true"] .theme-switch-knob { left: auto; right: 0.125rem; background: var(--ink); border-color: var(--ink); }
@media (prefers-reduced-motion: no-preference) {
  .theme-switch-track, .theme-switch-knob { transition: background-color 120ms ease, border-color 120ms ease; }
}
@media (hover: hover) {
  .theme-switch:hover .theme-switch-label { color: var(--text); }
}

/* On a tablet the switch keeps its name for the screen reader only, so
   the module stays on the word mark's row. */
@media (max-width: 48rem) {
  .nav-right .theme-switch-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
}

@media (max-width: 30rem) {
  /* Five header rows would take a third of a phone screen, so there the
     header scrolls away with the page instead of sticking - and then a
     swap needs no room reserved above it either. */
  .topbar { position: static; gap: 0 1rem; padding: 0.75rem 1rem; }
  html { scroll-padding-top: 0; }
  /* The sections under the word mark, then the module: the name left,
     "Mein Konto" and "Abmelden" right in the same row (D-178), the switch
     with its name in a row of its own under a hairline – a divider never
     starts a line. */
  .topnav { gap: 0 0.75rem; margin-bottom: 0.5rem; }
  .topnav .nav-link { font-size: 0.85rem; }
  .nav-right {
    order: 3;
    flex: 1 0 100%;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto;
    margin-left: 0;
  }
  .nav-right > * { padding-inline: 0.55rem; }
  .nav-right > .theme-form {
    grid-column: 1 / -1;
    border-inline-start: 0;
    border-top: 1px solid var(--border);
  }
  .theme-switch { width: 100%; justify-content: space-between; }
  .nav-right .theme-switch-label { position: static; width: auto; height: auto; overflow: visible; clip: auto; }
}

/* Layout
   Mobile first (spec section 6), but a wide screen must not waste its
   width. The gutter stays at 16 px down to 390 px and grows to the
   draft's 32 px once there is room for it. */
.content {
  max-width: min(100%, 68rem);
  margin: 0 auto;
  padding: 1.5rem 1rem 3rem;
}

@media (min-width: 48rem) {
  .topbar { padding-inline: 2rem; }
  .content { padding-inline: 2rem; }
}

/* A card is a surface held by a hairline, not a box drawn with a frame:
   champagne edge, 16 px radius, and a shadow soft enough that one reads
   the lift rather than the shadow. */
.card {
  background: var(--surface);
  border: 1px solid var(--card-border);
  border-radius: 1rem;
  padding: 1.25rem;
  margin-bottom: 1.5rem;
  box-shadow: var(--shadow);
}

@media (min-width: 40rem) {
  .card { padding: 1.5rem; }
}

/* The ledger: the day one reads on the left, what the week earned on the
   right. One column until there is room for two. The single column is
   floored at zero so a wide table inside it scrolls in its own wrapper
   instead of widening the page. */
.ledger { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0; }
@media (min-width: 62rem) {
  .ledger { grid-template-columns: minmax(0, 1.9fr) minmax(18rem, 1fr); gap: 1.5rem; align-items: start; }
  .ledger-main > .card:last-child, .ledger-side > .card:last-child { margin-bottom: 0; }
}

/* On the employee's page a card's heading is a label on the surface, not
   a title above it: tracked small capitals, the way the draft sets every
   secondary caption. Other pages keep their headings as headings. */
.ledger .card > .section-head > h2 {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Buttons - 44 px touch targets throughout, one clear hierarchy: the
   stamp button is filled, everything else is quiet. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  min-height: 2.75rem;
  padding: 0.6rem 1.1rem;
  font: inherit;
  font-size: 0.95rem;
  font-weight: 600;
  border-radius: 0.625rem;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  text-decoration: none;
  cursor: pointer;
  /* A customer name on a button is not ours to shorten; it wraps rather
     than pushing the card off a phone – between words, and inside a word
     only when that word alone is wider than the line. `anywhere` let a
     table or a flex row squeeze the button to one letter per line
     (D-256). */
  overflow-wrap: break-word;
  touch-action: manipulation;
  transition: background-color 120ms ease, border-color 120ms ease, transform 120ms ease;
}

.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.55; cursor: not-allowed; }

/* The lift is for a pointer only; on a touch screen `:hover` sticks
   after the tap. */
@media (hover: hover) {
  .btn:hover:not(:disabled) { transform: translateY(-1px); }
}
/* The sheet's last rule already stops every transition; only the two
   transforms are left to undo here. */
@media (prefers-reduced-motion: reduce) {
  .btn:hover:not(:disabled), .btn:active { transform: none; }
}

.btn-primary { background: var(--primary); border-color: var(--primary); color: var(--primary-text); }
/* Quiet still means a surface one step down from the card, not the card
   itself: `--sunken` and white differ by nothing on paper. */
.btn-secondary { background: var(--secondary); border-color: var(--border); color: var(--text); }
/* The ghost keeps its text colour from whatever surface it sits on, and
   its edge is drawn from that same colour so it holds on the ink card as
   well as on paper - but at a hairline's strength, not the text's. The
   plain declaration first is what an engine without color-mix keeps. */
.btn-quiet {
  background: transparent;
  color: inherit;
  border-color: currentColor;
  border-color: color-mix(in srgb, currentColor 40%, transparent);
}
@media (hover: hover) {
  .btn-quiet:hover:not(:disabled) { border-color: currentColor; }
}
/* The text on red comes from the palette, so the dark theme's lighter
   red gets dark type instead of white on pastel (2.3:1). */
.btn-danger { background: var(--danger); border-color: var(--danger); color: var(--primary-text); }
.btn-block { width: 100%; }

.btn-link {
  min-height: 2.75rem;
  padding: 0.2rem 0.4rem;
  font-size: 1rem;
  background: transparent;
  border-color: transparent;
  color: var(--primary);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/* Small in type, still a finger's worth of target. */
.btn-inline { min-height: 2.75rem; padding: 0.35rem 0.85rem; font-size: 0.92rem; }

/* The clock card is paper now, so it needs no ring of its own: --focus
   is petrol on it in the light theme and gold in the dark one. */

/* Badges - a narrow pill that names a state and does not compete with
   the figures beside it. Numbers in a badge are times too, so they are
   set in tabular numerals like everything else. It sets no colour: a
   finding badge takes its type colour from the severity class beside
   it, and a colour here would wash that away. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  min-height: 1.5rem;
  padding: 0.1rem 0.6rem;
  font-size: 0.72rem;
  font-style: normal;
  font-weight: 650;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  line-height: 1.4;
  border-radius: 999px;
  white-space: nowrap;
  vertical-align: middle;
  background: var(--secondary);
  border: 1px solid var(--border);
}

/* A message is a container as well, so it carries the same hairline and
   an inner radius rather than the card's. */
.notice {
  padding: 0.85rem 1.1rem;
  border-radius: 0.75rem;
  background: var(--notice-bg);
  color: var(--text);
  border: 1px solid var(--card-border);
}

/* Like the small window's footer: no separator, which stood alone once
   the notification button was hidden, and one left edge when it wraps
   (D-178). The button keeps its 44 px even here. */
.footer-line {
  margin: 2rem 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem 1.25rem;
}
.footer-line .btn-link { min-height: 2.75rem; padding: 0 0.1rem; border: 0; font-size: inherit; font-weight: inherit; }


body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  line-height: 1.5;
  background: var(--bg);
  color: var(--text);
  -webkit-text-size-adjust: 100%;
}

/* Every figure in this application is a time, and times are read in
   columns – so they never dance. */
time, .clock-time, .rows td, .rows th, .block-time, .stamp-net, .streak-count, .totals {
  font-variant-numeric: tabular-nums;
}

a { color: var(--primary); }

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

/* A figure and its unit are one word: "146:17 h" never breaks before
   the "h" (D-256). */
.nowrap { white-space: nowrap; }

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.logout-form { margin: 0; }

.form-card {
  max-width: 42rem;
}
/* A form that opens on the start page is as wide as the cards around it;
   its fields keep the reading width (D-256). */
.forms > .form-card { max-width: none; }
.forms > .form-card > * { max-width: 42rem; }

/* A page that is a single card – Mein Zugang, an error, the session that
   ended, the sign-in – shows it in the middle: left in the content column
   it looked pushed aside with the right third empty (D-178). `:where`
   gives the rule no weight, so a card's own width still wins; the wide
   pages break out of the column on purpose and keep doing so. */
:where(.content > .card:only-child:not(.wide)) {
  max-width: 42rem;
  margin-inline: auto;
}

/* The sign-in card is narrower still: one column of fields. */
.login-card {
  max-width: 26rem;
}

h1 { font-size: 1.5rem; line-height: 1.2; letter-spacing: -0.02em; margin: 0 0 0.5rem; }
h2 { font-size: 1.1rem; letter-spacing: -0.01em; margin: 0 0 0.5rem; }
h3 {
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--text);
  margin: 1.25rem 0 0.35rem;
}

.lede { margin: 0; color: var(--muted); max-width: 56ch; }

/* Nothing touches the element above it (D-256): a heading after the
   introduction or a list gets the air of a new section, a form under
   its introduction a line's worth, a button under its hint as well, and
   the first thing in a card starts at the card's own inset – also when
   an empty answer region stands before it. */
.card > h2:not(:first-child) { margin-top: 1.5rem; }
.card > :first-child,
.card > .flash-region:empty + * { margin-top: 0; }
.field-hint + :is(.btn, .form-actions, .inline-form) { margin-top: 1rem; }
.admin .card > form { margin-top: 1rem; }
.admin .card > form + form { margin-top: 1.5rem; padding-top: 1.25rem; border-top: 1px solid var(--border); }
.muted { color: var(--muted); }
.small { font-size: 0.85rem; }

code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }

.button-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 1rem 0;
}

/* Forms */
label { display: block; font-weight: 600; margin: 0.9rem 0 0.25rem; }

/* Every control the application uses looks the same; a type missing here
   falls back to the browser's own, much smaller, rendering. */
input[type="text"], input[type="search"], input[type="date"], input[type="time"],
input[type="number"], input[type="email"], input[type="password"], input[type="url"],
input[type="month"], input[type="datetime-local"],
select, textarea {
  width: 100%;
  min-height: 2.9rem;
  padding: 0.55rem 0.7rem;
  font: inherit;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  background: var(--surface);
  color: var(--text);
  -webkit-appearance: none;
  appearance: none;
}

input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
}

textarea { min-height: 6rem; resize: vertical; }

/* `appearance: none` took the select's arrow and left a field that read
   as a text box. The chevron is drawn from two gradients – no image
   source the CSP would have to allow – in the muted ink of either theme
   (D-256). */
select:not([multiple]) {
  padding-right: 2.25rem;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: right 1.1rem center, right 0.75rem center;
  background-size: 0.35rem 0.35rem;
  background-repeat: no-repeat;
}

/* Check boxes and radio buttons in the stamp ink and large enough to
   hit; the browser's own were 13 px and blue (D-256). */
input[type="checkbox"], input[type="radio"] {
  accent-color: var(--primary);
  width: 1.15rem;
  height: 1.15rem;
  flex: none;
}
fieldset:disabled .check-line { color: var(--muted); }

/* A field the server refused is marked where it is, not only in the
   sentence above the form (D-256). */
[aria-invalid="true"] {
  border-color: var(--danger);
  box-shadow: 0 0 0 1px var(--danger);
}

/* One look for every fold that opens in place: no browser triangle, a
   chevron that turns, a finger's height (D-256). Buttons, the header's
   "Mein Konto" and the folds with a mark of their own keep theirs. */
summary { list-style: none; cursor: pointer; }
summary::-webkit-details-marker { display: none; }
summary:not(.btn, .nav-link, .small, .clause-tip > summary, .payroll-transfers summary, .joint-split > summary, .payroll-why > summary) {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.75rem;
}
summary:not(.btn, .nav-link, .small, .clause-tip > summary, .payroll-transfers summary, .joint-split > summary, .payroll-why > summary)::before {
  content: "";
  flex: none;
  width: 0.45rem;
  height: 0.45rem;
  margin-inline: 0.15rem 0.1rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
}
details[open] > summary:not(.btn, .nav-link, .small, .clause-tip > summary, .payroll-transfers summary, .joint-split > summary, .payroll-why > summary)::before {
  transform: rotate(45deg);
}

/* Chips: a handful of choices are one tap each instead of a dropdown
   (D-137). The radio itself is hidden; the label is the target. */
.chip-field { margin: 0.9rem 0 0; padding: 0; border: 0; }
.chip-field legend { padding: 0; font-weight: 600; margin-bottom: 0.4rem; }
.chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.chip {
  display: inline-flex;
  align-items: center;
  min-height: 2.6rem;
  margin: 0;
  padding: 0.45rem 0.9rem;
  font-weight: 600;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  cursor: pointer;
}
.chip input { position: absolute; opacity: 0; width: 0; height: 0; }
.chip:has(input:checked) {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--primary-text);
}
.chip:has(input:focus-visible) { outline: 3px solid var(--focus); outline-offset: 2px; }
.chips-quiet .chip { font-weight: 500; }
.chips-quiet .chip:has(input:checked) {
  background: var(--primary-soft);
  border-color: var(--primary);
  color: var(--text);
}

/* Status and messages */
.status-line { font-size: 1.05rem; font-weight: 600; margin: 0; }

.error {
  padding: 0.75rem 1rem;
  border-radius: 0.5rem;
  background: var(--danger-bg);
  color: var(--danger);
  border: 1px solid var(--danger);
}

/* Warning levels of the labour-law checks */
.warn-info { background: var(--info-bg); color: var(--info); border-left: 4px solid var(--info); padding: 0.5rem 0.75rem; border-radius: 0.25rem; }
.warn-warning { background: var(--warning-bg); color: var(--warning); border-left: 4px solid var(--warning); padding: 0.5rem 0.75rem; border-radius: 0.25rem; }
.warn-critical { background: var(--critical-bg); color: var(--critical); border-left: 4px solid var(--critical); padding: 0.5rem 0.75rem; border-radius: 0.25rem; font-weight: 600; }
/* A badge that carries a severity stays a pill: the block of a finding
   – its padding, its 4 px bar, its square corner – turned every finding
   badge in a table into a box two lines high (D-256). */
.badge:is(.warn-info, .warn-warning, .warn-critical) {
  padding: 0.1rem 0.6rem;
  border: 1px solid currentColor;
  border-radius: 999px;
}

/* Entries waiting in the outbox */
.pending { font-style: italic; color: var(--muted); }

/* Day view */
.day-list { list-style: none; margin: 0; padding: 0; }
.day-list li { display: flex; justify-content: space-between; gap: 0.75rem; padding: 0.5rem 0; border-bottom: 1px solid var(--border); }
.day-list li:last-child { border-bottom: 0; }

.error-card h1 { color: var(--danger); }
/* The reference is one token to read out on the phone; it does not
   break (D-256). */
.error-card code { white-space: nowrap; }

/* The one sign-in page (D-176): Microsoft first, a quiet line, the
   password form, and the link by mail folded away under its question.
   Only the heading is centred; messages and labelled fields are read
   line by line and stay left-aligned. */
.login-card h1 { text-align: center; margin-bottom: 1rem; }
.login-form { margin: 0; }
.login-form label { display: block; font-size: 0.85rem; margin: 0.6rem 0 0.15rem; }
.login-form input { width: 100%; min-height: 2.75rem; padding: 0.5rem 0.7rem; font: inherit; border: 1px solid var(--border); border-radius: 0.5rem; background: var(--surface); color: var(--text); }
.login-form .btn { margin-top: 0.8rem; }
.login-divider { display: flex; align-items: center; gap: 0.75rem; margin: 1.5rem 0 0.5rem; font-size: 0.85rem; color: var(--muted); }
.login-divider::before,
.login-divider::after { content: ""; flex: 1; border-top: 1px solid var(--border); }
.login-forgot { margin: 1.25rem 0 0; }
.login-forgot > summary { cursor: pointer; color: var(--primary); font-size: 0.9rem; }
.login-forgot .login-form { margin-top: 0.5rem; }
.login-address { margin: 0 0 1.25rem; }
.login-address .btn { margin-top: 0.5rem; }
/* At the wrong address every other way on the page fails silently (the
   Secure cookie is dropped), so the switch button is left as the only
   one. app.js sets the class; without the script nothing is hidden. */
.login-card.address-wrong > :not(h1, .login-address) { display: none; }

/* ------------------------------------------------------------------ */
/* The small window (D-165)                                            */
/* ------------------------------------------------------------------ */
/* Installed beside the work it measures, often 320 px wide and always
   read out of the corner of an eye: one figure, one band, one deadline,
   one button. Everything it cannot do is a link to the page that can. */

body.mini { background: var(--bg); color: var(--text); min-height: 100vh; }
.mini-stage { max-width: 26rem; margin: 0 auto; padding: 0.9rem 0.9rem 1.25rem; }
.mini-state { display: grid; gap: 0.2rem; }
.mini-figure { margin: 0; display: flex; align-items: baseline; gap: 0.2rem; }
.mini-hm { font-size: 3rem; font-weight: 700; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; line-height: 1; }
.mini-unit { font-size: 1.1rem; color: var(--muted); }
.mini-where { margin: 0; font-weight: 600; overflow-wrap: break-word; }
.mini-since, .mini-goal { margin: 0; font-size: 0.85rem; color: var(--muted); }
.mini-band { width: 100%; height: 0.6rem; margin: 0.5rem 0 0.15rem; }
.mini-count {
  margin: 0.6rem 0 0;
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  font-size: 0.95rem;
  font-weight: 600;
  padding: 0.45rem 0.6rem;
  border-radius: 0.5rem;
  border-left: 4px solid currentColor;
  background: var(--sunken);
}
.mini-count-time { font-variant-numeric: tabular-nums; }
.mini-count.is-passed { font-weight: 700; }
.mini-activity, .mini-note { margin: 0.35rem 0 0; font-size: 0.82rem; color: var(--muted); overflow-wrap: break-word; }
.mini-actions { display: grid; gap: 0.5rem; margin: 0.9rem 0 0; }
.mini-actions form { display: contents; }
.mini-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 3.25rem;
  font: inherit;
  font-size: 1.05rem;
  font-weight: 700;
  border: 1px solid transparent;
  border-radius: 0.6rem;
  cursor: pointer;
  text-decoration: none;
  overflow-wrap: break-word;
  text-align: center;
}
/* The small window books like the start page: the gold stamp button,
   and "Gehen" as a quiet button in every state, never red (D-256). */
.mini-btn-go { background: var(--brass); border-color: var(--brass); color: var(--ink); }
.mini-btn-quiet { background: var(--surface); color: var(--text); border-color: var(--border); }
/* Two items that wrap to the same left edge. No separator character: it
   stood alone once the permission button was hidden (D-178). */
.mini-foot { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.35rem 1.25rem; margin: 1rem 0 0; font-size: 0.82rem; color: var(--muted); }
.mini-foot .btn-link { min-height: 0; padding: 0; border: 0; font-size: inherit; font-weight: inherit; }
/* The small window lies on the page ground, not on the dark clock card:
   the card's ink track read as a full dark bar at 0:00. The page's own
   sunken tone with a border reads as empty in both themes; the fill keeps
   the band's colours. */
.band.mini-band { background: var(--sunken); border: 1px solid var(--border); }
.band.mini-band::-webkit-progress-bar { background: var(--sunken); }

/* ------------------------------------------------------------------ */
/* The terminal on the wall (D-159)                                    */
/* ------------------------------------------------------------------ */
/* Read from two metres away, tapped with a work glove: everything here
   is larger than anywhere else in the application, and nothing on the
   screen is optional. The page asks for the dark theme itself because it
   hangs in a workshop, not in a browser tab – and it is drawn from the
   same petrol and gold as every other page, not from a navy and a blue
   of its own (D-256). The booking the door offers first – Kommen or
   Gehen – is the gold of the stamp button, not a green or a red alarm. */

body.terminal {
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
}
.term-stage {
  max-width: 60rem;
  margin: 0 auto;
  padding: 1.5rem 1.25rem 3rem;
}
.term-head { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; margin-bottom: 1.5rem; }
.term-clock { font-size: 2.6rem; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.term-place { font-size: 1rem; color: var(--muted); }
.term-ask { font-size: 1.4rem; margin: 0 0 1rem; font-weight: 600; }
.term-lede { font-size: 1.05rem; color: var(--muted); margin: 0 0 1rem; }

.term-grid { list-style: none; margin: 0 0 1.25rem; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); gap: 0.85rem; }
.term-tile {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.5rem;
  min-height: 8rem; padding: 1rem 0.6rem; text-align: center;
  background: var(--surface); border: 1px solid var(--card-border); border-radius: 1rem;
  color: inherit; text-decoration: none;
}
.term-tile:active { background: var(--secondary); }
.term-tile:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
/* The initials as in the header's module: a ring of champagne, the
   letters in the earned ink. */
.term-initials {
  display: flex; align-items: center; justify-content: center;
  width: 3.2rem; height: 3.2rem; border-radius: 50%;
  border: 1px solid var(--card-border); background: var(--sunken);
  color: var(--brass-ink); font-size: 1.3rem; font-weight: 700; letter-spacing: 0.02em;
}
.term-name { font-size: 1.05rem; font-weight: 600; line-height: 1.25; }

.term-panel { max-width: 30rem; margin: 0 auto; }
.term-panel h1 { font-size: 1.9rem; margin: 0 0 0.4rem; }
.term-state { font-size: 1.15rem; color: var(--muted); margin: 0 0 1.25rem; }
.term-panel label { display: block; font-size: 1rem; color: var(--muted); margin: 1rem 0 0.35rem; }
.term-panel input {
  width: 100%; min-height: 3.75rem; padding: 0.6rem 0.9rem;
  font: inherit; font-size: 1.5rem; letter-spacing: 0.06em;
  background: var(--sunken); color: inherit; border: 1px solid var(--card-border); border-radius: 0.7rem;
}
.term-panel input:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.term-btn {
  display: block; width: 100%; min-height: 4.25rem; margin-top: 1.25rem;
  font: inherit; font-size: 1.45rem; font-weight: 700;
  border: 1px solid transparent; border-radius: 0.8rem; cursor: pointer; text-align: center;
  text-decoration: none; line-height: 4.25rem;
}
.term-btn:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.term-btn-go,
.term-btn-stop { background: var(--brass); border-color: var(--brass); color: var(--ink); }
.term-btn-quiet { background: var(--secondary); border-color: var(--card-border); color: var(--text); }
.term-what { margin: 1.5rem 0 0.5rem; font-weight: 600; color: var(--text); }
/* The door offers up to five bookings; the first is the filled one, the
   rest wait quietly beside it (D-164). */
.term-choices { display: grid; gap: 0.7rem; }
.term-choices .term-btn { margin-top: 0; }
.term-problem {
  background: var(--danger-bg); border: 1px solid var(--danger); border-radius: 0.6rem;
  color: var(--text);
  padding: 0.75rem 0.9rem; margin: 0 0 1rem; font-size: 1.05rem;
}
.term-back-line { margin: 0 0 1rem; }
/* A way back a gloved finger can hit. */
.term-back {
  display: inline-flex; align-items: center; min-height: 3rem; padding: 0 1rem;
  border: 1px solid var(--card-border); border-radius: 0.7rem;
  color: var(--text); font-size: 1.05rem; font-weight: 600; text-decoration: none;
}
.term-done { text-align: center; padding-top: 2rem; }
.term-tick { font-size: 4.5rem; line-height: 1; margin: 0 0 0.75rem; color: var(--moss); }

/* ------------------------------------------------------------------ */
/* Booking UI (spec §6)                                                */
/* ------------------------------------------------------------------ */

.hidden { display: none !important; }

/* ------------------------------------------------------------------ */
/* The clock card (D-137): the one loud element of the application     */
/* ------------------------------------------------------------------ */
/* The draft settles what this card is made of: in the light state its
   ink token is #FFFFFF and its ink line is the champagne hairline, in
   the dark state #0C2B33 and a warmer champagne - "im Hellen dieselbe
   weiße Karte wie alle anderen". So the card stops carrying its own
   permanent night and follows --surface / --text / --muted /
   --card-border, which hold exactly those values in both themes. Gold
   appears here as line, ring, point and as a surface with petrol on it,
   never as a letter on paper. */

.clock-card {
  position: relative;
  margin-bottom: 1rem;
  /* 8 px raster: 16 px of air on a phone, 24 px once there is room. */
  padding: 1rem;
  border: 1px solid var(--card-border);
  border-radius: 1rem;
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow);
}

/* The 60 s poll failed: the last known state stays, visibly aged. */
.clock-card.state-stale, .state-stack.state-stale { opacity: 0.55; }

.clock-top { display: grid; gap: 1rem; }

/* A long sentence or a long customer name must not push the card off a
   390 px screen; a grid track is auto-sized by its widest item. */
.clock-figure, .clock-actions { min-width: 0; }

/* Gesperrte Kleinversalien: everything that only names a figure. The
   figure itself carries the weight. */
.clock-label {
  margin: 0;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The largest thing on the page, but not the heaviest: the draft sets
   the clock in 500, where 700 would shout, and the tabular figures
   (set globally) keep the columns still while the minutes run. */
.clock-time {
  display: flex;
  align-items: baseline;
  gap: 0.35rem;
  margin: 0.5rem 0 1rem;
  font-size: clamp(2.75rem, 12vw, 4.75rem);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.04em;
}
.clock-unit { font-size: 0.28em; font-weight: 500; color: var(--muted); letter-spacing: 0; }

/* The state mark. The draft sets it beside the clock as a small tag with
   a dot; the sentence here is a whole sentence, so the tag keeps the
   inner radius (12 px) and wraps instead of being a pill that breaks. */
.clock-card .status-line {
  display: inline-flex;
  align-items: flex-start;
  gap: 0.5rem;
  max-width: 100%;
  margin: 0;
  padding: 0.35rem 0.75rem;
  border-radius: 0.75rem;
  background: var(--secondary);
  color: var(--text);
  font-size: 1rem;
  font-weight: 600;
  overflow-wrap: break-word;
}
/* The point. The draft draws it in currentColor and says why: the
   running mark carries meaning, so it has to be there without colour
   vision as well. On --secondary that is 12:1; gold would be 1.8:1. */
.clock-card .status-line::before {
  content: "";
  flex: 0 0 auto;
  width: 0.5rem;
  height: 0.5rem;
  margin-top: 0.45em;
  border-radius: 999px;
  background: currentColor;
  /* The gold ring the draft puts around the running point. */
  box-shadow: 0 0 0 3px var(--brass-soft);
}

/* Die Frage, die jeder Arbeitstag stellt (D-150). */
.finish-line {
  margin: 0.75rem 0 0;
  font-size: 0.85rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  overflow-wrap: break-word;
}
.clock-card .state-offline { margin: 0.5rem 0 0; }
.clock-card .muted { color: var(--muted); }

.clock-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
.clock-direct { margin: 0; }

/* One filled button per card, and it is the gold one: a surface of gold
   with petrol on it. Gold never carries the letters the other way round
   - on this paper it would sit at 2.1:1. --ink is the near-black both
   themes put on gold (the draft's goldText, #092229 in both). */
.clock-card .btn-primary {
  background: var(--brass);
  border-color: var(--brass);
  color: var(--ink);
}
/* Everything else is quiet: paper, the champagne hairline the draft puts
   on its secondary buttons, the card's own text colour. Without this the
   global .btn-quiet draws its border in currentColor and fences every
   secondary action in full-strength petrol. */
.clock-card .btn-quiet {
  background: transparent;
  border-color: var(--card-border);
  color: var(--text);
  opacity: 1;
}
/* A link inside the card is still a link, not a gold word - and it is
   tapped all day, so it keeps the 44 px the global .btn-link gives up. */
.clock-card .btn-link { color: var(--primary); min-height: 2.75rem; }

/* The draft's two button sizes: 52 px for the booking button, 44 px for
   everything smaller. The global .btn-inline is 35 px, which is below
   the touch target this card is tapped with all day. */
.btn-clock { min-height: 3.25rem; padding-inline: 1.25rem; font-size: 1.05rem; }
.clock-card .btn { border-radius: 0.75rem; }
.clock-card .btn-inline { min-height: 2.75rem; border-radius: 0.625rem; }

@media (hover: hover) {
  /* The champagne line is what lights up under the pointer - the surface
     barely moves. */
  .clock-card .btn-quiet:hover { background: var(--brass-soft); }
  .clock-card .btn-primary:hover { filter: brightness(1.07); }
}
@media (prefers-reduced-motion: no-preference) {
  .clock-card .btn { transition: background-color 120ms ease, border-color 120ms ease, filter 120ms ease; }
}

/* Die Tür zum kleinen Fenster (D-165): ruhig, aber auf der Karte, auf die
   jeder Arbeitstag zuerst schaut. */
.mini-door { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem; margin: 1.5rem 0 0; }
.mini-door .small { color: var(--muted); max-width: 46ch; }

/* The band: the day measured against the law. Ticks sit at 9:30 and
   11:00 of the 12 h scale (79.2 %, 91.7 %). */
.band-wrap {
  position: relative;
  margin: 1.5rem 0 0;
  /* A length, not a colour: the bar, the tick layer and the goal mark
     must be the same box or the ticks float. Everything here is
     border-box (set globally), so 10 px outside is an 8 px channel -
     the draft's bar height. */
  --band-height: 0.625rem;
}

.band {
  display: block;
  width: 100%;
  /* The channel is a container like any other, so the champagne hairline
     holds it: the draft tints its own track (#EFE7CD) and app.css has no
     such tone, and --secondary alone sits at 1.19:1 on paper. What the
     band means is carried by the fill, at 13.9:1. */
  border: 1px solid var(--card-border);
  border-radius: 999px;
  background: var(--secondary);
  -webkit-appearance: none;
  appearance: none;
  overflow: hidden;
}
/* Only the band in this card takes the card's height; the small window
   keeps the one .mini-band asks for. */
.band-wrap .band { height: var(--band-height, 0.625rem); }
/* "Gold auf Weiß trägt keine Fläche - gefüllt wird mit der Tinte; Gold
   bleibt Linie, Ring und Knopf." That is the draft's `bar`: #092229 in
   the light state, #FAD25F in the dark one - which is --brass-ink. */
.band::-webkit-progress-bar { background: var(--secondary); border-radius: 999px; }
.band::-webkit-progress-value { background: var(--brass-ink); border-radius: 999px; }
.band::-moz-progress-bar { background: var(--brass-ink); border-radius: 999px; }
/* Approaching 9:30 is a warning, so it takes the warning tone. Gold here
   would be 1.8:1 on the track in the light theme - the one step that has
   to be more noticeable than the normal fill would be the least. */
.band-approaching::-webkit-progress-value { background: var(--warning); }
.band-approaching::-moz-progress-bar { background: var(--warning); }
/* Past 11 h the bar turns to the signal colour of the theme; the old
   fixed #ff8a80 was a pale pink on white. */
.band-warning::-webkit-progress-value,
.band-exceeded::-webkit-progress-value { background: var(--critical); }
.band-warning::-moz-progress-bar,
.band-exceeded::-moz-progress-bar { background: var(--critical); }

/* Inset by the band's own hairline, so the ticks stand where the fill
   ends and not a pixel beside it. */
.band-wrap::after {
  content: "";
  position: absolute;
  left: 1px;
  right: 1px;
  top: 0;
  height: var(--band-height, 0.625rem);
  border-radius: 999px;
  pointer-events: none;
  background:
    linear-gradient(to right, transparent calc(79.16% - 1px), var(--muted) calc(79.16% - 1px), var(--muted) calc(79.16% + 1px), transparent calc(79.16% + 1px)),
    linear-gradient(to right, transparent calc(91.66% - 1px), var(--muted) calc(91.66% - 1px), var(--muted) calc(91.66% + 1px), transparent calc(91.66% + 1px));
}

/* The contracted day is not a fixed share of the scale, and an inline
   style is out (CSP) - so the mark is a second, transparent progress
   element whose right edge is the tick (D-141). The transparent hairline
   gives it the same content box as .band, so mark and fill share one
   coordinate system. */
.band-mark {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: var(--band-height, 0.625rem);
  border: 1px solid transparent;
  background: transparent;
  pointer-events: none;
  -webkit-appearance: none;
  appearance: none;
}
.band-mark::-webkit-progress-bar { background: transparent; }
/* Gold line, and a two-pixel gap of paper in front of it: in the dark
   theme the fill is gold too, and a gold mark on a gold fill is no mark
   at all. */
.band-mark::-webkit-progress-value {
  background: linear-gradient(to right,
    transparent calc(100% - 5px),
    var(--surface) calc(100% - 5px), var(--surface) calc(100% - 3px),
    var(--brass) calc(100% - 3px));
}
.band-mark::-moz-progress-bar {
  background: linear-gradient(to right,
    transparent calc(100% - 5px),
    var(--surface) calc(100% - 5px), var(--surface) calc(100% - 3px),
    var(--brass) calc(100% - 3px));
}

/* The scale names a figure with its unit, so it keeps its case: set in
   capitals it read "TAGESZIEL 7:42 H" (D-256). Each label stays whole. */
.band-scale {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.25rem 1rem;
  margin: 0.5rem 0 0;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.band-scale > span { white-space: nowrap; }
/* Reached: gold as a ring around a gold surface with petrol on it,
   never gold as a word. */
.band-goal.is-reached {
  padding: 0.05rem 0.5rem;
  border: 1px solid var(--brass);
  border-radius: 999px;
  background: var(--brass-soft);
  color: var(--brass-ink);
}

.clock-card .hint { margin: 1rem 0 0; }
/* The card's own links start at its edge like its text, one gap apart. */
.clock-card > .link-row { display: flex; flex-wrap: wrap; gap: 0 1.25rem; }
.clock-card > .link-row .btn-link { padding-inline: 0; }
/* A link inside a sentence keeps the sentence's lines even: the 44 px of
   a free-standing link pushed its line 10 px apart from the others. */
p > .btn-link.btn-inline { min-height: 0; padding: 0; vertical-align: baseline; }

/* Running project blocks (D-105): one row per customer, each with its own
   switch and end action. Every row in this list is a running clock, and
   the draft marks a running row the same way every time: a gold ring, a
   gold-soft surface, and the ink point with its gold halo. That is where
   the gold of this page sits - line, ring, point, surface - and the
   letters on it stay petrol. */
.block-heading {
  margin: 1.5rem 0 0.5rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.block-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.block-row {
  display: grid;
  /* Narrow screens give the customer its own full line: a wrapped name
     beside two buttons is unreadable. */
  grid-template-columns: 1fr;
  gap: 0.25rem 0.75rem;
  align-items: center;
  min-height: 2.75rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--brass);
  border-radius: 0.75rem;
  background: var(--brass-soft);
}
/* "Es läuft kein Kunde" is not a running row and must not wear its ring. */
.block-row.block-empty { border-color: var(--border); background: transparent; }
.block-label { font-weight: 600; overflow-wrap: break-word; }
/* The point sits inside the label, not as a grid item: a ::before on the
   row itself would become a track of its own and shift every column. */
.block-label::before {
  content: "";
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  margin-inline-end: 0.6rem;
  border-radius: 999px;
  background: var(--brass-ink);
  vertical-align: 0.1em;
}
.block-time { grid-column: 1; font-size: 0.85rem; color: var(--muted); }
.block-time b { color: var(--text); font-weight: 600; }
/* What the one-tap "Gehen" will book (D-148). It stands here rather than
   on the button because it belongs to the customer, not to the action. */
.block-activity { grid-column: 1; font-size: 0.85rem; color: var(--muted); overflow-wrap: break-word; }
.block-actions { grid-column: 1; display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.25rem; }
.block-empty { display: block; color: var(--muted); }
/* On the tinted row a transparent button would lose its edge, so the
   controls take the card's paper back. */
.block-row .btn-quiet { background: var(--surface); border-color: var(--card-border); }
@media (hover: hover) {
  .block-row .btn-quiet:hover { background: var(--sunken); }
}
/* Ein Tipp startet den nächsten Kunden (D-149). */
.start-choices { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin: 1rem 0 0; }
.start-label {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

@media (min-width: 30rem) {
  .clock-card { padding: 1.5rem; }
  .btn-clock { padding-inline: 1.6rem; }
}
/* On a phone the card's buttons are one column of one width, not four
   widths flush left (D-256). */
@media (max-width: 30rem) {
  .clock-actions { flex-direction: column; align-items: stretch; }
  .clock-actions > .btn,
  .clock-direct,
  .clock-direct > .btn { width: 100%; }
  .auto-end-line .link-row .btn { flex: 1 1 100%; }
}

/* The buttons beside the clock from 56 rem on, under it below. They
   keep to the right half at most; the clock needs its room even when four
   actions are open at once – but a stamp button with a customer on it
   stays on one line (D-256). Beside each other from 44 rem, the 30 rem of
   buttons left the clock 150 px on a tablet, and "Seit 07:45 · 2 Kunden
   aktiv · Pause 0:30" stood in four lines. */
@media (min-width: 56rem) {
  .clock-top { grid-template-columns: minmax(0, 1fr) auto; align-items: end; gap: 1.5rem; }
  .clock-actions { justify-content: flex-end; max-width: 30rem; }
}
@media (min-width: 44rem) {
  /* A running customer is one line – name, time, share – and its
     activity the line under it. Left to place themselves, the buttons
     fell two lines below the name beside an empty column (D-256). On a
     tablet the buttons take a line of their own at the right, so the
     name keeps the width it needs. */
  .block-row { grid-template-columns: minmax(0, 1fr) auto minmax(4rem, 9rem); }
  .block-label, .block-time, .block-share { grid-row: 1; }
  .block-time { grid-column: 2; white-space: nowrap; }
  .block-share { grid-column: 3; margin: 0; }
  .block-activity { grid-column: 1 / -1; grid-row: 2; }
  .block-actions { grid-column: 1 / -1; justify-content: flex-end; margin-top: 0; flex-wrap: nowrap; }
}
@media (min-width: 62rem) {
  .block-row { grid-template-columns: minmax(0, 1fr) auto minmax(4rem, 9rem) auto; }
  .block-actions { grid-column: 4; grid-row: 1; }
}


/* ------------------------------------------------------------------ */
/* Laufende Arbeiten: the panel beside the main clock                  */
/* ------------------------------------------------------------------ */
/* The draft gives the running customers a board of their own between
   the clock and the day: attendance is the one main clock, every
   customer has a clock beside it, and two of them may run at once
   (D-121). The board is an ordinary card, so it carries the same
   champagne hairline as every other container. */
.state-stack { display: block; }
/* One rhythm between the stacked blocks of the start page – clock,
   customers, notices, confirmation: the card's own 24 px, where 16 and 24
   took turns (D-256). */
.state-stack > .clock-card { margin-bottom: 1.5rem; }

.works-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem 0.75rem;
  margin-bottom: 0.75rem;
}
.works-head h2 {
  margin: 0;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
/* What the customers add up to, against the attendance they run in. */
.works-sums {
  margin: 0;
  font-size: 0.85rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.works-sums b { color: var(--text); font-weight: 600; }
/* Two clocks at once are allowed, so the difference is named rather
   than flagged: a gold ring around the figure, no warning colour. */
.works-parallel {
  border-color: var(--brass);
  background: var(--brass-soft);
  color: var(--brass-ink);
}
/* The sentence that answers the question the board raises. It takes the
   whole line so it never squeezes between the sums and the chip. */
.works-hint { flex: 1 0 100%; margin: 0; max-width: none; }

/* How much of the attendance this customer has. A `progress` carries the
   value in the markup, so the bar needs no inline width – which the
   content security policy would refuse anyway. */
.block-share {
  display: block;
  width: 100%;
  height: 0.375rem;
  margin: 0.15rem 0 0.1rem;
  border: 0;
  border-radius: 999px;
  background: var(--secondary);
  -webkit-appearance: none;
  appearance: none;
  overflow: hidden;
}
.block-share::-webkit-progress-bar { background: var(--secondary); border-radius: 999px; }
.block-share::-webkit-progress-value { background: var(--brass-ink); border-radius: 999px; }
.block-share::-moz-progress-bar { background: var(--brass-ink); border-radius: 999px; }

.works-foot {
  display: grid;
  gap: 0.5rem;
  margin-top: 1rem;
  padding-top: 0.75rem;
  border-top: 1px dashed var(--border);
}
.works-foot p { margin: 0; }
.works-foot .start-choices { margin: 0; }

/* The switch form repeats which customer is being left. */
.block-target { margin: 0 0 0.5rem; padding: 0.5rem 0.75rem; border-left: 4px solid var(--primary); border-radius: 0.25rem; background: var(--primary-soft); font-weight: 600; }
.block-fieldset { margin: 0.75rem 0 0; padding: 0.25rem 0.75rem 0.75rem; border: 1px solid var(--border); border-radius: 0.5rem; }
.block-fieldset legend { font-weight: 600; padding: 0 0.25rem; }
/* „Wann sind Sie gegangen?“ (D-252): day and time of the end as one group,
   framed like the customers below it. A fieldset is as wide as its widest
   content unless told otherwise; the chips wrap instead. */
.go-when { margin: 0.75rem 0 0; padding: 0.25rem 0.75rem 0.75rem; border: 1px solid var(--border); border-radius: 0.5rem; min-width: 0; }
.go-when > legend { font-weight: 600; padding: 0 0.25rem; }
/* The title of a framed group stands inside its frame. As the browser
   draws it, in the frame's top line, a title that wraps cut that line
   with its second row and floated its first above the box (D-256). */
.block-fieldset, .go-when { padding-top: 0.75rem; }
.block-fieldset > legend, .go-when > legend { float: left; width: 100%; padding: 0; margin: 0 0 0.35rem; }
.block-fieldset > legend + *, .go-when > legend + * { clear: both; }


/* Findings */
.hint { margin: 0.75rem 0; padding: 0.6rem 0.75rem; border-radius: 0.5rem; font-weight: 600; }
.link-row { margin: 0.5rem 0 0; }
.warn-list { list-style: none; margin: 0.9rem 0 0; padding: 0; display: grid; gap: 0.35rem; }
.warn-item { margin: 0; font-size: 0.92rem; }
.warn-details { font-weight: 400; }
.sync-notice { margin: 0.75rem 0 0; font-size: 0.92rem; }

/* A finding badge takes its surface and edge from the severity beside
   it; the generic .badge would otherwise wash both away, and on a narrow
   screen the badge is the only signal left. */
.badge-finding.warn-info { background: var(--info-bg); border-color: var(--info); color: var(--info); }
.badge-finding.warn-warning { background: var(--warning-bg); border-color: var(--warning); color: var(--warning); }
.badge-finding.warn-critical { background: var(--critical-bg); border-color: var(--critical); color: var(--critical); }
.badge-pending { background: var(--pending-bg); color: var(--pending); border-color: var(--pending); }
.badge-auto { background: var(--info-bg); color: var(--info); border-color: var(--info); }
.badge-running { background: var(--moss-soft); color: var(--moss); border-color: var(--moss); }
.badge-deleted { background: var(--danger-bg); color: var(--danger); border-color: var(--danger); }
.badge-absence { background: var(--secondary); color: var(--text); border-color: var(--border); margin-right: 0.25rem; }
/* "Gemeinsam mit Bea Wolf, Carl Huber" is a sentence: as one unbreakable
   pill it pushed its cell, and the page, past a phone's edge (D-256). */
.badge-joint { background: var(--primary-soft); color: var(--text); border-color: var(--border); white-space: normal; text-align: left; }

.strike td { text-decoration: line-through; color: var(--muted); }
.strike td .badge { text-decoration: none; }

/* Flash and forms region */
.flash:empty, .forms:empty { display: none; }
/* Die Bestätigung trägt das Rückgängig neben sich, nicht darunter. */
.flash .notice {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: 0.5rem 1rem;
}
/* The confirmation, its undo and how long the undo lasts read as one
   sentence; a further action stands apart at the right (D-256). */
.flash .notice > .btn:last-child:not(:nth-child(2)) { margin-left: auto; }
.flash .notice .btn-link { padding: 0; }
.flash .error, .flash .notice { margin: 0 0 1.5rem; }

.form-card { border-left: 4px solid var(--primary); }
.form-card h2 { margin-top: 0; }

.form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: 1.25rem;
}

.field-hint { margin: 0.3rem 0 0; font-size: 0.85rem; color: var(--muted); }

.check-row { display: flex; align-items: center; gap: 0.5rem; margin: 0.75rem 0 0.25rem; }
.check-row label { display: inline; margin: 0; }
.check-row input[type="checkbox"] { width: 1.4rem; height: 1.4rem; margin: 0; }

.two-col { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0 0.75rem; }

/* Two fields side by side share the row equally, whatever their labels
   say; in the side column they stand one under the other (D-256). */
@media (min-width: 30rem) {
  .two-col { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ------------------------------------------------------------------ */
/* The right column: punch card and week (D-138)                       */
/* ------------------------------------------------------------------ */

/* Both panels are containers in the sense of the drawing: they keep the
   champagne hairline .card already gives them, take the outer radius of
   16 px and a soft two-step shadow that lifts a panel off the paper
   without drawing a second frame around it. */
.stamp-card,
.week-view {
  border-radius: 1rem;
  box-shadow: var(--shadow);
}

/* Every figure in the column is read against the one above it, so the
   lines the global rule does not already cover get tabular figures too. */
.stamp-week,
.stamp-sums,
.board .day-title,
.week-view .plain-list,
.stamp-badge .badge-hint {
  font-variant-numeric: tabular-nums;
}

/* A panel heading is a label, not a title: small letter-spaced capitals
   in silver, with the figure it belongs to set against it on the right.
   That pairing – word left, number right – is what makes the column read
   as one instrument rather than as four stacked cards. */
.stamp-card .section-head h2,
.week-view .section-head h2,
.board .section-head h2 {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.stamp-card .section-head,
.week-view .section-head,
.board .section-head,
.leave-card .section-head,
.flex-card .section-head {
  align-items: center;
  gap: 0.25rem 0.5rem;
  margin-bottom: 0.75rem;
}
.stamp-card .stamp-week,
.board .day-title {
  margin: 0 0 0 auto;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text);
}
/* The week steps with the shared period control (.period-nav): the
   arrows sit beside the title on the heading's line, where they used to
   take a line of their own and leave a hole beside them (D-256). */
.week-view .period-title { font-size: 0.85rem; }
/* Where the card is too narrow for the heading and the arrows on one
   line – the side column, a phone – the arrows take the next line whole,
   the period centred between them, instead of hanging at its right with
   a hole beside them (D-256). */
.week-view { container: week / inline-size; }
@container week (max-width: 27rem) {
  .week-view .period-nav { flex: 1 0 100%; justify-content: space-between; margin-left: 0; }
}

/* ---- The stamp row: the week's rhythm, seven columns wide ---------- */

/* The row ends on a hairline: it is the axis the days stand on, the same
   line the drawing puts under its bar labels. The days carry the space
   above it themselves, so today's gold foot lands exactly on the axis
   instead of floating above it. */
.stamp-row {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 0.25rem;
  margin: 0;
  padding: 0;
  border-bottom: 1px solid var(--border);
  list-style: none;
}

/* On the narrowest phone the card's own inset leaves each of the seven
   days about 40 px, under the 44 px a thumb needs. The row takes half of
   that inset back – the axis then runs the full width of the card, which
   is how the drawing sets it anyway. */
@media (max-width: 26rem) {
  .stamp-row { margin-inline: -0.5rem; }
}

@media (min-width: 26rem) {
  .stamp-row { gap: 0.5rem; }
}

/* A day is a column, and it keeps the bar's own corner: rounded at the
   head, nearly square at the foot where it meets the axis. */
.stamp {
  display: grid;
  align-content: center;
  gap: 0.3rem;
  min-height: 2.75rem;
  padding: 0.5rem 0.1rem 0.6rem;
  text-align: center;
  border: 0;
  border-radius: 0.375rem 0.375rem 0.25rem 0.25rem;
  background: transparent;
  transition: background-color 120ms ease;
}

/* Where a day is linkable the link is the whole column, so the thumb
   hits the day and not the three letters of its name. The padding moves
   into the link rather than being cancelled with negative margins, which
   kept mis-measuring the hit area by the row gap. */
.stamp:has(> a) {
  padding: 0;
  align-content: stretch;
}
.stamp > a {
  display: grid;
  align-content: center;
  gap: 0.3rem;
  min-height: 2.75rem;
  padding: 0.5rem 0.1rem 0.6rem;
  border-radius: inherit;
  text-decoration: none;
  color: inherit;
}
.stamp-day {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.stamp-net {
  font-size: 0.7rem;
  color: var(--muted);
}

/* The mark is the stamp itself: an open ring while the day still owes
   something, a filled dot once it is settled. It is drawn in silver, not
   in the hairline colour – a --border ring on --surface is about 1.1:1
   and simply is not there. Gold fills only the dot of a day that has
   been earned; everything else is ink or silver. */
.stamp-mark {
  justify-self: center;
  width: 0.875rem;
  height: 0.875rem;
  border-radius: 50%;
  border: 2px solid var(--muted);
}

/* ---- The week's bars ---------------------------------------------- */

/* Wochentempo: how much each day carried, as a column standing on the
   axis. A `progress` holds its value in the markup, so no bar needs an
   inline height – which the content security policy would refuse. The
   element is drawn lying down and turned upright; its fill then grows
   from the axis up. */
.stamp-bar-box {
  position: relative;
  display: block;
  /* The column's height, and the length of the bar once it is upright. */
  height: 2.75rem;
}
.stamp-bar,
.stamp-goal {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 2.75rem;
  border: 0;
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
  /* Turned to the left, the bar's start edge lands on the axis, so the
     fill grows upwards out of it. */
  transform: translate(-50%, -50%) rotate(-90deg);
  pointer-events: none;
}
/* Every day stands on a track of its own, so an empty day reads as an
   empty column and the goal's dash marks a height on something instead
   of floating in the air (D-256). */
.stamp-bar { height: 0.75rem; border-radius: 0.125rem; overflow: hidden; background: var(--secondary); }
.stamp-bar::-webkit-progress-bar { background: var(--secondary); }
.stamp-bar::-webkit-progress-value { background: var(--muted); }
.stamp-bar::-moz-progress-bar { background: var(--muted); }

/* The contracted day, drawn the way the clock band draws it (D-141): a
   second, transparent bar whose far edge is the mark. */
.stamp-goal { height: 1.5rem; }
.stamp-goal::-webkit-progress-bar { background: transparent; }
.stamp-goal::-webkit-progress-value { background: transparent; border-right: 1px dashed var(--brass); }
.stamp-goal::-moz-progress-bar { background: transparent; border-right: 1px dashed var(--brass); }
/* A day the calendar never asked for, or one away, owes no goal. */
.stamp-free .stamp-goal,
.stamp-absent .stamp-goal { display: none; }

/* A settled day fills in gold, the day in hand in the earned ink; every
   other day stays silver, so the week reads at a glance. */
.stamp-complete .stamp-bar::-webkit-progress-value { background: var(--brass); }
.stamp-complete .stamp-bar::-moz-progress-bar { background: var(--brass); }
.stamp.is-today .stamp-bar::-webkit-progress-value { background: var(--brass-ink); }
.stamp.is-today .stamp-bar::-moz-progress-bar { background: var(--brass-ink); }

/* Settled: the one gold in the row. The tint is a surface, the writing
   on it stays ink, so gold never carries a letter on light paper. */
.stamp-complete { background: var(--brass-soft); }
.stamp-complete .stamp-mark { background: var(--brass); border-color: var(--brass); }
.stamp-complete .stamp-day { color: var(--text); }
.stamp-complete .stamp-net { color: var(--text); font-weight: 600; }

/* Running: the day in hand. --brass-ink is ink on paper and gold on the
   dark ground, which is exactly what the drawing gives the current bar. */
.stamp-running { background: var(--sunken); }
.stamp-running .stamp-mark {
  border-color: var(--brass-ink);
  border-style: dashed;
  background: transparent;
}
.stamp-running .stamp-day { color: var(--brass-ink); }

/* Worked, but not yet settled: silver, the colour of secondary structure. */
.stamp-incomplete .stamp-mark { background: var(--muted); border-color: var(--muted); }
.stamp-absent .stamp-mark { border-style: dotted; }

/* A day the calendar never asked for. Without a rule of its own it took
   the empty ring of a day still to come, and the week read as if Sunday
   were owed. A dash is not a ring: nothing is open here. */
.stamp-free .stamp-mark {
  height: 2px;
  /* The dash keeps the ring's line in the grid, otherwise Saturday's
     figure sits 12 px higher than Friday's. */
  margin-block: 0.375rem;
  border: 0;
  border-radius: 999px;
  background: var(--muted);
}

/* A workday with nothing booked is the only thing in this column that
   still has to shout, and it does it with a line, not with a field. */
.stamp-missing .stamp-mark { border-color: var(--warning); border-style: dashed; }
.stamp-missing .stamp-day { color: var(--warning); }

/* Days that have not happened yet stand as empty tracks. The emptiness
   is in the weight of the ring, not in an opacity over the whole column
   – faded text is the fastest way to lose a contrast ratio. */
.stamp-ahead .stamp-mark { border-width: 1px; }

/* Today is named by a gold foot rule, not by a ring around the column:
   a box around one of seven narrow cells reads as an error. Gold is only
   2:1 against paper, so the day's name carries the marking as well. */
.stamp.is-today { background: var(--sunken); box-shadow: inset 0 -2px 0 var(--brass); }
.stamp.is-today .stamp-day { color: var(--brass-ink); }

@media (hover: hover) {
  .stamp:has(> a):hover { background: var(--sunken); }
}

/* ---- Streak and sums ---------------------------------------------- */

/* The streak is the drawing's header chip: a sunken pill, the count big
   and tabular, the words beside it as letter-spaced capitals. */
.streak-line {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 1rem 0 0;
  padding: 0.5rem 0.75rem;
  min-height: 2.75rem;
  border: 1px solid var(--border);
  border-radius: 0.75rem;
  background: var(--sunken);
}
.streak-count {
  font-size: 1.75rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.02em;
  /* Ink on paper, gold on the dark ground – the one token that carries
     the "earned" colour in both themes without ever putting gold on
     white, where it would fall to 2.1:1. */
  color: var(--brass-ink);
}
/* The line beside the count is a sentence ("Noch kein Tag
   abgeschlossen"), not a caption – so it keeps its lower case. */
.streak-text {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text);
}
.stamp-card .stamp-sums {
  margin: 0.4rem 0 0;
  font-size: 0.8rem;
}
.stamp-sums + .stamp-sums { margin-top: 0.15rem; }

/* Die Schalterformulare der Verwaltung: eine Spalte, volle Breite. Die
   Felder der Vorgaben sind kurze Auswahlen und stehen in 11-rem-Säulen;
   ein Kästchen mit zwei Sätzen Erklärung daneben würde darin zu einem
   Turm aus zwanzig Zeichen je Zeile. */
.admin .switch-form { display: grid; gap: 1rem; max-width: 46rem; margin: 0; }
.admin .switch-form .filter-field { flex: none; max-width: none; margin: 0; }
.admin .switch-form .field-hint { max-width: 62ch; }

/* ---- Punkte und Stufe (D-213) ------------------------------------- */

/* Off by default, so this block is simply absent most of the time. When
   it is there it sits between the week and the streak, in the same
   sunken field the streak uses – it is the same kind of statement about
   the same week, not a second, louder one. */
.points {
  margin: 1rem 0 0;
  padding: 0.75rem;
  border: 1px solid var(--card-border);
  border-radius: 0.75rem;
  background: var(--sunken);
}
/* Die Überschrift benennt den Block, statt eine Zahl ohne Namen
   dastehen zu lassen. */
.points-title {
  margin: 0 0 0.5rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.points-head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 0 0 0.25rem;
}

/* Der Ring statt eines zweiten Balkens: die Karte trägt darüber schon
   sieben Balken und darunter einen. Er trägt seinen Anteil als
   stroke-dasharray im Markup – pathLength="100" macht aus dem Umfang
   eine Hundertstel-Skala, unabhängig vom Radius. */
.points-ring { flex: none; transform: rotate(-90deg); }
.points-ring-track {
  fill: none;
  stroke: var(--secondary);
  stroke-width: 4;
}
.points-ring-arc {
  fill: none;
  stroke: var(--brass);
  stroke-width: 4;
  stroke-linecap: round;
}
.points-figures { display: grid; gap: 0.1rem; min-width: 0; }
.points-scale {
  font-size: 0.8rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
/* The level is the figure of this block, so it carries the weight – in
   the one colour that is ink on paper and gold on the dark ground. */
.points-level {
  font-size: 1.35rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--brass-ink);
}
.points-total {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.points-next {
  margin: 0.5rem 0 0;
  font-size: 0.85rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.points-sub { margin: 0.15rem 0 0; }
/* How points arise and the badges sit behind one fold (D-256, B24):
   open, they made the card the longest beside the clock. The summary is
   one of the card's quiet lines; opened, the content keeps its gaps. */
.points-more { margin: 0.75rem 0 0; }
.points-more > summary { font-size: 0.85rem; font-weight: 600; color: var(--text); }
.points-more > .field-hint:first-of-type { margin-top: 0.25rem; }

/* ---- The badges: the quiet list ----------------------------------- */

/* One row per badge, like the list under the bars. In the 19 rem column
   that is a single column; where the ledger has not split yet the row
   still fills the width rather than leaving five short lines on a wide
   screen. An unearned badge is an empty ring on an unfilled row; an
   earned one gets the gold dot, the gold hairline and the champagne
   field – with ink on top of it. */
.badge-row {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  gap: 0.5rem;
}
.stamp-badge {
  display: grid;
  grid-template-columns: 0.625rem minmax(0, 1fr);
  align-items: start;
  align-content: center;
  column-gap: 0.6rem;
  row-gap: 0.1rem;
  min-height: 2.75rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 0.625rem;
  background: transparent;
  color: var(--muted);
  transition: background-color 120ms ease, border-color 120ms ease;
}
.stamp-badge::before {
  content: "";
  grid-column: 1;
  grid-row: 1 / span 2;
  width: 0.625rem;
  height: 0.625rem;
  margin-top: 0.3rem;
  border: 1px solid var(--muted);
  border-radius: 50%;
}
.stamp-badge .badge-name {
  grid-column: 2;
  grid-row: 1;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.stamp-badge .badge-hint {
  grid-column: 2;
  grid-row: 2;
  font-size: 0.78rem;
  line-height: 1.35;
}
.stamp-badge.is-earned {
  border-color: var(--brass);
  background: var(--brass-soft);
  color: var(--text);
}
.stamp-badge.is-earned::before {
  background: var(--brass);
  border-color: var(--brass);
}
.stamp-badge.is-earned .badge-name { color: var(--brass-ink); }

/* ---- Die Tafel der Woche (D-213) ---------------------------------- */

/* Die eine Stelle, an der eine fremde Zahl steht. Sie ist deshalb die
   ruhigste Tafel der Seite: kein Podest, keine Medaille, kein Farbwechsel
   nach Platz – eine Liste mit einem Balken, und die eigene Zeile ist die
   einzige, die hervorgehoben wird. */
.board-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.25rem;
}
.board-row {
  display: grid;
  grid-template-columns: 1.25rem 2rem minmax(0, 1fr);
  align-items: center;
  gap: 0.15rem 0.6rem;
  padding: 0.4rem 0.5rem;
  border: 1px solid transparent;
  border-radius: 0.625rem;
}
/* Die eigene Zeile: goldene Haarlinie und die ruhige Fläche, dieselbe
   Marke, die eine laufende Kundenzeile trägt. */
.board-row.is-me {
  border-color: var(--brass);
  background: var(--brass-soft);
}
/* Wer diese Woche noch nichts erwartet hat, steht da, ohne dazustehen –
   über die Fläche, nie über die Deckkraft: ein blasser Text fällt unter
   4,5:1, und er träfe ausgerechnet die Zeilen von Menschen im Urlaub
   oder im Krankenstand. */
.board-row.is-quiet { background: var(--sunken); }
.board-row.is-quiet .board-name { font-weight: 500; color: var(--muted); }
.board-rank {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  text-align: right;
}
/* Die Initialen wie an der Terminalwand, nur kleiner. */
.board-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--sunken);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.board-row.is-me .board-mark {
  border-color: var(--brass);
  color: var(--brass-ink);
  background: var(--surface);
}
.board-name {
  grid-column: 3;
  font-size: 0.9rem;
  font-weight: 600;
  overflow-wrap: break-word;
}
.board-you {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.board-line {
  grid-column: 3;
  font-size: 0.8rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.board-bar {
  grid-column: 3;
  display: block;
  width: 100%;
  height: 0.25rem;
  margin-top: 0.15rem;
  border: 0;
  border-radius: 999px;
  background: var(--secondary);
  -webkit-appearance: none;
  appearance: none;
  overflow: hidden;
}
.board-bar::-webkit-progress-bar { background: var(--secondary); border-radius: 999px; }
.board-bar::-webkit-progress-value { background: var(--muted); border-radius: 999px; }
.board-bar::-moz-progress-bar { background: var(--muted); border-radius: 999px; }
.board-row.is-me .board-bar::-webkit-progress-value { background: var(--brass-ink); }
.board-row.is-me .board-bar::-moz-progress-bar { background: var(--brass-ink); }

/* ---- The week table ----------------------------------------------- */

/* The .rows rules below this block carry the same weight as a bare
   .week-table selector, so the table keeps its own edge only when the
   selector names both classes – otherwise the heavy head rule and the
   2 px foot of the generic table win on source order alone. */

/* One grid for every week (D-256): fixed columns, so paging from week to
   week moves nothing and a badge never widens its column. The widths sit
   on the head – the content security policy refuses a style attribute,
   and a fixed layout reads them from the first row. Day, date and figure
   are as wide as their longest content – „Mo“, „28.09.“, „24:00 h“ – and
   the note takes the rest of the card: as shares of the card the date and
   the figure stood far apart on a wide card, with a hole between them
   and the note squeezed to the right edge. The figure keeps no air on
   its right; the note's own padding parts them. A wider card gives the
   three a little more air, never the width the note needs. */
.rows.week-table { table-layout: fixed; }
.rows.week-table thead th:nth-child(1) { width: 2.5rem; }
.rows.week-table thead th:nth-child(2) { width: 3.625rem; }
.rows.week-table thead th:nth-child(3) { width: 3.75rem; }
.rows.week-table .num { padding-right: 0; }
@container week (min-width: 30rem) {
  .rows.week-table thead th:nth-child(1) { width: 3rem; }
  .rows.week-table thead th:nth-child(2) { width: 4.25rem; }
  .rows.week-table thead th:nth-child(3) { width: 4.25rem; }
  /* „NETTO HINWEIS“ and „0:00 h nachtragen“ read as one phrase 8 px
     apart; the general rule further down has the same weight. */
  .week-view .rows.week-table .num + :is(td, th) { padding-left: 1rem; }
}
/* The last column ends at the card's own inset; its share is the badges'. */
.rows.week-table :is(th, td):last-child { padding-right: 0; }

/* Column headings are labels like every other secondary caption in the
   column, and they rest on a hairline: the heavy rule under the head
   made the table look like a form. */
.rows.week-table thead th {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom-color: var(--border);
  padding-bottom: 0.5rem;
}
/* Every day is one row of the same height, whatever it carries – a date
   one can open, a badge, a button or nothing. The height is the 44 px
   target plus air; content is centred in it. */
.rows.week-table tbody td {
  height: 3rem;
  padding-block: 0;
  border-bottom-color: var(--border);
  vertical-align: middle;
}
.week-table tbody tr { transition: background-color 120ms ease; }

/* The date is the way into a day, so it is a target one can hit – but it
   looks like every other date in the column: underlined only under the
   pointer or the keyboard, never as a second style for some rows. */
.week-table tbody td a {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
}

@media (hover: hover) {
  .week-table tbody tr:hover { background: var(--sunken); }
}

/* The week's sum is what the week earned: it sits over a gold rule, the
   goal line of the drawing, and the figure itself takes the earned ink. */
.rows.week-table tfoot th,
.rows.week-table tfoot td {
  border-top: 1px solid var(--brass);
  padding-top: 0.6rem;
  /* The small caption and the larger sum share one baseline. */
  vertical-align: baseline;
}
.rows.week-table tfoot th {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.week-table tfoot strong {
  font-size: 1rem;
  font-weight: 600;
  color: var(--brass-ink);
}

/* A daily figure at the legal limit keeps its severity colour but loses
   the block it inherited from the finding list – inside a table cell a
   padded, left-barred field breaks the column of numbers. */
.week-table td span.warn-info:not(.badge),
.week-table td span.warn-warning:not(.badge),
.week-table td span.warn-critical:not(.badge) {
  padding: 0;
  border-left: 0;
  border-radius: 0;
  background: none;
  font-weight: 600;
}

/* An absence is a note beside the figure, so it is a silver outline, not
   a filled chip competing with the day's own colour. */
.week-table .badge-absence {
  background: transparent;
  border-color: var(--border);
  color: var(--muted);
  font-weight: 600;
  letter-spacing: 0.02em;
}

/* The missing-day badge is the shortest way to the retro form (D-154):
   a pill like every badge in the column, outlined as a line rather than
   a field. It is drawn 28 px tall so the row keeps its height; the
   invisible strip above and below gives the finger its 44 px. */
.badge-missing {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 1.75rem;
  padding: 0.1rem 0.65rem;
  font: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  border: 1px solid var(--warning);
  border-radius: 999px;
  background: transparent;
  color: var(--warning);
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 120ms ease;
}
.badge-missing::after { content: ""; position: absolute; inset: -0.5rem 0; }

@media (hover: hover) {
  .badge-missing:hover { background: var(--warning-bg); }
}

/* The notes under the table – average, rest, goal – are one block of
   secondary structure, held by the dashed goal line of the drawing. */
.week-view > .table-wrap + .muted.small {
  margin-top: 0.75rem;
  padding-top: 0.75rem;
  border-top: 1px dashed var(--border);
}
.week-view > .muted.small {
  margin-bottom: 0;
  font-size: 0.8rem;
}
.week-view > .muted.small + .muted.small { margin-top: 0.15rem; }

/* A finding keeps its severity colour, but as a rule on the leading edge
   instead of a filled field: three coloured blocks stacked in a 19 rem
   column drown the panel, and the panel is meant to be read, not
   alarmed at. */
.week-view .warn-item {
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--border);
  border-left: 3px solid var(--border);
  border-radius: 0.625rem;
  background: transparent;
}
.week-view .warn-item.warn-info { border-left-color: var(--info); color: var(--info); }
.week-view .warn-item.warn-warning { border-left-color: var(--warning); color: var(--warning); }
.week-view .warn-item.warn-critical { border-left-color: var(--critical); color: var(--critical); }

.week-view h3 {
  margin: 1.25rem 0 0.5rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The absence list is the second quiet list of the panel: rows on
   hairlines, no bullets, figures in line with the table above. */
.week-view .plain-list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.week-view .plain-list li {
  padding: 0.5rem 0;
  font-size: 0.85rem;
  border-bottom: 1px solid var(--border);
}
.week-view .plain-list li:last-child { border-bottom: 0; }

/* A phone keeps the week as a table: four short columns fit 286 px, and
   seven cards of label and value made the card 1,400 px tall (D-256). */

/* Day and week sections */
.section-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.75rem; }
.section-head h2 { margin: 0; }
/* The introduction is a paragraph, not a subtitle squeezed beside the
   heading: its own line, whatever its length – the reading measure of
   .lede capped the line and let it slip beside a short heading. The
   measure is kept by the padding instead (D-256). */
.section-head .lede { flex: 1 0 100%; max-width: none; padding-inline-end: max(0px, calc(100% - 56ch)); }
.day-title { margin: 0; color: var(--muted); font-weight: 600; }
.day-nav { display: flex; flex-wrap: wrap; gap: 0.35rem 0.5rem; margin-left: auto; }
.day-nav .btn-link { min-height: 2.75rem; padding-inline: 0.6rem; font-size: 0.92rem; }

/* Stepping through days, weeks and months is one control everywhere
   (D-256): the period between two square arrows, as the payroll month
   steps – on the heading's line, right-aligned, never on a line of its
   own with a hole beside it. The arrows name their way in hidden text
   and in a tooltip. An edge one cannot step past keeps its legibility
   and loses its field. */
.period-nav {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 0.35rem;
  min-width: 0;
  margin-left: auto;
}
.period-title {
  margin: 0;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.period-nav .btn.period-step {
  flex: none;
  min-width: 2.75rem;
  min-height: 2.75rem;
  margin: 0;
  padding: 0;
  font-size: 1.15rem;
  line-height: 1;
}
.period-nav .btn.period-step[aria-disabled="true"] {
  color: var(--muted);
  background: transparent;
  border-style: dashed;
  cursor: default;
  transform: none;
}

.table-wrap { overflow-x: auto; }
/* The sum alone answers nothing; the target beside it does (D-163). */
.period-target { font-size: 0.88rem; color: var(--muted); white-space: nowrap; }

.rows { width: 100%; border-collapse: collapse; }
.rows th, .rows td { text-align: left; padding: 0.45rem 0.5rem 0.45rem 0; border-bottom: 1px solid var(--border); vertical-align: top; }
.rows thead th { font-size: 0.8rem; color: var(--muted); font-weight: 600; border-bottom-color: var(--text); }
.rows tfoot th, .rows tfoot td { border-bottom: 0; border-top: 2px solid var(--border); }
.rows .activity { white-space: pre-wrap; overflow-wrap: break-word; }
.rows td[data-label=""]:empty { display: none; }
/* A figure column is read down its colons: right-aligned, never broken
   between number and unit, with air before the column after it. */
.rows .num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.rows.week-table .num + :is(td, th) { padding-left: 0.5rem; }

/* The tables of days (the week card, the Übersicht) mark today and open
   a day the same way (D-256). Today wears the gold foot of its stamp as a
   rule on the leading edge, on the sunken ground, with the weekday in
   ink – neither the rule nor the ground clears 3:1 on paper alone. The
   first column keeps the rule off its letters. */
.rows.week-table :is(th, td):first-child { padding-left: 0.5rem; }
/* Where the tables stay tables; folded, every cell hangs from its label. */
@media (min-width: 36.0625rem) {
  .rows .num + :is(td, th) { padding-left: 0.5rem; }
  .overview .rows :is(th, td):first-child { padding-left: 0.5rem; }
}
.rows .is-today { background: var(--sunken); }
.rows .is-today > td:first-child {
  box-shadow: inset 2px 0 0 var(--brass);
  font-weight: 600;
  color: var(--text);
}
/* A date one can open looks like every other date: the row of a past day
   and that of a day still ahead read alike, and the pointer or the
   keyboard brings the gold underline. */
.rows:is(.week-table, .overview-period) tbody td a {
  color: inherit;
  text-decoration: none;
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
}
.rows:is(.week-table, .overview-period) tbody td a:is(:hover, :focus-visible) {
  text-decoration: underline;
  text-decoration-color: var(--brass);
}

/* Tables collapse to cards on narrow screens; the column header travels
   with the cell via data-label. */
@media (max-width: 36rem) {
  /* The week card stays a table (see there); `:where` keeps the weight of
     every rule here what it was. */
  .rows:where(:not(.week-table)) thead { display: none; }
  .rows:where(:not(.week-table)),
  .rows:where(:not(.week-table)) tbody,
  .rows:where(:not(.week-table)) tfoot,
  .rows:where(:not(.week-table)) tr,
  .rows:where(:not(.week-table)) th,
  .rows:where(:not(.week-table)) td { display: block; width: 100%; }
  /* Every card has the same inset; today keeps it and adds its gold
     rule, instead of moving its labels four pixels to the right. */
  .rows:where(:not(.week-table)) tr { padding: 0.5rem; border-bottom: 1px solid var(--border); }
  .rows .is-today { box-shadow: inset 2px 0 0 var(--brass); }
  .rows .is-today > td:first-child { box-shadow: none; }
  /* The label hangs in a column of its own and the value keeps its own
     left edge: a second line starts under the value, never back under
     the label, and a long label never touches its value (D-256). */
  .rows:where(:not(.week-table)) td, .rows:where(:not(.week-table)) tfoot th { position: relative; border: 0; padding: 0.15rem 0 0.15rem 7.5rem; }
  .rows:where(:not(.week-table)) tfoot th { padding-left: 0; }
  .rows:where(:not(.week-table)) td::before {
    content: attr(data-label);
    position: absolute;
    left: 0;
    top: 0.3rem;
    width: 7rem;
    font-size: 0.8rem;
    font-weight: 600;
    line-height: 1.3;
    color: var(--muted);
  }
  .rows td[data-label=""]::before { display: none; }
  .rows:where(:not(.week-table)) td[data-label=""] { padding-left: 0; }
  .rows:where(:not(.week-table)) td:empty { display: none; }
  .rows:where(:not(.week-table)) .num { text-align: left; }
  /* The foot is one block under one rule, not a rule over every line. */
  .rows:where(:not(.week-table)) tfoot tr { border-top: 2px solid var(--border); border-bottom: 0; }
  .rows:where(:not(.week-table)) tfoot :is(th, td) { border-top: 0; }
  .table-wrap { overflow: visible; }

  .admin .error-text,
  .admin pre.json { max-width: 100%; }
}

/* The action a past day offers ("Tag nachtragen") sits above the tables
   it would fill. */
.day-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.9rem;
  margin: 0.75rem 0 0;
}
.day-actions .field-hint { margin: 0; }

.totals {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1.25rem;
  margin: 1rem 0 0;
  padding-top: 0.75rem;
  border-top: 2px solid var(--border);
  font-weight: 600;
}
.totals .totals-net { color: var(--primary); }
.totals span { white-space: nowrap; }
.totals .totals-label { color: var(--muted); font-weight: 600; }

.empty { margin: 0.75rem 0; color: var(--muted); }
.plain-list { margin: 0; padding-left: 1.2rem; }

/* ---------------------------------------------------------------------
   The day's board (fragments/day.html)

   The draft makes one board out of the day: a letter-spaced caption, the
   date as the line the eye lands on, and under it the entries as bands –
   a champagne hairline around each, a narrow rule on the left that says
   what kind of entry it is, the figures right-aligned in tabular
   numerals. Entries that are already settled – the breaks the machine
   added, the absences that stand – lie down quietly instead of being
   coloured. Gold is only ever a line, a ring or a dot here; on the light
   ground it never sets a word, because gold type on white reads at
   2.1:1.

   Everything is scoped to .day-view, which only fragments/day.html
   carries. The same .rows tables carry the week, the overview and the
   whole administration, and those must not move.
   --------------------------------------------------------------------- */

/* .card sets padding and radius with the same weight, so the day names
   both classes rather than relying on where this block sits in the file. */
.card.day-view {
  padding: 1rem;
  border-radius: 1rem;
}

@media (min-width: 30rem) {
  .card.day-view { padding: 1.5rem; }
}

/* The caption of the board: the heading steps back into the small label
   the draft gives it, so the date can be the line that is read. */
.day-view .section-head {
  align-items: center;
  gap: 0.5rem 0.75rem;
  margin-bottom: 0.25rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--border);
}

.day-view .section-head h2 {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The date is a figure like every other on this board, so it is set in
   the same tabular numerals; the global rule does not reach .day-title. */
.day-view .day-title {
  flex: 1 1 auto;
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.day-view .day-title .muted {
  margin-left: 0.4rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* The three day links are quiet pills, each a full 44 px target. */
.day-view .day-nav {
  gap: 0.5rem;
  margin-left: auto;
}

.day-view .day-nav .btn-link {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0 0.75rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 0.625rem;
}

/* "Heute" is the one day the view always returns to, so it carries the
   gold – as an edge around a soft field, with the ink's own text on it.
   The class is named alongside the address so this rule outweighs the
   one above instead of merely following it. */
.day-view .day-nav .btn-link[href="/"] {
  background: var(--brass-soft);
  border-color: var(--brass);
}

/* A note about the day (holiday, locked month) is a quiet strip with a
   gold rule, not a coloured block – it overrides the generic .warn-info
   field, which paints its own background and text. */
.day-view .hint.warn-info {
  margin: 1rem 0 0;
  padding: 0.75rem 0.875rem;
  font-weight: 500;
  color: var(--text);
  background: var(--sunken);
  border: 1px solid var(--border);
  border-left: 3px solid var(--brass);
  border-radius: 0.75rem;
}

/* The one action a past day offers sits in a field of its own, above the
   tables it would fill. */
.day-view .day-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.875rem;
  margin: 1rem 0 0;
  padding: 0.75rem;
  background: var(--sunken);
  border: 1px solid var(--border);
  border-radius: 0.75rem;
}

.day-view .day-actions .btn-secondary {
  min-height: 2.75rem;
  padding: 0 1rem;
  font-weight: 600;
  color: var(--text);
  background: var(--surface);
  border-color: var(--border);
  border-radius: 0.625rem;
}

.day-view .day-actions .field-hint {
  flex: 1 1 14rem;
  margin: 0;
}

/* Each group of entries gets a label, not a headline – with gold as the
   dot that marks it. The dot only repeats what the word already says, so
   nothing is lost where it is too faint to see. */
.day-view h3 {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 1.5rem 0 0.5rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.day-view h3::before {
  content: "";
  flex: none;
  width: 0.375rem;
  height: 0.375rem;
  background: var(--brass);
  border-radius: 999px;
}

/* The rows are bands, so they need their own gaps: collapsed borders
   cannot be rounded and cannot be spaced. */
.day-view .rows {
  border-collapse: separate;
  border-spacing: 0 0.5rem;
}

.day-view .rows thead th {
  padding: 0 0.75rem 0.25rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
}

.day-view .rows tbody td {
  padding: 0.75rem;
  vertical-align: middle;
  color: var(--text);
  background: transparent;
  border: 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.day-view .rows tbody td:first-child {
  border-left: 1px solid var(--border);
  border-top-left-radius: 0.625rem;
  border-bottom-left-radius: 0.625rem;
}

.day-view .rows tbody td:last-child {
  border-right: 1px solid var(--border);
  border-top-right-radius: 0.625rem;
  border-bottom-right-radius: 0.625rem;
}

/* The absence table ends in a cell that is empty unless the entry may be
   deleted. The global sheet hides such a cell; hidden, the band would
   lose its right edge, so here it stays and simply carries nothing. */
.day-view .rows tbody td[data-label=""]:empty { display: table-cell; }

/* The narrow rule on the left says what the row is: gold for the
   attendance the day rests on, ink for the work that is booked, silver
   for what merely accompanies it. It is the band's own left edge, grown
   from a hairline to a rule, so it follows the rounded corner cleanly
   instead of being cut by it. */
.day-view .rows .row-attendance > td:first-child {
  border-left: 0.1875rem solid var(--brass);
}

.day-view .rows .row-timesheet > td:first-child {
  font-weight: 600;
  border-left: 0.1875rem solid var(--primary);
}

.day-view .rows .row-break > td:first-child,
.day-view .rows .row-absence > td:first-child {
  border-left: 0.1875rem solid var(--muted);
}

/* Settled rows lie down instead of shouting: a filled but edgeless band
   in the muted ink. The left rule above is written on the first cell
   alone and therefore survives this. */
.day-view .rows .row-break > td,
.day-view .rows .row-absence > td {
  color: var(--muted);
  background: var(--sunken);
  border-color: transparent;
}

/* A clock-out that is still missing is the one thing on this board that
   may raise its voice – on the row and in the badge. The badge carries
   the message on its own, so a browser without :has() loses nothing. */
.day-view .rows .row-attendance:has(.warn-critical) > td:first-child {
  border-left-color: var(--critical);
}

.day-view .badge.warn-critical {
  padding: 0.1rem 0.6rem;
  color: var(--critical);
  background: var(--critical-bg);
  border: 1px solid var(--critical);
  border-radius: 999px;
}

/* An automatic break is structure, not news. */
.day-view .badge-auto {
  color: var(--muted);
  background: transparent;
  border-color: var(--border);
}

/* The figures are the content of this board: right-aligned, never
   broken, and the duration is the one that carries the weight. Which
   column that is depends on the table, so each table names its own. */
.day-view .rows:has(.row-attendance) th:nth-child(3),
.day-view .rows:has(.row-attendance) td:nth-child(3),
.day-view .rows:has(.row-break) th:nth-child(3),
.day-view .rows:has(.row-break) td:nth-child(3),
.day-view .rows:has(.row-timesheet) th:nth-child(2),
.day-view .rows:has(.row-timesheet) td:nth-child(2) {
  text-align: right;
  font-weight: 600;
  white-space: nowrap;
}

.day-view .row-attendance > td:nth-child(-n + 2),
.day-view .row-break > td:nth-child(-n + 2) { white-space: nowrap; }
/* A badge beside the end ("Ende automatisch gesetzt – unbestätigt") may
   wrap inside its column; kept on one line it pushed the table past the
   card's edge and cut off the duration (D-256). */
.day-view .row-attendance > td:nth-child(2):has(.badge) { white-space: normal; }
.day-view .row-attendance > td:nth-child(2) .badge { white-space: normal; text-align: left; text-wrap: balance; }

/* Attendance, breaks and absences share one grid, so their durations
   stand at the same x; the widths sit on the head – the CSP refuses a
   style attribute, and a fixed layout reads them from the first row. */
@media (min-width: 36.0625rem) {
  .day-view .rows:is(:has(.row-attendance), :has(.row-break), :has(.row-absence)) { table-layout: fixed; }
  .day-view .rows:is(:has(.row-attendance), :has(.row-break), :has(.row-absence)) th:nth-child(1) { width: 18%; }
  .day-view .rows:is(:has(.row-attendance), :has(.row-break), :has(.row-absence)) th:nth-child(2) { width: 34%; }
  .day-view .rows:is(:has(.row-attendance), :has(.row-break), :has(.row-absence)) th:nth-child(3) { width: 16%; }
  .day-view .rows:is(:has(.row-attendance), :has(.row-break), :has(.row-absence)) th:nth-child(4) { width: 32%; }
  .day-view .rows:is(:has(.row-attendance), :has(.row-break)) td:last-child { text-align: right; }
}

/* Both inline actions of the day – change the activity, delete an
   absence – are quiet pills with a full 44 px target; the shared
   .btn-inline is 35 px and too small for a finger. */
.day-view .btn-inline {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  margin-left: 0.25rem;
  padding: 0 0.75rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0.625rem;
}

/* A pointer may show where it is; a finger may not, so the state is
   never the only signal. */
@media (hover: hover) {
  .day-view .row-attendance:hover > td,
  .day-view .row-timesheet:hover > td { background: var(--sunken); }

  .day-view .row-break:hover > td,
  .day-view .row-absence:hover > td { background: var(--secondary); }

  .day-view .day-nav .btn-link:hover,
  .day-view .day-actions .btn-secondary:hover,
  .day-view .btn-inline:hover {
    background: var(--sunken);
    border-color: var(--brass);
  }
}

/* A day without a single entry says so in a field of its own. */
.day-view .empty {
  margin: 1rem 0 0;
  padding: 1.5rem 1rem;
  text-align: center;
  background: var(--sunken);
  border: 1px dashed var(--border);
  border-radius: 0.75rem;
}

/* The sum closes the board: a field under a gold hairline, every figure
   over its own small label, and the net time in a gold ring – gold as
   the frame, the ink as the text. */
.day-view .totals {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.5rem;
  margin: 1.25rem 0 0;
  padding: 0.875rem 1rem;
  font-weight: 600;
  background: var(--sunken);
  border: 1px solid var(--border);
  border-top: 2px solid var(--brass);
  border-radius: 0.75rem;
}

.day-view .totals > span {
  display: inline-flex;
  flex-direction: column;
  gap: 0.15rem;
  font-size: 1.05rem;
  white-space: nowrap;
}

.day-view .totals .totals-label {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* "davon Pflichtpause ergänzt" is a sentence, not a figure: it keeps the
   smaller size but is allowed to break, so a longer wording can never
   push the card past the edge of a 390 px screen. */
.day-view .totals > span.small {
  font-size: 0.8rem;
  font-weight: 500;
  white-space: normal;
}

.day-view .totals .totals-net {
  padding: 0.4rem 0.875rem;
  font-size: 1.25rem;
  color: var(--text);
  background: var(--brass-soft);
  border: 1px solid var(--brass);
  border-radius: 0.625rem;
}

/* The findings keep the colour of their severity – that is the signal –
   but take the board's own corner. */
.day-view .warn-list { margin-top: 1rem; }
.day-view .warn-item { border-radius: 0.625rem; }

.day-view .rows tbody td,
.day-view .day-nav .btn-link,
.day-view .day-actions .btn-secondary,
.day-view .btn-inline {
  transition: background-color 120ms ease, border-color 120ms ease;
}
/* prefers-reduced-motion needs no rule of its own: the sheet ends with
   `* { transition: none !important }`, which no later rule can outrank. */

/* Narrow screens: the tables fold into cards, as the global sheet
   already does at this very breakpoint. Each row becomes a band of its
   own and keeps its rule on the left; the column heading travels with
   the value as a small label, the value stands at the right edge.
   Nothing here is wider than its column, so a 390 px screen never
   scrolls sideways. */
@media (max-width: 36rem) {
  .day-view .rows tbody tr {
    margin-bottom: 0.5rem;
    padding: 0.75rem;
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 0.75rem;
  }

  .day-view .rows tbody tr.row-attendance { border-left: 0.1875rem solid var(--brass); }
  .day-view .rows tbody tr.row-timesheet { border-left: 0.1875rem solid var(--primary); }

  .day-view .rows tbody tr.row-break,
  .day-view .rows tbody tr.row-absence {
    background: var(--sunken);
    border-color: transparent;
    border-left: 0.1875rem solid var(--muted);
  }

  .day-view .rows tbody tr.row-attendance:has(.warn-critical) {
    border-left-color: var(--critical);
  }

  .day-view .rows tbody td {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.15rem 0 0.15rem 0.5rem;
    text-align: right;
    background: transparent;
    border: 0;
    border-radius: 0;
    /* A customer name or a remark is not ours to shorten, and here it
       has a label beside it: it breaks rather than widening the page. */
    overflow-wrap: break-word;
  }

  /* A value the day does not have takes no line of its own – not even
     the last cell of the absence table, which has no edge to keep here. */
  .day-view .rows tbody td:empty,
  .day-view .rows tbody td[data-label=""]:empty { display: none; }

  .day-view .rows tbody td::before {
    position: static;
    flex: none;
    width: auto;
    min-width: 0;
    font-size: 0.75rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    text-align: left;
  }

  /* An activity text is a sentence, not a figure: it takes the whole
     width under its label instead of a column beside it. */
  .day-view .rows tbody td.activity {
    display: block;
    text-align: left;
  }

  .day-view .rows tbody td.activity::before {
    display: block;
    margin-bottom: 0.25rem;
  }

  .day-view .rows tbody td[data-label=""] { justify-content: flex-start; }

  /* Folded, a row is its own band: the rounded ends the table gives its
     first and last cell would draw a "(" left of the first label and a
     frame around the last button (D-256). */
  .day-view .rows tbody tr[class] > td:first-child,
  .day-view .rows tbody tr[class] > td:last-child { border: 0; border-radius: 0; }
  .day-view .rows tbody td:has(> .badge-joint) { flex-wrap: wrap; }

  /* A missing end carries its action beside the badge („Gehen
     eintragen“, „Ende eintragen“): on a phone the action moves under the
     badge instead of out of the card. */
  .day-view .rows tbody tr.row-attendance > td:nth-child(2):has(> .badge, > .btn-inline) {
    flex-wrap: wrap;
    justify-content: flex-end;
    white-space: normal;
  }

  .day-view .rows tbody tr.row-attendance > td:nth-child(2):has(> .badge, > .btn-inline)::before {
    margin-right: auto;
  }

  .day-view .day-nav {
    flex: 1 0 100%;
    margin-left: 0;
  }

  .day-view .totals { gap: 0.75rem 1.25rem; }
}

@media (min-width: 36.0625rem) {
  /* Wide enough for the sum line to separate its net time from the rest. */
  .day-view .totals .totals-net { margin-left: auto; }
}


/* Pages that are tables or long lists rather than a single form break out
   of the reading column, up to the viewport width. Used by the
   administration, the overview and the project selection. */
.wide,
.admin,
.overview {
  width: min(calc(100vw - 3rem), 80rem);
  margin-inline: calc((100% - min(calc(100vw - 3rem), 80rem)) / 2);
}

@media (max-width: 36rem) {
  .wide,
  .admin,
  .overview { width: auto; margin-inline: 0; }
}

/* Every administration page has the same place for what an action
   answers; without it an HTMX error would be swapped into the field the
   request was aiming at (D-166). */
.flash-region:empty { display: none; }

/* admin */
.admin-nav { display: flex; flex-wrap: wrap; gap: 0.25rem 0.75rem; margin: 0 0 1rem; padding: 0.5rem 0.75rem; background: var(--surface); border: 1px solid var(--card-border); border-radius: 0.75rem; }
.admin-nav a { text-decoration: none; font-weight: 600; padding: 0.35rem 0.5rem; border-radius: 0.5rem; }
.admin-nav a.active { background: var(--primary); color: var(--primary-text); }
/* Seven areas in one row from a tablet on; on a phone they wrap into two
   rows. Scrolling sideways hid "Tageskorrektur" and "Lohn" behind the
   edge – and with them the area one was in (D-183, D-256). */
@media (max-width: 48rem) {
  .admin-nav { gap: 0.25rem 0.35rem; padding: 0.4rem 0.5rem; }
  .admin-nav a { padding-inline: 0.45rem; }
}
/* The second row of Einstellungen: the title and its three tabs. */
.settings-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.5rem 1rem; margin: 0 0 1rem; }
.settings-head h1 { margin: 0; }
.settings-tabs { display: flex; flex-wrap: wrap; gap: 0.25rem; border-bottom: 1px solid var(--border); }
.settings-tabs a { text-decoration: none; font-weight: 600; padding: 0.4rem 0.75rem; border-bottom: 3px solid transparent; }
.settings-tabs a.active { border-bottom-color: var(--primary); }
/* Start page: each line one thing to do, its button at the end. */
.attention-list { list-style: none; margin: 0.5rem 0 0; padding: 0; }
.attention-line { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.25rem 1rem; padding: 0.5rem 0; border-bottom: 1px solid var(--border); }
.attention-line:last-child { border-bottom: 0; }
.attention-line .btn { margin-top: 0; }
.attention-line .inline-form { margin: 0; }
.flash-with-undo { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.75rem; }
.flash-with-undo .inline-form { margin: 0; }
.flash-with-undo .btn-link { min-height: 0; padding: 0; color: inherit; }
.attention-none { font-weight: 600; color: var(--moss); }
.ops-line { list-style: none; margin: 0.5rem 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.25rem 1.25rem; }
.ops-line .stat-label::after { content: ":"; }
.tech-details { margin-top: 0.75rem; }
.tech-details .stat-grid { margin-top: 0.5rem; }
.admin h3 { font-size: 1rem; margin: 0.75rem 0 0.4rem; }
.admin .flash { padding: 0.75rem 1rem; border-radius: 0.5rem; border: 1px solid var(--border); }
/* An answer to a form is a box of its colour on every page, not only in
   the administration: "Ihre Anfrage ist gestellt." stood there as plain
   text (D-256). `.flash` alone is the region of the start page, so the
   box hangs on the colour classes. */
.flash-ok, .flash-warn, .flash-error { padding: 0.75rem 1rem; border-radius: 0.5rem; border: 1px solid var(--border); }
.flash-ok { background: var(--info-bg); color: var(--info); border-color: var(--info); }
.flash-warn { background: var(--warning-bg); color: var(--warning); border-color: var(--warning); }
.flash-error { background: var(--danger-bg); color: var(--danger); border-color: var(--danger); }
.admin .stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: 0.75rem; }
.admin .stat { display: flex; flex-direction: column; gap: 0.15rem; padding: 0.75rem; border: 1px solid var(--border); border-radius: 0.5rem; }
.admin .stat-label { font-size: 0.85rem; color: var(--muted); }
.admin .stat-value { font-weight: 700; }
.admin .health-ok { color: var(--moss); }
.admin .health-unreachable, .admin .health-auth_failed, .admin .health-failed, .admin .health-stale { color: var(--danger); }
.ok { color: var(--moss); font-weight: 600; }
.admin .link-list, .admin .plain-list { padding-left: 1.25rem; }
.admin .plain-list li { margin: 0.25rem 0; }
.admin .table-wrap { overflow-x: auto; }
/* The sum alone answers nothing; the target beside it does (D-163). */
.period-target { font-size: 0.88rem; color: var(--muted); white-space: nowrap; }
.admin .admin-table { width: 100%; border-collapse: collapse; font-size: 0.92rem; }
.admin .admin-table th, .admin .admin-table td { text-align: left; vertical-align: top; padding: 0.5rem 0.5rem; border-bottom: 1px solid var(--border); }
.admin .admin-table th { font-size: 0.82rem; color: var(--muted); }
.admin .admin-table tr.inactive td { color: var(--muted); }
.admin .admin-table td p { margin: 0 0 0.4rem; }
/* The ways in: label, state and button on one line (D-256). */
.admin .access-ways :is(th, td) { vertical-align: middle; }
.admin .access-ways .inline-form { margin: 0; }
.admin .access-ways .btn { margin-top: 0; }
/* A warning inside a cell is a box under the line it qualifies, never an
   inline box laid over its neighbours (D-256). */
.admin .admin-table td p.warn-warning { margin: 0.35rem 0 0; padding: 0.35rem 0.6rem; }
/* A figure column of a report: right-aligned, read down its colons, never
   broken between number and unit (D-256). */
.admin .admin-table .num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
/* The figure's heading may wrap; only the figure may not. */
.admin .admin-table th.num { white-space: normal; }
.admin .admin-table th.col-group { text-align: center; border-bottom-color: var(--border); }
.admin .admin-table thead tr + tr th { padding-top: 0; }
.admin .report-table .row-note td { padding-top: 0; }
.admin .report-table .row-note p { margin: 0 0 0.35rem; max-width: 70ch; }
.admin .report-table tr.has-note td { border-bottom: 0; }
/* A person's name is one line in a table of figures. */
@media (min-width: 48.0625rem) {
  .admin .report-table tbody td:first-child strong { white-space: nowrap; }
}

/* The administration's tables are the widest in the application – the
   employee list carries five forms per row, the sync queue four in its
   last cell. On a phone they fold into cards, the same way the employee
   tables do (D-166); a report with ten figures side by side does so on a
   tablet already, each figure beside its label. These rules stand after
   the table's own: at the same weight, the earlier fold lost its border
   and padding to them (D-256). */
@media (max-width: 36rem) {
  .admin .admin-table thead { display: none; }
  .admin .admin-table,
  .admin .admin-table tbody,
  .admin .admin-table tfoot,
  .admin .admin-table tr,
  .admin .admin-table th,
  .admin .admin-table td { display: block; width: 100%; }
  .admin .admin-table tr { padding: 0.6rem 0; border-bottom: 2px solid var(--border); }
  .admin .admin-table td,
  .admin .admin-table th { border: 0; padding: 0.2rem 0; }
  .admin .admin-table td:empty { display: none; }
  .admin .admin-table .num { text-align: left; }
  .admin .admin-table .badge { white-space: normal; border-radius: 0.6rem; }
  .admin .admin-table td::before {
    content: attr(data-label);
    display: block;
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--muted);
    margin-top: 0.35rem;
  }
  .admin .admin-table td[data-label=""]::before,
  .admin .admin-table td:not([data-label])::before { display: none; }
}
@media (max-width: 48rem) {
  .admin .report-table thead { display: none; }
  .admin .report-table,
  .admin .report-table tbody,
  .admin .report-table tfoot,
  .admin .report-table tr,
  .admin .report-table th,
  .admin .report-table td { display: block; width: 100%; }
  .admin .report-table tr { padding: 0.6rem 0; border-bottom: 2px solid var(--border); }
  .admin .report-table tr.has-note { border-bottom: 0; padding-bottom: 0; }
  .admin .report-table td,
  .admin .report-table th { border: 0; padding: 0.15rem 0; }
  .admin .report-table td:empty { display: none; }
  /* A badge that wraps keeps the inner radius; a pill of two lines reads
     as a lozenge. */
  .admin .report-table .badge { white-space: normal; border-radius: 0.6rem; }
  /* Label and value side by side: a column of figures, not a column of
     labels with a figure under each. */
  .admin .report-table td {
    display: grid;
    grid-template-columns: minmax(0, 11rem) minmax(0, 1fr);
    column-gap: 0.75rem;
    align-items: baseline;
    text-align: left;
    /* An address or an identifier has no space to break at; in a column
       of fixed width it breaks rather than running past the card. */
    overflow-wrap: anywhere;
  }
  .admin .report-table td.num { text-align: left; }
  .admin .report-table td > * { justify-self: start; }
  .admin .report-table td::before {
    content: attr(data-label);
    display: block;
    margin: 0;
    white-space: normal;
    text-align: left;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--muted);
  }
  /* The first cell names the card; it needs no label. */
  .admin .report-table td:first-child,
  .admin .report-table td[data-label=""],
  .admin .report-table td:not([data-label]) { display: block; }
  .admin .report-table td:first-child::before,
  .admin .report-table td[data-label=""]::before,
  .admin .report-table td:not([data-label])::before { display: none; }
}
@media (max-width: 36rem) {
  .admin .report-table td { grid-template-columns: minmax(0, 7.5rem) minmax(0, 1fr); }
}
.admin .error-text { white-space: pre-wrap; word-break: break-word; max-width: 28rem; color: var(--danger); }
.admin pre.json { max-width: 28rem; max-height: 16rem; overflow: auto; font-size: 0.8rem; padding: 0.5rem; background: var(--sunken); border-radius: 0.4rem; }
.admin details summary { cursor: pointer; }
/* Doing without the PIN takes something away, so its door is red (D-166);
   the explanation and the checkbox sit behind it, framed in the same red. */
.admin summary.btn { list-style: none; }
/* "Stand löschen …" is one button, not a column of syllables: a table
   cell may not squeeze it below its words (D-256). */
.admin .admin-table summary.btn { white-space: nowrap; }
.admin summary.btn::-webkit-details-marker { display: none; }
.admin details.details-danger[open] { border-left: 4px solid var(--danger); padding-left: 0.75rem; margin: 0.25rem 0 0.5rem; }
.admin .check-line { display: flex; gap: 0.5rem; align-items: flex-start; font-size: 0.9rem; }
.admin .check-line input { margin-top: 0.2rem; }
/* D-251: the choices under „nur mit Genehmigung“ sit below it; under
   „frei“ they are out of view - the server ignores them anyway. Without
   :has() app.js hides them (initExtraPaths). */
.admin .check-sub { margin-left: 1.75rem; }
.admin .extra-paths:has(input[name="extra_scope"][value="free"]:checked) .extra-approval { display: none; }
/* The surcharge card's table (D-250): the reader looks for the kind of
   hour, so its name stands in the text colour at the table's size; what
   makes an hour of that kind stays muted beside it. */
.admin .surcharge-kinds tbody th { font-size: inherit; font-weight: 400; }
.admin .surcharge-kinds tbody th strong { color: var(--text); font-weight: 600; }
/* The notes under the table and below the form run as wide as the card
   otherwise - about 190 characters a line at 1280 px; the form's own
   hints keep this measure already. */
.admin #zuschlaege > .field-hint { max-width: 62ch; }
/* The card at a glance (02.10.2026): the explanation and the law open in
   place under the table - read beside it, never over the form - and a
   field's „Mehr dazu“ stays on the line of its hint until it is opened. */
.sur-more { display: flex; flex-wrap: wrap; gap: 0 1.5rem; }
.sur-more > details[open] { flex: 1 0 100%; }
@media (min-width: 48rem) {
  /* The kind of hour takes the larger part of the table, so its few
     words keep to one line where they can; the short figures need little. */
  .admin .surcharge-kinds thead th:first-child { width: 50%; }
}
@media (min-width: 64rem) {
  .admin .surcharge-kinds thead th:first-child { width: 60%; }
}
/* The one line under the table reads in one line where it fits. */
.admin #zuschlaege > .sur-decides { max-width: none; }
.sur-more .clause-tip-panel { font-size: 0.88rem; }
.sur-more .clause-tip-panel > p,
.sur-explain { max-width: 72ch; }
.sur-more .clause-tip-panel > p { margin: 0 0 0.6rem; }
.sur-more .clause-tip-panel > p:last-child { margin-bottom: 0; }
.sur-explain { margin: 0; }
.sur-explain dt { font-weight: 600; color: var(--text); margin: 0.8rem 0 0; }
.sur-explain dt:first-child { margin-top: 0; }
.sur-explain dd { margin: 0.15rem 0 0; }
.sur-explain dd + dd { color: var(--muted); }
/* Its lines stay at the top when a row of fields is taller than it. */
.hint-more { display: flex; flex-wrap: wrap; align-items: baseline; align-content: flex-start; gap: 0 0.75rem; }
.admin .hint-more > .field-hint { max-width: none; }
.hint-more > details[open] { flex: 1 0 100%; }
.field-more > summary { margin-top: 0.3rem; font-size: 0.85rem; color: var(--primary); cursor: pointer; }
.field-more > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 0.2rem; }
/* „Mehr dazu“ wears the chevron of every fold (D-256), but the card is
   read at a glance (D-255): drawn one line tall, it gets the finger's
   44 px from an invisible strip above and below, like „nachtragen“ in
   the week - as a 44 px block every hint pushed the next field down. */
details.field-more > summary { min-height: 0; position: relative; }
details.field-more > summary::after { content: ""; position: absolute; inset: -0.75rem 0; }
.field-more > p { margin: 0.3rem 0 0; font-size: 0.85rem; color: var(--muted); max-width: 62ch; }
/* The three rates side by side where there is room: one row of figures,
   their labels ending on one line above the inputs. */
.admin .sur-form { max-width: none; margin-top: 1rem; }
.admin .sur-form > * { max-width: 46rem; }
.admin .sur-form > .sur-rates { max-width: none; }
.sur-rates { display: grid; gap: 1rem 1.5rem; }
.sur-rates input { max-width: 10rem; }
.sur-form input[type="date"] { max-width: 12rem; }
@media (min-width: 64rem) {
  .sur-rates { grid-template-columns: repeat(3, minmax(0, 1fr)); row-gap: 0; }
  .sur-rates > .filter-field { grid-row: span 3; grid-template-rows: subgrid; }
  .sur-rates > .filter-field > label { align-self: end; }
}
/* Only a correction keeps a reason: its field shows once the box is
   ticked - without :has() it simply stays. */
.sur-form:not(:has(input[name="correction"]:checked)) .sur-reason { display: none; }
@media (max-width: 36rem) {
  /* Folded into cards, a short cell reads beside its column's name, and
     the lines of the wide table go: the card's own line parts the kinds. */
  .admin .surcharge-kinds tbody th,
  .admin .surcharge-kinds td { border: 0; padding: 0.15rem 0; }
  .admin .surcharge-kinds td::before { display: inline; margin: 0 0.4rem 0 0; }
}
.admin .approver-form fieldset { border: 0; padding: 0; margin: 0; display: grid; gap: 0.5rem; }
.admin .approver-form .approver-sub { margin: -0.25rem 0 0 1.5rem; }
.admin .approver-form select.approver-sub { margin-top: 0; max-width: calc(100% - 1.5rem); }
.admin .state-warning { color: var(--warning); font-weight: 600; }
.admin .badge { margin-left: 0.4rem; }
.admin .badge-admin { background: var(--info-bg); color: var(--info); border-color: var(--info); }
.admin .badge-inactive { background: var(--warning-bg); color: var(--warning); border-color: var(--warning); }
.admin .badge-local { background: var(--sunken); color: var(--muted); border-color: var(--border); }
.admin .badge-PENDING { background: var(--pending-bg); color: var(--pending); border-color: var(--pending); }
.admin .badge-SENT { background: var(--moss-soft); color: var(--moss); border-color: var(--moss); }
.admin .badge-FAILED { background: var(--danger-bg); color: var(--danger); border-color: var(--danger); }
.admin .badge-DISCARDED { background: var(--secondary); color: var(--muted); }
.admin tr.state-DISCARDED td { color: var(--muted); }
.admin .inline-form { margin: 0.25rem 0; }
.admin .inline-form label { font-size: 0.8rem; margin: 0.4rem 0 0.15rem; }
.admin .toolbar { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 0.75rem; margin-bottom: 0.75rem; }
.admin .filter-links { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.admin .filter-links a { padding: 0.3rem 0.6rem; border-radius: 999px; border: 1px solid var(--border); text-decoration: none; }
.admin .filter-links a.active { background: var(--primary); color: var(--primary-text); border-color: var(--primary); }
.admin .filter-form { display: flex; flex-wrap: wrap; gap: 0.75rem 1rem; align-items: end; max-width: 48rem; margin-bottom: 0.75rem; }
.admin .filter-form .filter-field { flex: 1 1 12rem; }
.admin .day-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 0.5rem; }
.admin .day-head h2 { margin: 0; }
/* The weeks report: the period is the heading, between its arrows. */
.period-nav.period-head { margin: 0.75rem 0 0; }
.period-nav.period-head .period-title { font-size: 1.1rem; }
.admin .period-nav .btn.period-step { margin-top: 0; min-height: 2.75rem; }
.admin .pager { display: flex; gap: 0.5rem; }
.admin .row-card { border: 1px solid var(--card-border); border-radius: 0.5rem; padding: 0.75rem; margin: 0.75rem 0; }
.admin .row-card.deleted h3 { text-decoration: line-through; color: var(--muted); }
.admin .row-form { margin: 0.5rem 0 0; padding-top: 0.5rem; border-top: 1px dashed var(--border); }
.admin .field-row { display: grid; grid-template-columns: 1fr; gap: 0 0.75rem; }
.admin input[type="datetime-local"] { width: 100%; min-height: 2.5rem; padding: 0.4rem 0.6rem; font: inherit; border: 1px solid var(--border); border-radius: 0.5rem; background: var(--surface); color: var(--text); }
/* Every field of a form row is as tall as the button beside it: time,
   number and month fields stood 6 px taller than their neighbours. */
.admin :is(input[type="text"], input[type="date"], input[type="email"], input[type="password"], input[type="url"], input[type="month"], input[type="time"], input[type="number"], input[type="datetime-local"], input[type="search"], select, textarea) { min-height: 2.5rem; padding: 0.4rem 0.6rem; }
.admin select:not([multiple]) { padding-right: 2.25rem; }
.admin .stacked-form { max-width: 32rem; margin: 0 0 1rem; }
.admin .stacked-form label { display: block; font-size: 0.85rem; margin: 0.6rem 0 0.15rem; }
.admin .stacked-form input { width: 100%; font: inherit; border: 1px solid var(--border); border-radius: 0.5rem; background: var(--surface); color: var(--text); }
/* Radio buttons and check boxes in a stacked form keep their own size: the
   rule above stretched them to the full width, and the label fell onto a
   line of its own below a lone, centred circle. */
.admin .stacked-form input[type="radio"],
.admin .stacked-form input[type="checkbox"] { width: auto; flex: none; border: 0; }

/* The one place a secret is on screen (D-155). It is framed like
   something to be torn off and carried away, and set large enough to
   read out over a desk without leaning in. */
.admin .secret-once { border: 2px dashed var(--primary); border-radius: 0.7rem; padding: 1rem 1.1rem; margin: 1rem 0; background: var(--sunken); }
.admin .secret-once h2 { margin-top: 0; }
.admin .secret-label { margin: 0; font-size: 0.85rem; color: var(--muted); }
.admin .secret-value { margin: 0.25rem 0 0.75rem; }
.admin .secret-value code { font-size: 1.6rem; letter-spacing: 0.08em; font-weight: 700; word-break: break-all; }
.admin textarea { min-height: 4rem; }
.admin .btn { min-height: 2.5rem; padding: 0.4rem 0.9rem; font-size: 0.95rem; margin-top: 0.4rem; }
.admin .btn-link { margin-top: 0; }
@media (min-width: 40rem) {
  .admin .field-row { grid-template-columns: 1fr 1fr; }
}

/* A contract's versions (D-217): hours and their basis side by side, the
   weekdays as a row of boxes a finger can hit. */
.admin .contract-hours { display: flex; gap: 0.5rem; }
.admin .contract-hours input { flex: 1 1 6rem; }
.admin .contract-hours select { flex: 0 0 auto; width: auto; }
.admin .day-picks { display: flex; flex-wrap: wrap; gap: 0.4rem; border: 0; padding: 0; margin: 0.9rem 0 0; }
.admin .day-picks legend { font-size: 0.85rem; margin-bottom: 0.3rem; padding: 0; }
.admin .day-pick { display: inline-flex; align-items: center; gap: 0.3rem; min-height: 2.5rem; padding: 0 0.7rem; border: 1px solid var(--border); border-radius: 0.5rem; background: var(--surface); cursor: pointer; }
.admin .stacked-form .day-pick { margin: 0; font-size: 0.95rem; }
.admin .day-pick input { width: auto; margin: 0; }
.admin .day-pick:has(input:checked) { border-color: var(--primary); background: var(--primary-soft); }
.admin .day-pick:has(input:focus-visible) { outline: 2px solid var(--primary); outline-offset: 2px; }
.admin .plain-fieldset { border: 0; padding: 0; margin: 0; }
/* A group's title is set like the labels in it, not lighter than its own
   options (D-256). */
.plain-fieldset > legend { padding: 0; margin: 0 0 0.35rem; font-weight: 600; }
.stacked-form .plain-fieldset { margin-top: 1.25rem; }
/* A wrapped option line stays beside its box: the stacked form's
   block labels put the second line under the radio (D-256). */
.admin .stacked-form label.check-line { display: flex; }
.admin .stacked-form label.day-pick { display: inline-flex; align-items: center; }
.admin .badge-contract { background: var(--moss-soft); color: var(--moss); border-color: var(--moss); }

/* The leave account (D-218): one big figure, four that always add up,
   the sentences that explain them, the spans of leave below. */
.leave-account { margin-top: 0.5rem; }
.leave-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 0.5rem; }
.leave-year { margin: 0; color: var(--muted); font-size: 0.9rem; }
.leave-years { display: flex; gap: 0.75rem; font-size: 0.9rem; }
.leave-headline { margin: 0.4rem 0 0.8rem; font-size: 2.2rem; font-weight: 700; letter-spacing: -0.02em; line-height: 1.1; font-variant-numeric: tabular-nums; color: var(--primary); }
.leave-headline.is-none { color: var(--text); }
.leave-headline.is-advance { color: var(--warning); }
.leave-figures { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.5rem; margin: 0 0 0.9rem; }
.leave-figures > div { border: 1px solid var(--border); border-radius: 0.5rem; padding: 0.5rem 0.7rem; background: var(--sunken); }
.leave-figures dt { font-size: 0.8rem; color: var(--muted); }
.leave-figures dd { margin: 0.1rem 0 0; font-size: 1.35rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.leave-figures .leave-open { border-color: var(--primary); }
@media (min-width: 40rem) {
  .leave-figures { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.leave-line { margin: 0.3rem 0; max-width: 62ch; }
/* htmx's own indicator styles are off (they would need an inline style,
   which the CSP forbids); these two rules are all it needs. */
.htmx-indicator { display: none; }
.htmx-indicator.htmx-request { display: block; }
.odoo-hire { margin-top: 1rem; }
.admin .odoo-hire .admin-table td { vertical-align: middle; }
.admin .odoo-hire .inline-form, .admin .odoo-hire td .btn { margin: 0; }
/* One person across the systems (D-225): the head with the person's own
   tabs, the cards two by two on wide screens. */
.person-head { margin: 0 0 1rem; }
.person-head .person-name { margin: 0.2rem 0 0.4rem; }
.person-tabs { margin-bottom: 0.4rem; }
.person-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0 1.5rem; align-items: start; }
@media (min-width: 62rem) {
  .person-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
.employee-filters .filter-count { margin-left: 0.4rem; font-variant-numeric: tabular-nums; opacity: 0.8; }
.employee-link { text-decoration: none; }
.employee-link:hover strong, .employee-link:focus-visible strong { text-decoration: underline; }
.odoo-link-row { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
.odoo-link-row select { flex: 1 1 18rem; min-width: 0; }
.admin .odoo-link-row .btn { margin-top: 0; }
.odoo-queue { list-style: none; margin: 0.6rem 0 0; padding: 0; }
.odoo-queue > li { padding: 0.7rem 0; border-bottom: 1px solid var(--border); }
.odoo-queue label { display: block; margin-bottom: 0.35rem; }
.odoo-queue label .muted { font-weight: 400; }
.ms-steps { padding-left: 1.3rem; }
.ms-steps > li { margin: 0 0 0.7rem; max-width: 70ch; }
.copy-line { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-top: 0.35rem; }
.copy-line code { padding: 0.3rem 0.5rem; border: 1px solid var(--border); border-radius: 0.35rem; background: var(--surface); user-select: all; word-break: break-all; }
.admin .copy-line .btn { margin-top: 0; }
.lock-state.is-warning { color: var(--warning); }
.leave-jump { margin: 0.2rem 0 0.6rem; }
@media (min-width: 62rem) {
  .leave-jump { display: none; }
  .ledger-side .two-col { grid-template-columns: minmax(0, 1fr); }
}
.leave-card-headline { margin: 0.2rem 0 0.5rem; font-size: 1.6rem; }
.leave-news { margin: 0 0 0.6rem; }
.leave-bonus { margin: 0.9rem 0; padding: 0.2rem 0.9rem 0.6rem; border-left: 4px solid var(--brass); background: var(--brass-soft); border-radius: 0 0.5rem 0.5rem 0; }
.leave-bonus .leave-list-heading { margin-top: 0.6rem; }
/* The yearly notice (D-223): unread it stands out like a letter on the
   desk; read, it folds into one line that still opens. */
.leave-notice { margin: 0.9rem 0; padding: 0.4rem 0.9rem 0.7rem; border-left: 4px solid var(--primary); background: var(--primary-soft); border-radius: 0 0.5rem 0.5rem 0; }
.leave-notice h2 { font-size: 1.05rem; margin: 0.5rem 0 0.4rem; }
.leave-notice ul { margin: 0.3rem 0 0.6rem; padding-left: 1.2rem; }
.leave-notice.is-read { border-left-color: var(--border); background: none; padding-top: 0.2rem; padding-bottom: 0.2rem; }
.leave-notice.is-read > summary { cursor: pointer; color: var(--muted); }
.leave-explain { margin: 0.8rem 0; }
.leave-explain summary { cursor: pointer; font-weight: 600; }
.leave-explain ul { margin: 0.5rem 0 0; padding-left: 1.2rem; max-width: 62ch; }
.leave-explain li { margin: 0.25rem 0; }
.leave-list-heading { font-size: 1rem; margin: 1rem 0 0.3rem; }
/* The queue (D-219): one card per request, the two answers side by side;
   an opened refusal takes the full width below them. */
.leave-waiting { list-style: none; margin: 0; padding: 0; }
.team-page .leave-waiting > li { border: 1px solid var(--border); border-radius: 0.5rem; padding: 0.75rem; margin: 0.75rem 0; }
.leave-waiting > li { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0 0.6rem; }
.leave-waiting > li > p { flex: 1 0 100%; margin: 0 0 0.35rem; }
.leave-waiting > li > .inline-form { margin: 0.25rem 0; }
.leave-waiting > li > details { margin: 0.25rem 0; }
.leave-waiting > li > details[open] { flex: 1 0 100%; }
.leave-actions { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0 0.6rem; margin: 0.35rem 0 0; }
.leave-actions > details { margin: 0.25rem 0; }
.leave-actions > details[open] { flex: 1 0 100%; }
.leave-rows { list-style: none; margin: 0; padding: 0; }
.leave-rows li { padding: 0.35rem 0; border-bottom: 1px solid var(--border); font-variant-numeric: tabular-nums; }

/* The sample clause next to the bonus switch (D-220): a toggletip - a
   click opens it, it stays while the text is read and copied, in place. */
.clause-tip { position: relative; margin: 0.5rem 0 0; }
.clause-tip > summary { display: inline-flex; align-items: center; gap: 0.45rem; cursor: pointer; font-size: 0.9rem; font-weight: 600; color: var(--primary); list-style: none; }
.clause-tip > summary::-webkit-details-marker { display: none; }
.clause-tip > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; border-radius: 0.3rem; }
.clause-tip-icon { display: inline-grid; place-items: center; width: 1.25rem; height: 1.25rem; border-radius: 50%; border: 1.5px solid currentColor; font-size: 0.75rem; font-weight: 700; font-style: italic; font-family: Georgia, serif; }
.clause-tip-panel { margin-top: 0.5rem; padding: 0.9rem 1rem; border: 1px solid var(--card-border); border-radius: 0.6rem; background: var(--surface); box-shadow: var(--shadow); }
.clause-tip-title { font-weight: 600; margin: 0; }
/* Several clauses in one panel (D-253): each title starts a new block. */
.clause-tip-title ~ .clause-tip-title { margin-top: 1.1rem; }
.clause-text { white-space: pre-wrap; font-family: inherit; font-size: 0.85rem; line-height: 1.55; max-height: 22rem; overflow: auto; margin: 0.5rem 0; padding: 0.7rem 0.8rem; background: var(--sunken); border: 1px solid var(--border); border-radius: 0.4rem; color: var(--text); }
.clause-text:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.clause-actions { display: flex; flex-wrap: wrap; gap: 0.5rem 0.9rem; align-items: center; }
.clause-status { margin: 0; }
/* The open panel stays in the flow and pushes what follows down: laid
   over the form, two open tips covered each other and the next card
   (D-256). One tip of a page is open at a time (`name`). */
.clause-tip[open] > .clause-tip-panel { max-width: 42rem; }

/* Booking lock on the dashboard (D-139): the state first, the form
   under it. */
.lock-state { margin: 0.75rem 0 0; font-weight: 600; }
.lock-state.is-locked { color: var(--warning); }
/* The button stands on the line of the field, not of the hint under the
   field: it starts one label's height down (0.9rem × 1.5 and the field's
   gap), and loses the margin the administration's buttons carry, which
   set it 6 px below the field (D-256). */
.lock-form { display: flex; flex-wrap: wrap; gap: 0.75rem 1rem; align-items: flex-start; margin-top: 0.5rem; }
.lock-form .filter-actions { padding-top: 1.6rem; }
.lock-form .btn { margin-top: 0; }

/* The report list (D-146): what there is to read, each with its export. */
.report-list {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.5rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
}
/* Title, description, action: the actions of a row of tiles stand on
   one line, whatever the descriptions' lengths (D-256). */
.report-entry {
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: 0.2rem;
  align-content: start;
  padding: 0.7rem 0.8rem;
  border: 1px solid var(--card-border);
  border-radius: 0.6rem;
  background: var(--sunken);
}
.report-entry.is-current { border-color: var(--primary); box-shadow: inset 3px 0 0 var(--primary); }
.report-name { font-weight: 700; text-decoration: none; }
.report-entry.is-current .report-name { color: var(--text); }
.report-desc { margin: 0; font-size: 0.85rem; color: var(--muted); }
.report-entry .btn-link { justify-self: start; padding-left: 0; }
.report-download { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.9rem; margin: 0.5rem 0 0; flex: 1 0 100%; }
.report-download .field-hint { margin: 0; }

/* Monthly report (D-144): one card per employee for the meeting. */
.meeting-grid {
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr));
}
.meeting-card {
  padding: 0.9rem 1rem;
  border: 1px solid var(--card-border);
  border-radius: 0.7rem;
  background: var(--sunken);
}
.meeting-card h3 { margin: 0 0 0.5rem; font-size: 1rem; }
.meeting-figures { display: grid; gap: 0.15rem 1rem; margin: 0 0 0.6rem; }
.meeting-figures > div { display: flex; justify-content: space-between; gap: 1rem; }
.meeting-figures dt { color: var(--muted); font-size: 0.88rem; }
.meeting-figures dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; }
.meeting-points { margin: 0; padding-left: 1.1rem; font-size: 0.9rem; }
.meeting-points li { margin: 0.2rem 0; }
/* A month below the contract is marked in the figure itself, not with a
   box that shouts across the table. */
.balance-under { color: var(--warning); font-weight: 700; }

/* Preferences switcher in the header: a disclosure, so it needs no
   JavaScript and costs no space until it is opened. */
.prefs { position: relative; }
.prefs > summary { cursor: pointer; list-style: none; }
.prefs > summary::-webkit-details-marker { display: none; }
.prefs-form {
  position: absolute;
  right: 0;
  z-index: 20;
  display: grid;
  gap: 0.35rem;
  min-width: 12rem;
  margin-top: 0.4rem;
  padding: 0.75rem;
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  background: var(--surface);
  box-shadow: var(--shadow);
}
.prefs-form label { font-size: 0.9rem; font-weight: 600; margin: 0; }
.prefs-form select { width: 100%; }
.prefs-account {
  display: block;
  margin-top: 0.35rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--border);
  min-height: 2.25rem;
  color: var(--text);
  font-weight: 600;
}

/* "Mein Zugang" (D-171): two short forms, one per way in, each with its
   state line above it. */
.account-block { margin-top: 1.5rem; padding-top: 1.25rem; border-top: 1px solid var(--border); }
.account-block .state-line { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.25rem 1rem; align-items: baseline; }
.account-block .state-line h2 { margin: 0; }
.account-form { max-width: 26rem; }
.account-form label { display: block; font-size: 0.9rem; margin: 0.75rem 0 0.15rem; }
.account-form input { width: 100%; min-height: 2.75rem; padding: 0.5rem 0.7rem; font: inherit; border: 1px solid var(--border); border-radius: 0.5rem; background: var(--surface); color: var(--text); }
.account-form .btn { margin-top: 1rem; }
.account-block .error, .account-block .warn-warning { margin: 0.75rem 0 0; }

/* Erinnerungen (D-249): one fieldset per mail – its state, when it comes,
   what the start page shows instead, and the one form that changes it. */
.reminder-block p, .reminder-row p { margin: 0.35rem 0 0; }
.reminder-state { font-weight: 600; }
.reminder-state.is-off { color: var(--warning); }
.reminder-off { margin-top: 0.6rem; }
.reminder-off > summary { cursor: pointer; font-weight: 600; min-height: 2.25rem; }
.reminder-form { display: grid; gap: 0.6rem; justify-items: start; margin-top: 0.35rem; max-width: 46rem; }
.reminder-form .check-line { display: flex; gap: 0.5rem; align-items: flex-start; }
.reminder-form .check-line input { margin-top: 0.2rem; flex: none; }
.reminder-list { margin: 0.5rem 0 0; padding-left: 1.25rem; }
.reminder-always { font-weight: 600; }
.reminder-risk { font-style: italic; }
.reminders-form h3, .admin #reminders > h3 { margin: 1.25rem 0 0; }
.admin .reminder-row .filter-field { margin-top: 0.6rem; }
.reminder-row input[type="number"] { max-width: 8rem; }
.reminder-form .check-line span { font-weight: 400; }
.reminder-transparency { margin: 0.75rem 0 0; }

@media (max-width: 30rem) {
  .prefs-form { position: static; min-width: 0; box-shadow: none; }
}

/* A page whose cards break out of the reading column takes its heading
   with it, so heading and cards share their edges (D-256). */
.page-head { margin-block: 0 1.25rem; }
.page-head h1 { margin: 0 0 0.35rem; }
.page-head .lede { margin: 0; }

/* overview */
.overview .rows { font-size: 0.95rem; }
.overview .rows td, .overview .rows th { padding-right: 0.75rem; }
.overview h3 { margin-top: 1.5rem; }
.overview .empty { margin: 0.75rem 0 0; }
/* The period is the card's heading, between its arrows; "Heute" and
   "Diesen Tag öffnen" stand at the right of the same line. */
.overview-head { align-items: center; gap: 0.75rem; }
.overview-head .period-nav { margin-left: 0; }
.overview-head .period-title { font-size: 1.1rem; }
.overview-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 0 auto; }
.overview-hint { margin: 0 0 0.75rem; }

/* One grid for the week and the month (D-256): the figures and the
   dates keep fixed columns, so paging and switching views move nothing;
   the customers take what is left and the notes a fixed share. The
   widths sit on the head – the CSP refuses a style attribute. */
.rows.overview-period { table-layout: fixed; }
.overview-period .col-day { width: 3rem; }
.overview-period .col-date { width: 5rem; }
.overview-period .col-figure { width: 5.25rem; }
.overview-period .col-note { width: 30%; }
/* "Monatssumme" spans the day and the date and ends there. */
.overview-period tfoot th { padding-right: 0; white-space: nowrap; }
.overview-day .col-figure { width: 5.25rem; }
.overview-customers .col-figure { width: 8rem; }
/* The notes of a day stand one under the other, each as narrow as its
   words and wrapping inside the column instead of pushing it wider. */
.rows td.badges .badge {
  display: flex;
  width: fit-content;
  max-width: 100%;
  white-space: normal;
  text-align: left;
  /* Two lines in a pill read as a lozenge; a wrapped note keeps the
     inner radius of the board (D-256). */
  border-radius: 0.6rem;
}
.rows td.badges .badge + .badge { margin-top: 0.25rem; }
/* What explains the sum – the doctor's part, the hours held by an
   automatic end, how a Gleitzeit month splits – is a line of its own
   under it, not a third line under one figure. */
.rows tfoot .row-note td { padding-top: 0.15rem; border-top: 0; }
.rows tfoot tr:first-child + .row-note td { padding-top: 0.35rem; }
@media (max-width: 62rem) {
  .overview .rows { font-size: 0.9rem; }
  .overview-period .col-day { width: 2.75rem; }
  .overview-period .col-date { width: 4.75rem; }
  .overview-period .col-figure { width: 4.5rem; }
  .overview-period .col-note { width: 22%; }
}
/* The figures of the week under the table are one block of notes, not
   three paragraphs a line apart. */
.overview .table-wrap ~ p.muted.small { margin: 0.25rem 0 0; }
.overview .table-wrap + p.muted.small { margin-top: 0.75rem; }

/* View switch: pills, because the three views are one choice, not three
   separate destinations. They are links, so they work without HTMX. */
.overview-views { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 1rem 0; }
.overview-view {
  min-height: 2.5rem;
  display: inline-flex;
  align-items: center;
  padding: 0.35rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-weight: 600;
  text-decoration: none;
  background: var(--surface);
  color: var(--primary);
}
.overview-view.active { background: var(--primary); border-color: var(--primary); color: var(--primary-text); }

.row-customer td:last-child, .overview-customers tfoot td { white-space: nowrap; }

@media (max-width: 36rem) {
  .overview .rows tbody tr { border-bottom: 1px solid var(--border); }
  .overview .rows td:empty { display: none; }
  .overview-head .period-nav { flex: 1 1 100%; }
  .overview-head .period-title { flex: 1 1 auto; min-width: 0; font-size: 1rem; white-space: normal; text-align: center; text-wrap: balance; }
  .overview-actions { margin-left: 0; }
}

/* ------------------------------------------------------------------ */
/* Personal project and task selection (D-123, D-124, D-135)           */
/* ------------------------------------------------------------------ */

.project-list {
  list-style: none;
  margin: 0.75rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 21rem), 1fr));
  align-items: start;
}
.project-row {
  display: grid;
  gap: 0.4rem;
  padding: 0.85rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: 0.7rem;
  background: var(--surface);
}
/* A chosen project is the one that will be offered when booking, so it
   carries the stamp colour on its edge. */
.project-row.is-chosen { border-color: var(--primary); box-shadow: inset 3px 0 0 var(--primary); }
.project-pick { display: flex; gap: 0.6rem; align-items: baseline; margin: 0; cursor: pointer; }
.project-pick input { width: 1.25rem; height: 1.25rem; flex: none; accent-color: var(--primary); }
.project-title { display: grid; gap: 0.05rem; }
.project-name { font-weight: 700; overflow-wrap: break-word; }
.project-customer { font-size: 0.88rem; color: var(--muted); overflow-wrap: break-word; }
/* The badges start where the name starts, past the check box. */
.project-flags { margin: 0; display: flex; flex-wrap: wrap; gap: 0.3rem; padding-left: calc(1.25rem + 0.6rem); }
.project-flags:empty { display: none; }

/* The closed card always says which tasks are chosen; only the editor
   is fetched on demand (D-161). */
.task-picks {
  margin: 0.15rem 0 0;
  padding: 0.6rem 0.7rem;
  border-radius: 0.55rem;
  background: var(--sunken);
  border: 1px solid var(--border);
}
.task-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.5rem;
  margin: 0 0 0.4rem;
}
.task-title { font-weight: 700; font-size: 0.9rem; }
.task-count { color: var(--muted); font-size: 0.85rem; }
.task-count { margin-left: auto; text-align: right; }
/* "Für dieses Projekt sind keine Aufgaben angelegt; …" is a sentence, not
   a count: it reads from the left under the title (D-256). */
.task-head .task-none { flex: 1 0 100%; margin-left: 0; text-align: left; }
.task-pick-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.15rem;
  /* A project with twenty tasks scrolls inside its own card instead of
     pushing every other project off the screen. */
  max-height: 13rem;
  overflow-y: auto;
}
.task-pick {
  display: flex;
  gap: 0.5rem;
  align-items: baseline;
  margin: 0;
  padding: 0.25rem 0.35rem;
  font-size: 0.92rem;
  font-weight: 400;
  border-radius: 0.35rem;
  cursor: pointer;
}
.task-pick.is-chosen { background: var(--primary-soft); font-weight: 600; }
.task-pick input { width: 1.1rem; height: 1.1rem; flex: none; accent-color: var(--primary); }
.task-now { font-weight: 600; }
.task-field { display: grid; gap: 0.25rem; margin: 0.5rem 0; }

/* Filter bar of the project selection (D-131). */
.filter-form {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: end;
  margin: 1rem 0 0.5rem;
}
.filter-field { display: grid; gap: 0.25rem; flex: 1 1 14rem; max-width: 22rem; min-width: 0; }
.filter-field label { font-size: 0.9rem; font-weight: 600; margin: 0; }
.filter-actions { display: flex; gap: 0.5rem; align-items: center; }

/* The two bulk buttons act on whatever the filter left, and say so
   beside themselves (D-131, D-160). */
.bulk-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
  margin: 0.75rem 0 0;
}
.bulk-actions .field-hint { margin: 0; flex: 1 1 14rem; }
/* Each card saves itself, so its own button is the quiet way in for a
   browser without scripts – not the main action of the page. */
.card-apply { justify-self: start; margin-top: 0.2rem; }
.pager { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin: 1rem 0 0; }
.pager-state { color: var(--muted); font-size: 0.9rem; }

/* The save button stays reachable while a long list is being ticked; it
   is opaque, so what it covers is plainly covered and not half-legible. */
.form-actions-sticky {
  position: sticky;
  bottom: 0;
  margin-top: 0.75rem;
  padding: 0.75rem 0;
  background: var(--surface);
  border-top: 1px solid var(--border);
}

/* Reload and the two defaults share one row above the project table. */
.projects-toolbar { display: flex; flex-wrap: wrap; gap: 1rem 2rem; align-items: end; margin: 0.75rem 0; }
.defaults-form { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: end; }
.defaults-form .filter-field { flex: 0 1 11rem; }

/* ------------------------------------------------------------------ */
/* Keeping the catalogue: Verwaltung > Projekte (D-186)                */
/* ------------------------------------------------------------------ */

.admin-projects .notice { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.75rem; }
.admin-projects .notice .btn-link { padding: 0; min-height: 0; }
.customer-group { margin: 1.25rem 0 0; }
.customer-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.25rem 1rem;
  padding-bottom: 0.3rem;
  border-bottom: 1px solid var(--border);
}
.customer-head h2 { margin: 0; font-size: 1.05rem; }
.customer-actions summary { cursor: pointer; font-size: 0.9rem; font-weight: 600; }
.customer-actions form { margin-top: 0.5rem; }
/* An archived project keeps its place but stops shouting: the edge that
   marks a live card is gone and the text steps back. */
.project-row.is-archived { opacity: 0.75; }
.project-row.is-editing { border-color: var(--primary); box-shadow: inset 3px 0 0 var(--primary); }
.project-meta { margin: 0; overflow-wrap: break-word; }
.card-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-top: 0.2rem; }
.task-lines { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.4rem; }
.task-line { display: flex; flex-wrap: wrap; gap: 0.35rem; align-items: center; }
.task-line input[type="text"] { flex: 1 1 10rem; min-width: 0; }
.task-line.is-archived input[type="text"] { opacity: 0.75; }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}
/* Settings › Odoo (D-205): what is set up reads as a short list of facts,
   the actions sit in one row, and what changes or ends the connection
   waits behind a fold - a form nobody needs today should not be the
   loudest thing on the page. */
.odoo-facts { display: grid; gap: 0.35rem; margin: 0 0 0.75rem; }
.odoo-facts > div { display: grid; grid-template-columns: minmax(8rem, 11rem) 1fr; gap: 0.75rem; align-items: baseline; }
.odoo-facts dt { color: var(--muted); font-size: 0.9rem; }
.odoo-facts dd { margin: 0; overflow-wrap: break-word; }
.odoo-facts dd.is-on { color: var(--moss); font-weight: 600; }
.odoo-facts dd.is-off { color: var(--warning); font-weight: 600; }
.odoo-card .warn-info,
.odoo-card .warn-warning,
.odoo-card .warn-critical { margin: 0 0 0.75rem; max-width: 62ch; }
.odoo-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 1rem; }
.odoo-actions form { margin: 0; }
.odoo-change { margin: 0 0 0.75rem; }
.odoo-change > summary { font-weight: 600; }
.odoo-change[open] > summary { margin-bottom: 0.5rem; }
.odoo-change .odoo-other { max-width: 62ch; margin: 0 0 0.75rem; }
.odoo-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: 0.75rem 1rem; max-width: 46rem; }
.odoo-form .filter-field { max-width: none; }
.odoo-form .error,
.odoo-form .odoo-field-wide { grid-column: 1 / -1; }
.odoo-form .field-hint { max-width: 62ch; margin: 0; }
.odoo-disconnect form p { max-width: 62ch; }
/* The first sync (D-194): a section inside the card, the preview as a
   small table of numbers that stays a table on a phone - four short
   columns fit, and a card per number would say less. */
.odoo-first { border-top: 1px solid var(--border); padding-top: 0.75rem; margin: 0 0 1rem; }
.odoo-first h3 { margin: 0 0 0.35rem; font-size: 1.05rem; }
.odoo-first .lede { margin-bottom: 0.75rem; }
.odoo-preview { border-collapse: collapse; margin: 0.25rem 0 0.75rem; }
.odoo-preview th, .odoo-preview td { padding: 0.35rem 0.6rem; border-bottom: 1px solid var(--border); text-align: left; }
.odoo-preview thead th { font-size: 0.82rem; color: var(--muted); font-weight: 600; vertical-align: bottom; }
.odoo-preview tbody th { font-weight: 600; }
.odoo-preview .num { text-align: right; font-variant-numeric: tabular-nums; }
.odoo-preview-lines { margin: 0 0 0.75rem; padding-left: 1.2rem; max-width: 70ch; }
.odoo-preview-lines li + li { margin-top: 0.25rem; }
.odoo-promise { max-width: 70ch; }
/* Employee data to Odoo (D-257): the preview's list of people, the card
   on the person page, the release under Lohn › Firmendaten. */
.staff-preview-heading { margin: 0.75rem 0 0.25rem; font-size: 1rem; }
.staff-preview { list-style: none; margin: 0.5rem 0 0; padding: 0; max-width: 70ch; }
.staff-preview > li { padding: 0.5rem 0; border-bottom: 1px solid var(--border); overflow-wrap: anywhere; }
.staff-preview > li > a { font-weight: 600; }
.staff-preview .odoo-preview-lines { margin: 0.3rem 0 0; }
.staff-preview-result .warn-info { margin: 0.25rem 0 0.5rem; max-width: 62ch; }
.odoo-staff { border-top: 1px solid var(--border); margin-top: 1rem; padding-top: 0.75rem; }
.odoo-staff h3 { margin: 0 0 0.35rem; font-size: 1.05rem; }
.odoo-staff .warn-warning,
.odoo-staff .warn-info { margin: 0 0 0.6rem; max-width: 62ch; }
.odoo-staff .field-hint,
.odoo-release .field-hint,
.odoo-release .lede,
.odoo-release details p { max-width: 62ch; }
.odoo-release .inline-form { display: grid; gap: 0.4rem; justify-items: start; }
/* Switching the push on names the Odoo it writes to (D-257): opened, the
   question takes the whole row below the preview button. */
.staff-confirm[open] { flex-basis: 100%; }
.staff-confirm form { display: grid; gap: 0.4rem; justify-items: start; margin: 0.5rem 0 0; }
.staff-confirm form p { margin: 0; max-width: 62ch; overflow-wrap: anywhere; }
@media (max-width: 30rem) {
  .odoo-preview th, .odoo-preview td { padding: 0.3rem 0.35rem; }
}
@media (max-width: 30rem) {
  .odoo-facts > div { grid-template-columns: 1fr; gap: 0; }
}

/* The cases from the sync (D-204): both sides next to each other, the
   decision under them - on a phone one under the other. */
.case-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 1rem; margin-bottom: 0.5rem; }
.case-head h2 { margin: 0; font-size: 1.05rem; }
.case-reason { margin: 0; color: var(--warning); font-weight: 600; }
.case-select { margin-left: auto; }
.case-sides { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 0.75rem 1.5rem; margin-bottom: 0.75rem; }
.case-side h3 { margin: 0 0 0.35rem; font-size: 0.95rem; color: var(--muted); }
.case-fields { display: grid; gap: 0.2rem; margin: 0; }
.case-fields > div { display: grid; grid-template-columns: minmax(6rem, 8rem) 1fr; gap: 0.5rem; }
.case-fields dt { color: var(--muted); font-size: 0.88rem; }
.case-fields dd { margin: 0; overflow-wrap: break-word; }
.case-decide { display: grid; gap: 0.5rem; max-width: 46rem; }
.case-reason-field { max-width: none; }
/* D-258: employee data has three answers, one a link; on a phone they
   stand under each other, each as wide as the card. */
.case-actions { margin: 0; }
.case-decide .field-hint { margin: 0; }
.case-base { margin: 0 0 0.75rem; }
.odoo-wage-case .case-decide { margin-top: 0.75rem; }
.odoo-wage-case + .odoo-wage-case { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--border); }
.odoo-wage-line { margin: 0 0 0.25rem; }
@media (max-width: 40rem) {
  .case-actions { flex-direction: column; align-items: stretch; }
  .case-actions .btn { width: 100%; text-align: center; }
}
.cases-filter { margin-top: 0.75rem; }

/* Lohn (D-231): offer with a greyed example, and the area itself. */
.payroll-offer h1 { margin-bottom: 0.5rem; }
.payroll-points { margin: 1rem 0; padding-left: 1.2rem; display: grid; gap: 0.35rem; max-width: 70ch; }
.payroll-sample { margin: 1rem 0 0; }
.payroll-sample figcaption { margin-bottom: 0.75rem; }
.payroll-sample-body { opacity: 0.55; filter: grayscale(1); user-select: none; pointer-events: none; }
.payroll-board-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.5rem 1.5rem; }
.payroll-board-head h2 { margin: 0; }
.payroll-deadline { margin: 0; font-weight: 600; color: var(--warning); }
.payroll-steps { list-style: none; margin: 1rem 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem; }
.payroll-steps li { display: flex; align-items: center; gap: 0.45rem; color: var(--muted); }
.payroll-steps li.is-current { color: var(--text); font-weight: 600; }
/* A step still ahead is an empty ring one can see: the hairline colour
   stood at 1.3:1 (D-256). */
.payroll-step-mark { width: 0.85rem; height: 0.85rem; border-radius: 50%; border: 2px solid var(--muted); flex: none; }
.payroll-steps .is-done .payroll-step-mark { background: var(--moss); border-color: var(--moss); }
.payroll-steps .is-current .payroll-step-mark { border-color: var(--primary); background: var(--primary-soft); }
.payroll-sums { display: grid; grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr)); gap: 0.75rem; margin: 0 0 1rem; }
.payroll-sums div { background: var(--sunken); border: 1px solid var(--border); border-radius: 0.5rem; padding: 0.6rem 0.8rem; }
.payroll-sums dt { font-size: 0.8rem; color: var(--muted); }
.payroll-sums dd { margin: 0.15rem 0 0; font-size: 1.15rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.payroll-notes h3 { font-size: 0.95rem; margin: 0 0 0.5rem; }
.payroll-notes ul { list-style: none; margin: 0 0 1rem; padding: 0; display: grid; gap: 0.4rem; }
.payroll-notes li { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.5rem; padding: 0.5rem 0.75rem; border-left: 4px solid var(--warning); background: var(--warning-bg); border-radius: 0.25rem; }
.payroll-table { width: 100%; border-collapse: collapse; }
.payroll-table th, .payroll-table td { padding: 0.45rem 0.5rem; border-bottom: 1px solid var(--border); text-align: left; }
.payroll-table th { font-size: 0.8rem; color: var(--muted); font-weight: 600; }
.payroll-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.payroll-release { margin: 1rem 0 0; text-align: right; }
.payroll-grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr)); align-items: start; }
.payroll-license-lead { font-weight: 600; }
.payroll-license.is-grace .payroll-license-lead { color: var(--warning); }
.payroll-license.is-expired .payroll-license-lead,
.payroll-license.is-refused .payroll-license-lead { color: var(--critical); }
.payroll-checklist { list-style: none; padding: 0; margin: 0 0 0.75rem; }
.payroll-checklist li::before { content: "\25CB"; margin-right: 0.5rem; color: var(--muted); }
.payroll-checklist li.is-done::before { content: "\2713"; color: var(--moss); }
@media (max-width: 40rem) {
  .payroll-table th:nth-child(4), .payroll-table td:nth-child(4) { display: none; }
}

/* Wide screens (D-232). On a 2K or 4K display the app sat in a strip of
   about 1000 px with the rest of the screen empty, and 16 px type read
   small from a desk's distance. From 1600 px on, three things change:
   the type grows in steps (everything is in rem, so spacing and
   widths grow with it), the columns widen, and the start page becomes a
   cockpit - clock, customers and the day on the left, the week and its
   cards beside them instead of below. Breakpoints are in rem of the
   browser default (16 px), independent of the scaled root. */
@media (min-width: 100rem) {
  html { font-size: 106.25%; }
  /* One column for every page: the pages that used to break out of the
     reading column now fill it, so headings and cards line up again. */
  .content { max-width: min(100%, 100rem); }
  .wide,
  .admin,
  .overview { width: auto; margin-inline: 0; }
  .content:has(> #state) {
    display: grid;
    grid-template-columns: minmax(0, 1.7fr) minmax(24rem, 1fr);
    column-gap: 1.5rem;
    align-items: start;
  }
  .content:has(> #state) > * { grid-column: 1; }
  .content:has(> #state) > .ledger { display: contents; }
  .content:has(> #state) .ledger-main { grid-column: 1; }
  .content:has(> #state) .ledger-side { grid-column: 2; grid-row: 1 / span 5; }
  .content:has(> #state) > .footer-line { grid-column: 1 / -1; }
  /* Three columns of cards of any height, filled top to bottom: a grid
     left holes under the short cards. */
  .person-grid { display: block; columns: 3 22rem; column-gap: 1.5rem; }
  .person-grid > .card { break-inside: avoid; }
  .payroll-offer { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: 0 3rem; }
  .payroll-offer > h1, .payroll-offer > .lede { grid-column: 1; }
  .payroll-offer > .payroll-points { grid-column: 2; grid-row: 1 / span 3; margin: 0; align-self: center; }
}
@media (min-width: 120rem) {
  html { font-size: 112.5%; }
}
@media (min-width: 150rem) {
  html { font-size: 125%; }
}

/* Lohndaten einer Person (D-233): the valid version and its history
   beside the form for the next one; one column on narrow screens. */
.payroll-person { display: grid; gap: 0 1.5rem; grid-template-columns: minmax(0, 1fr); align-items: start; }
@media (min-width: 62rem) {
  .payroll-person { grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr); }
}
.pp-summary { margin: 0; display: grid; gap: 0.5rem; }
.pp-summary div { display: grid; grid-template-columns: minmax(9rem, 14rem) 1fr; gap: 0.75rem; align-items: baseline; }
.pp-summary dt { color: var(--muted); font-size: 0.9rem; }
.pp-summary dd { margin: 0; font-variant-numeric: tabular-nums; }
.pp-history { margin: 0; padding-left: 1.2rem; color: var(--muted); font-size: 0.9rem; }
.admin .pp-form { max-width: none; }
.pp-fieldset { border: 1px solid var(--border); border-radius: 0.5rem; padding: 0.75rem 1rem; margin: 0.5rem 0 1rem; display: grid; gap: 0.4rem; }
.pp-fieldset legend { font-weight: 600; padding: 0 0.25rem; }
.admin .stacked-form input.pp-short { max-width: 10rem; }
.pp-child { display: grid; grid-template-columns: minmax(0, 12rem) 1fr; gap: 0.25rem 1rem; align-items: center; }
.pp-child label:first-child { grid-column: 1 / -1; font-size: 0.9rem; }
@media (max-width: 30rem) {
  .pp-summary div { grid-template-columns: 1fr; gap: 0.1rem; }
  .pp-child { grid-template-columns: 1fr; }
}
.pp-fieldset .check-line span { font-weight: 400; }

/* Der Lohnlauf eines Monats (D-236): month switch, the additions as one
   row of fields on wide screens, and the release with its two buttons
   side by side - the primary one last, where the eye ends. */
.payroll-month-nav { display: flex; align-items: center; gap: 0.5rem; }
.payroll-month-nav h2 { margin: 0; }
.payroll-month-nav .btn { min-width: 2.75rem; justify-content: center; }
.payroll-deadline.is-late { color: var(--critical); }
.payroll-table a { font-weight: 600; }
.payroll-extras { margin: 1rem 0 0; border: 1px solid var(--border); border-radius: 0.5rem; padding: 0.6rem 0.9rem; }
.payroll-extras summary { cursor: pointer; font-weight: 600; }
.payroll-extra-list { list-style: none; margin: 0.75rem 0 0; padding: 0; display: grid; gap: 0.25rem; }
.payroll-extra-list li { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.25rem 0.75rem; padding: 0.25rem 0; border-bottom: 1px solid var(--border); }
.payroll-extra-list .inline-form { margin: 0; }
.payroll-extra-form { display: grid; gap: 0.5rem 0.75rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); align-items: end; margin: 0.75rem 0 0.25rem; }
.payroll-extra-form label { display: block; font-size: 0.85rem; margin: 0 0 0.15rem; }
.payroll-extra-form select,
.payroll-extra-form input { width: 100%; }
.payroll-extra-form .field-hint { grid-column: 1 / -1; margin: 0; }
.payroll-extra-action { grid-column: 1 / -1; }
.payroll-release-panel { margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--border); }
.payroll-release-form label { display: block; font-weight: 600; margin: 0 0 0.25rem; }
.payroll-release-form textarea { width: 100%; font: inherit; border: 1px solid var(--border); border-radius: 0.5rem; background: var(--surface); color: var(--text); padding: 0.5rem 0.65rem; }
.payroll-release-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.5rem; margin-top: 0.75rem; }
.payroll-released { font-weight: 600; color: var(--moss); }
.payroll-downloads { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.payroll-reference { font-family: inherit; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.payroll-transfers { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.25rem; }
.payroll-transfers > li { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.25rem 1rem; padding: 0.5rem 0; border-bottom: 1px solid var(--border); }
.payroll-transfers > li.is-done summary { color: var(--muted); }
.payroll-transfers > li.is-done .payroll-transfer-name::after { content: "\00a0\2713"; color: var(--moss); }
.payroll-transfers details { flex: 1 1 18rem; }
.payroll-transfers summary { cursor: pointer; display: flex; justify-content: space-between; gap: 1rem; list-style: none; }
.payroll-transfers summary::-webkit-details-marker { display: none; }
.payroll-transfers summary .payroll-transfer-name::before { content: "\25B8\00a0"; color: var(--muted); }
.payroll-transfers details[open] summary .payroll-transfer-name::before { content: "\25BE\00a0"; }
.payroll-transfers .num { font-variant-numeric: tabular-nums; white-space: nowrap; }
.payroll-transfers .inline-form { margin: 0; display: flex; align-items: center; gap: 0.5rem; }
.payroll-transfer-body { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; margin: 0.5rem 0; }
.payroll-transfer-body .qr { width: 9rem; height: 9rem; border-radius: 0.25rem; }
.payroll-transfer-body dl { margin: 0; display: grid; gap: 0.35rem; }
.payroll-transfer-body dt { font-size: 0.8rem; color: var(--muted); }
.payroll-transfer-body dd { margin: 0; overflow-wrap: anywhere; }
.payroll-lines { table-layout: fixed; }
/* An amount stands on the line of its name, not halfway down to
   "Warum?" (D-256). */
.payroll-lines td { vertical-align: top; }
.payroll-lines th:nth-child(1) { width: 46%; }
.payroll-lines th:nth-child(2), .payroll-lines th:nth-child(4) { width: 20%; }
.payroll-why { margin-top: 0.15rem; }
.payroll-why summary { cursor: pointer; font-size: 0.8rem; color: var(--primary); }
.payroll-why p { margin: 0.25rem 0 0; font-size: 0.85rem; color: var(--muted); }
.payroll-slip h2 { font-size: 1.05rem; margin: 1.25rem 0 0.35rem; }
.company-zip-city { display: grid; grid-template-columns: minmax(0, 7rem) minmax(0, 1fr); gap: 0 0.75rem; }
.admin .company-form { max-width: none; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 26rem), 1fr)); gap: 0 1.5rem; align-items: start; }
.company-form > details, .company-save { grid-column: 1 / -1; }
.company-accounts { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr)); gap: 0 1rem; }
.admin .company-form details summary { cursor: pointer; font-weight: 600; }
.payslip-list { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: 0.25rem; }
.payslip-list li { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.25rem 1rem; padding: 0.6rem 0; border-bottom: 1px solid var(--border); }
.payslip-list a { display: flex; gap: 1rem; font-weight: 600; }
.payslip-payout { font-variant-numeric: tabular-nums; }
@media (max-width: 40rem) {
  /* A payslip line on a phone: what and how much; base and rate stay
     behind "Warum?" and in the PDF. */
  .payroll-lines th:nth-child(4), .payroll-lines td:nth-child(4) { display: table-cell; }
  .payroll-lines th:nth-child(2), .payroll-lines td:nth-child(2),
  .payroll-lines th:nth-child(3), .payroll-lines td:nth-child(3) { display: none; }
  .payroll-lines th:nth-child(1) { width: auto; }
  /* The amount keeps the width of "13.850,00 €"; at a fifth of the
     card it ran past the card's edge (D-256). */
  .payroll-lines th:nth-child(4) { width: 6.5rem; }
}

/* Meldungen an die Sozialversicherung (D-239): one line per file, its
   state as a word in the colour of its outcome. */
.elda-new details + details { margin-top: 0.5rem; }
.elda-new summary { cursor: pointer; font-weight: 600; }
.elda-files { list-style: none; margin: 0.75rem 0 0; padding: 0; display: grid; gap: 0.25rem; }
.elda-files > li { padding: 0.6rem 0; border-bottom: 1px solid var(--border); }
.elda-file-head { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.25rem 1rem; align-items: baseline; }
.elda-file-title { font-weight: 600; }
.elda-state { font-weight: 600; color: var(--muted); }
.elda-files .is-accepted .elda-state { color: var(--moss); }
.elda-files .is-partial .elda-state,
.elda-files .is-failed .elda-state { color: var(--warning); }
.elda-files .is-rejected .elda-state { color: var(--critical); }
.elda-files .is-sent .elda-state { color: var(--primary); }
.elda-files p { margin: 0.15rem 0; }
.elda-codes { margin: 0.25rem 0; padding-left: 1.2rem; font-size: 0.9rem; }
.elda-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.35rem; }
.elda-actions .inline-form { margin: 0; }
.admin .flash ul { margin: 0.35rem 0 0; padding-left: 1.2rem; }
.admin .flash p { margin: 0; }
.pp-child-names { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr)); gap: 0.1rem 0.75rem; align-items: end; }
.pp-child-names label { font-size: 0.85rem; }
.field-legend-inline { font-weight: 600; margin: 0.75rem 0 0.25rem; }

/* Gemeinsame Kundenzeit (D-244): the notice between the confirmation
   and the forms. The answers of a difference stand side by side with the
   same weight; an opened "Aufteilen …" takes the full width below them,
   and "Nicht gemeinsam führen" is a quiet line underneath. */
.joint-notice { border-left: 4px solid var(--primary); }
.joint-notice > h2 { margin-top: 0; }
.joint-item + .joint-item { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--border); }
.joint-item > p { margin: 0 0 0.5rem; max-width: 62ch; }
.joint-actions { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.5rem; margin: 0.5rem 0; }
.joint-actions > details[open] { flex: 1 0 100%; }
.joint-split > summary { list-style: none; }
.joint-split > summary::-webkit-details-marker { display: none; }
.joint-split form { margin-top: 0.5rem; }
.joint-split textarea { width: 100%; }
.joint-lines { border: 0; margin: 0; padding: 0; }
.joint-line { display: flex; gap: 0.5rem; align-items: flex-start; margin: 0.35rem 0; }
.joint-line input { margin-top: 0.3rem; }
.joint-quiet { color: var(--muted); }
.joint-quiet .btn-link { padding-inline: 0.25rem; }
/* Mehrleistung (D-242): the notice sits between the confirmation of a
   booking and the forms, where a phone shows it without scrolling; the
   quick hours are buttons only once the script can fill the field. */
.extra-notice { margin: 0 0 1.5rem; padding: 0; border: 0; background: none; box-shadow: none; }
.extra-notice .notice { margin: 0 0 0.5rem; }
.extra-notice .notice:last-child { margin-bottom: 0; }
.notice-answer > p,
#auto-end-notice .notice > p { margin: 0; max-width: 75ch; }
.notice-answer > .form-actions { margin-top: 0.75rem; }
/* On the notice's own ground the quiet answer needs the paper back, or it
   reads as a word, not as a button. */
.notice .btn-secondary { background: var(--surface); border-color: var(--card-border); }
#auto-end-notice .notice > .link-row { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.75rem; }
#auto-end-notice .btn-quiet { border-color: var(--card-border); color: var(--text); }
.extra-page .extra-figures { list-style: none; padding: 0; }
/* Each part of the page under its heading keeps a gap above it - the
   lede ran into „Stand 2026“ where the jump link is hidden. */
.extra-page > h2 { margin-top: 1rem; }
.vacation-card .plain-fieldset { border: 0; padding: 0; margin: 0.5rem 0 0; }
.quick-fill { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.35rem 0 0; }
.quick-fill:not(:has(.btn:not(.hidden))) { display: none; }
.team-extra h3 { margin: 1.25rem 0 0.25rem; }
.team-extra h4 { margin: 1rem 0 0.25rem; font-size: 1rem; }
/* A question in a form - „Auch nachts?“ (D-255), the compensation of the
   person's own forms: the box beside its text, so a long label wraps
   beside the box and not under it; the answers of a group read in the
   text's weight under their bold question, as on the reminders. Labels
   may use the width. */
.form-question { margin: 0.5rem 0 0.25rem; padding: 0; border: 0; min-width: 0; }
.form-question legend { font-weight: 600; padding: 0; margin: 0; }
.form-question .check-line { display: flex; gap: 0.5rem; align-items: flex-start; margin: 0.35rem 0 0; }
.form-question .check-line input { flex: none; margin: 0.25rem 0 0; }
fieldset.form-question .check-line { font-weight: 400; }
.form-question .field-hint { margin-top: 0.35rem; }
.form-question-hint { margin: 0 0 0.25rem; }
/* A question sent back unanswered is marked like a refused field
   (D-256), but as a red rule along the group: the ring of a refused
   input, drawn round a fieldset, ran through its legend and touched the
   radios. The legend floats so the rule starts above it, not halfway. */
fieldset.form-question[aria-invalid="true"] { box-shadow: inset 3px 0 0 var(--danger); padding: 0.1rem 0 0.25rem 0.85rem; }
fieldset.form-question[aria-invalid="true"] > legend { float: left; width: 100%; }
fieldset.form-question[aria-invalid="true"] > legend + * { clear: left; }
/* One measure for every hint of a form that asks, the question's and the
   others alike (D-255): a narrow hint beside a wide one read as a break. */
.team-extra .field-hint,
#extra-request .field-hint,
#extra-report .field-hint,
.extra-page .field-hint { max-width: 62ch; }
/* A decision that asks gives way in its row: the question spans the row,
   the buttons stay side by side as without it. A refusal that came back
   stands over the question. */
.leave-waiting > li > .question-form { display: contents; }
.leave-waiting > li > .question-form > .form-question,
.leave-waiting > li > .question-form > .hint { flex: 1 0 100%; }
.leave-waiting > li > .question-form > .hint { margin: 0.25rem 0 0; }
.leave-waiting > li > .question-form > .btn { margin: 0.25rem 0; }
/* „Stattdessen beauftragen …“ beside „Genehmigen“: a way of its own,
   opened like „Ablehnen …“, its form indented under it. */
.leave-waiting > li > details.instead-order[open] > form { border-left: 4px solid var(--border); padding-left: 0.75rem; margin: 0.25rem 0 0.5rem; }
/* Gleitzeit (D-247): the card beside the leave card carries the ampel as
   its edge, like a notice; the line in the state card stays quiet unless
   the session runs outside the frame; the month's footer may wrap - it
   is a sentence, not a figure. */
.flex-card { border-left: 4px solid var(--border); }
.flex-card.is-ok { border-left-color: var(--moss); }
.flex-card.is-warning { border-left-color: var(--warning); }
.flex-card.is-critical { border-left-color: var(--critical); }
.flex-card .leave-line.warn-warning, .flex-card .leave-line.warn-critical { margin: 0.5rem 0; }
.flex-card-team { font-weight: 600; }
.clock-card .frame-line { margin: 0.75rem 0 0; max-width: 62ch; }
/* Die Pflichtpause vor dem Eintragen (D-249): dieselbe Zeile wie die
   Erinnerungsmail, solange sie etwas nützt. */
.clock-card .break-line { margin: 0.75rem 0 0; max-width: 62ch; }
.flex-footer { white-space: normal; }
.month-confirm { margin: 1.25rem 0 0; padding: 0.75rem 1rem; border: 1px solid var(--border); border-radius: 0.5rem; background: var(--sunken); }
.month-confirm h2, .month-confirm h3 { margin: 0 0 0.5rem; font-size: 1rem; }
.month-confirm .stacked-form { margin: 0; }
.month-confirm .stacked-form > p { margin: 0 0 0.5rem; max-width: 62ch; }
.team-flex h3 { margin: 1.25rem 0 0.25rem; }
.team-flex h4 { margin: 1rem 0 0.25rem; font-size: 1rem; }
.team-flex .leave-waiting > li > .btn { margin: 0.25rem 0; }
/* One framed block per person, with the person's name as its heading;
   the batches inside stay the boxed rows they are. */
.team-flex .flex-people > li { display: block; }
.team-flex .flex-people > li > h3 { margin: 0 0 0.35rem; font-size: 1.05rem; }
/* The person's own entries: a table that folds into cards on a phone,
   with long reasons broken instead of pushing the page sideways. */
.flex-entries td:last-child { overflow-wrap: break-word; }
/* Green is the quiet edge of the start page's card, not a notice. */
.status-line.flex-ok { border-left: 4px solid var(--moss); padding-left: 0.75rem; }

/* „Ende eintragen“ (D-248): two answers of equal weight, none open. */
.auto-end-answer { margin: 0.75rem 0 0; padding: 0.5rem 0.75rem; border: 1px solid var(--border); border-radius: 0.5rem; }
.auto-end-answer > summary { cursor: pointer; font-weight: 600; }
.auto-end-answer[open] > summary { margin-bottom: 0.5rem; }
.auto-end-choice { display: flex; gap: 0.5rem; align-items: flex-start; margin: 0.25rem 0; font-weight: 400; }
.auto-end-facts { color: var(--muted); }
/* The card's line about the automatic end (D-248): the prompt before the
   limit, the raised limit, or the end that was set – its two actions of
   equal weight. */
.clock-card .auto-end-line { margin: 0.75rem 0 0; }
.auto-end-line p { margin: 0 0 0.35rem; max-width: 62ch; }
.auto-end-line .link-row { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.5rem 0 0; }
.mini-state .auto-end-line { margin: 0.5rem 0 0; font-size: 0.85rem; }
