/* The management dashboard. Desktop-first, the mirror image of the field screens:
   a wide table beats a stack of cards when the job is scanning twenty rows for the
   one that is wrong. */

.page-head {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--s-3);
	margin-bottom: var(--s-5);
}

.page-head h1 {
	font-size: var(--fs-2xl);
}

/* Let the heading block shrink instead of pushing the action onto its own line: a
   long subtitle otherwise sets the minimum width of the whole row. */
.page-head > div {
	min-width: 0;
}

/* A table is the one thing that genuinely cannot reflow to 375px. Let it scroll
   inside its own box rather than pushing the whole page sideways. */
.table-scroll {
	overflow-x: auto;
	margin-bottom: var(--s-5);
	border: 1px solid var(--c-border);
	border-radius: var(--radius-lg);
	background: var(--c-surface);
}

.table {
	width: 100%;
	border-collapse: collapse;
	font-size: var(--fs-sm);
}

.table th,
.table td {
	padding: var(--s-2) var(--s-3);
	text-align: left;
	vertical-align: middle;
	border-bottom: 1px solid var(--c-border);
	white-space: nowrap;
}

.table thead th {
	white-space: normal;
	vertical-align: bottom;
	position: sticky;
	top: 0;
	z-index: 1;
	background: var(--c-surface-sunken);
	font-size: 0.75rem;
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--c-text-muted);
}

.table tbody tr:last-child td {
	border-bottom: 0;
}

.table tbody tr:hover {
	background: var(--c-primary-subtle);
}

/* `th.`/`td.` rather than the bare class: `.table th, .table td` above sets
   `text-align: left` at specificity (0,1,1), which beat a lone `.table__number` at
   (0,1,0) — so every numeric column in the app has been left-aligned since the class
   was written. alpdest spotted it on the Monteure times; it was also the position
   counts, the resort breakdown, the surface list and the import preview
   (26 Aug 2026). */
th.table__number,
td.table__number {
	text-align: right;
}

/* Deactivated rows stay visible — they answer "who was on the team in February?" —
   but recede, so a scan for a live account does not stop on them. */
.table__row--muted td {
	color: var(--c-text-muted);
}

.table__actions {
	display: flex;
	gap: var(--s-2);
	justify-content: flex-end;
}

/* A secondary action next to a primary one: same size and hit area, less shouting. */
.btn--quiet {
	background: transparent;
	border: 1px solid var(--c-border-strong);
	color: var(--c-primary);
	font-weight: 600;
}

.btn--quiet:hover {
	background: var(--c-surface-sunken);
	color: var(--c-primary-hover);
}


/* --- Filters ------------------------------------------------------------- */

/* Tabs in all but name. Which set you are looking at has to be readable without
   reading — a manager lands here dozens of times a week. */
/* No rule underneath. The table below starts with its own header band, so the line
   sat two pixels above another line and read as a mistake (20 Aug 2026). */
.filters {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--s-1);
	margin-bottom: var(--s-3);
}

/* The search box that sits in front of the tabs. Narrow: it is a filter like the
   others, not the main control of the page. */
.filters__search {
	display: flex;
	align-items: center;
	margin-right: var(--s-2);
}

.filters__search input {
	width: 11rem;
	height: 2rem;
	padding: var(--s-1) var(--s-2);
	font-size: var(--fs-sm);
}

/* Between groups of tabs, so five in a row do not read as one list of eight. */
.filters__rule {
	width: 1px;
	align-self: stretch;
	margin: var(--s-1) var(--s-2);
	background: var(--c-border);
}

.filters__item {
	display: inline-flex;
	align-items: center;
	gap: var(--s-2);
	padding: var(--s-2);
	white-space: nowrap;
	margin-bottom: -1px;
	border: 1px solid transparent;
	border-bottom: 2px solid transparent;
	color: var(--c-text-muted);
	font-size: var(--fs-sm);
	font-weight: 600;
	text-decoration: none;
}

.filters__item:hover {
	color: var(--c-text);
	text-decoration: none;
}

.filters__item--on {
	border-bottom-color: var(--c-primary);
	border-radius: var(--radius);
	background: var(--c-primary-subtle);
	color: var(--c-primary);
}

.filters__count {
	padding: 0 var(--s-2);
	border-radius: 999px;
	background: var(--c-surface-sunken);
	font-size: 0.75rem;
	font-weight: 700;
}

.filters__item--on .filters__count {
	background: var(--c-primary-subtle);
	color: var(--c-primary);
}

/* --- Desktop tables ------------------------------------------------------ */

/* Proportional columns rather than content-width ones: the header row should not
   jump sideways when a long email address arrives. */
.table--fixed {
	table-layout: fixed;
}

.table--fixed th:nth-child(1) { width: 18%; }
.table--fixed th:nth-child(2) { width: 22%; }
.table--fixed th:nth-child(3) { width: 8%; }
.table--fixed th:nth-child(4) { width: 12%; }
.table--fixed th:nth-child(5) { width: 12%; }
.table--fixed th:nth-child(6) { width: 8%; }
/* The rest goes to the actions column — enough for both buttons at their widest.
   A flex row with justify-content:flex-end overflows *leftwards* when it runs out of
   room, straight over the status badge, which is how this was first noticed. */

.table--fixed td:nth-child(1),
.table--fixed td:nth-child(2) {
	overflow: hidden;
	text-overflow: ellipsis;
}

.table__actions-head {
	text-align: right;
}

.table__empty {
	padding: var(--s-6);
	text-align: center;
	color: var(--c-text-muted);
}

/* Every row carries the same two buttons, so they are quiet and small — a wall of
   blue down the right-hand side would compete with the data. */
.row-actions {
	display: flex;
	gap: var(--s-2);
	justify-content: flex-end;
}

.btn--sm {
	min-height: 2rem;
	padding: 0 var(--s-3);
	font-size: var(--fs-sm);
}

/* --- Forms --------------------------------------------------------------- */

/* A form does not want the full width of a laptop: the eye should not have to travel
   across empty space between a label and its input. */
.form-page {
	max-width: 34rem;
}

.form-page .card {
	margin-bottom: var(--s-4);
}

/* Two fields that belong together, side by side above 40rem. */
.field-grid {
	display: grid;
	gap: var(--s-3);
}

@media (min-width: 40rem) {
	.field-grid {
		grid-template-columns: 1fr 1fr;
	}
}

/* Facts about an existing person: read-only, quiet, and not pretending to be a form. */
.meta-list {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: var(--s-1) var(--s-4);
	margin-bottom: var(--s-4);
	padding: var(--s-3) var(--s-4);
	background: var(--c-surface-sunken);
	border-radius: var(--radius-lg);
	font-size: var(--fs-sm);
}

.meta-list dt {
	color: var(--c-text-muted);
}

.meta-list dd {
	margin: 0;
	font-variant-numeric: tabular-nums;
}

/* The role radios read as a list of sentences, not a row of dots. */
.field--radio ul,
.form-page ul {
	list-style: none;
	margin: 0;
	padding: 0;
}

.form-page li label {
	display: flex;
	align-items: baseline;
	gap: var(--s-2);
	padding: var(--s-1) 0;
	font-weight: 400;
}


/* --- Reference data hub --------------------------------------------------- */

.cards {
	display: grid;
	gap: var(--s-3);
	grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
}

.card--link {
	display: block;
	color: inherit;
	text-decoration: none;
	transition: border-color 120ms, box-shadow 120ms;
}

.card--link:hover {
	color: inherit;
	border-color: var(--c-primary);
	box-shadow: var(--shadow-lg);
	text-decoration: none;
}

.card__count {
	margin-top: var(--s-3);
	font-weight: 700;
}

/* A path back up, for screens two levels deep. The dashboard nav only reaches the
   hub, and a list of resorts is one further in. */
.crumbs {
	display: flex;
	align-items: center;
	gap: var(--s-2);
	margin-bottom: var(--s-3);
	font-size: var(--fs-sm);
	color: var(--c-text-muted);
}


/* --- Position list -------------------------------------------------------- */

/* Filters wrap rather than scroll: on a laptop they fill one line, on a narrow
   window they stack, and nothing is ever hidden behind a horizontal scrollbar. */
/* No card around it. It is a row of controls, not a panel — and a white box on a
   white-ish page mostly announced its own edges (20 Aug 2026). Same treatment as the
   tabs on Personen. */
.filter-bar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--s-2);
	margin-bottom: var(--s-3);
}

/* A bar with rows in it: the positions list has nine controls, and one line made
   every one of them too narrow to read (18 Sep 2026). Each row lays out like the bar
   itself, so a bar without rows is unchanged. */
.filter-bar:has(.filter-bar__row) {
	display: grid;
	gap: var(--s-2);
}

.filter-bar__row {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--s-2);
}

/* The same height as the controls it sits between. `.btn--sm` is 2rem, which read as
   a button that had slipped out of the row. */
.filter-bar .btn {
	min-height: 2.5rem;
	height: 2.5rem;
}

/* A third of the bar, and it keeps that width while the dropdowns beside it take what
   they need — it is typed into rather than picked from, and an Alpdest-ID plus a
   location does not fit in the width of a select. It only gives way once the row is
   too narrow to hold all four controls, and then it wraps to a line of its own. */
.filter-bar__row .filter-bar__search {
	flex: 0 1 33%;
	min-width: 16rem;
}

/* One line, and the same height throughout. A select sized to its longest option —
   "DOOH Switzerland Country Packages" — is a control four times the width of the one
   beside it, and eight of them wrapped onto three rows (alpdest, 20 Aug 2026).

   `text-overflow: ellipsis` cuts the *closed* control only; the open dropdown is
   drawn by the operating system and shows every option in full, so nothing is
   actually hidden from the reader. */
.filter-bar select,
.filter-bar input[type="search"],
.filter-bar input[type="date"],
.filter-bar input[type="text"] {
	height: 2.5rem;
	min-height: 0;
	padding: var(--s-1) var(--s-2);
	font-size: var(--fs-sm);
}

/* A date input is full width from 05-forms, which put each of the gallery's two on a
   line of its own and pushed everything else apart. It only ever holds a date. */
.filter-bar input[type="date"] {
	flex: 0 0 auto;
	width: auto;
	min-width: 0;
}

.filter-bar select {
	flex: 0 1 auto;
	width: auto;
	max-width: 7.75rem;
	text-overflow: ellipsis;
}

/* Wide enough for what is *chosen*, not for the longest option. `field-sizing` asks
   the control to size to its own value, so "Diese Woche & später" is readable and
   "Alle Länder" stays narrow; the cap keeps a resort called "DOOH Switzerland Country
   Packages" from taking the row, and the ellipsis below still cuts that one. Browsers
   without `field-sizing` fall back to the cap, which is wider than the old 7.75rem
   and shows the same labels in full (alpdest, 18 Sep 2026). */
.filter-bar__row select {
	field-sizing: content;
	max-width: 15rem;
	min-width: 5rem;
}

/* The labels are visually hidden but still flex items, so each one was contributing
   its own gap: eight of them cost 70px of the line and pushed the last two controls
   onto a second row. Taking them out of the flow costs nothing — a screen reader
   finds them through `for`, not through where they sit. */
.filter-bar .visually-hidden {
	position: absolute;
}

/* The search box gets whatever the selects leave. It shrinks before they do, because
   a search box is still usable narrow and a truncated dropdown is not. */
.filter-bar__search {
	flex: 1 1 9rem;
	min-width: 7rem;
}

.filter-bar__search input {
	width: 100%;
}

/* Push the reset to the end of the line, whatever is in front of it, and match the
   height of the controls it sits beside — a shorter button on the same row reads as
   a different kind of thing. */
.filter-bar > .btn:last-child {
	margin-left: auto;
	height: 2.5rem;
	min-height: 0;
	white-space: nowrap;
}

/* Sits above the table so the count and the action are where the eye already is
   after ticking a row. */
.bulk-bar {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	min-height: 2.5rem;
	margin-bottom: var(--s-2);
}

/* `.row-actions` is right-aligned because it lives in the last cell of a table row.
   In a card it should start where the heading above it does. */
.card > .row-actions {
	justify-content: flex-start;
}

/* A state as a word rather than a badge. In a table a badge sets the row height and
   makes every line taller than its text needs — and it is what pushed the action
   buttons out of line with their own header (20 Aug 2026). */
.state {
	font-weight: 600;
}

.state--on { color: var(--c-success); }
.state--off { color: var(--c-text-muted); }

/* Tighter rows on the people list. Seven columns of one short line each do not need
   the height a report row does. */
.table--tight th,
.table--tight td {
	padding-top: var(--s-1);
	padding-bottom: var(--s-1);
	white-space: nowrap;
}

.table--tight .row-actions {
	gap: var(--s-1);
}

/* An edit screen that also has something to read beside it: the form on the left, the
   account's own facts on the right, the same two-panel shape as the position detail.
   `.form-page` stays narrow for creating somebody, where there is nothing to show. */
.form-page--wide {
	max-width: none;
}

/* A form whose content is two boxes side by side. Wider than a single column of
   fields, narrower than the whole laptop — two small cards stretched across 1600px
   read as a page that lost its content. */
.form-page--pair {
	max-width: 58rem;
}

/* Every status the same width, so the column reads as a column rather than as ragged
   text of five different lengths. 7ch fits "Erledigt", the longest of them. */
.badge--block {
	display: inline-block;
	min-width: 7ch;
	text-align: center;
}

/* An <abbr> here is a code with its full name on the title. No dotted underline —
   there are three of them per row and the table would look ruled. */
.table abbr {
	text-decoration: none;
	border-bottom: none;
	cursor: help;
}

.table__pick {
	width: 2.5rem;
	text-align: center;
}

.table__pick input {
	width: 1.1rem;
	height: 1.1rem;
	min-height: 0;
}

.table__sortable a {
	display: inline-flex;
	align-items: center;
	gap: var(--s-1);
	color: inherit;
	text-decoration: none;
	white-space: nowrap;
}

.table__sortable a:hover {
	color: var(--c-primary);
}

.table__sortable--on {
	color: var(--c-primary);
}

.pagination {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--s-3);
	margin-bottom: var(--s-6);
}

.pagination__where {
	font-size: var(--fs-sm);
	color: var(--c-text-muted);
}

/* --- Position detail ------------------------------------------------------ */

.detail-grid {
	display: grid;
	gap: var(--s-3);
	margin-bottom: var(--s-5);
	grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
}

.detail-grid .meta-list {
	background: transparent;
	padding: 0;
}

.reports h2 {
	font-size: 0.8125rem;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--c-text-muted);
	margin-bottom: var(--s-3);
}

/* One card per report, newest first — the answer to "why is this still open?". */
.report-card {
	padding: var(--s-4);
	margin-bottom: var(--s-3);
	background: var(--c-surface);
	border: 1px solid var(--c-border);
	border-radius: var(--radius-lg);
}

.report-card__head {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--s-3);
	margin-bottom: var(--s-2);
}

.photo-gallery {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
	gap: var(--s-2);
	margin-top: var(--s-3);
	list-style: none;
	padding: 0;
}

.photo-gallery img {
	width: 100%;
	/* `height: auto` is load-bearing, not tidiness. The <img> carries width and
	   height attributes so the row does not jump while the photo loads, and those
	   map to CSS as presentational hints. A hint only loses to a real rule on the
	   same property — nothing here set `height`, so `height: 900px` survived and
	   `aspect-ratio` was ignored, because it applies only when height is auto. The
	   result was a 160×900 sliver of one photo. */
	height: auto;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	border-radius: var(--radius);
	background: var(--c-surface-sunken);
}

.plain-list {
	list-style: none;
	margin: var(--s-2) 0 0;
	padding: 0;
	font-size: var(--fs-sm);
}

.plain-list li {
	padding: var(--s-1) 0;
	border-bottom: 1px solid var(--c-border);
}

/* A cell of free text among columns of codes and numbers. Capped and clipped: a
   Standortdetail is a sentence, and one long one otherwise decides the width of the
   whole table. The full text is on the surface, one click away. */
.table__note {
	max-width: 18rem;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.text-danger {
	color: var(--c-danger);
	font-weight: 600;
}

/* Not an error — something the office still has to get to. A provisional ID and a
   board with no measurements are both work waiting, not faults. */
.text-warning {
	color: var(--c-warning);
	font-weight: 600;
}


/* --- Week dashboard ------------------------------------------------------- */

.counters {
	display: grid;
	gap: var(--s-3);
	margin-bottom: var(--s-5);
	grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}

/* A coloured edge rather than a coloured card: four saturated blocks across the top
   of a screen compete with the data underneath, and the point of the colour is only
   to say which number is the worrying one. */
.counter {
	position: relative;
	background: var(--c-surface);
	border: 1px solid var(--c-border);
	border-left: 4px solid var(--c-border-strong);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow);
}

.counter--danger { border-left-color: var(--c-danger); }
.counter--success { border-left-color: var(--c-success); }
.counter--warning { border-left-color: var(--c-warning); }
.counter--info { border-left-color: var(--c-primary); }
.counter--muted { border-left-color: var(--c-border-strong); }

.counter__peek {
	display: block;
	padding: var(--s-3) var(--s-4);
	color: inherit;
	text-decoration: none;
}

.counter__peek:hover {
	color: inherit;
	text-decoration: none;
	background: var(--c-primary-subtle);
	border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
}

.counter__label {
	display: block;
	font-size: 0.6875rem;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--c-text-muted);
}

.counter__value {
	display: block;
	font-size: 2rem;
	font-weight: 700;
	line-height: 1.15;
}

.counter__note {
	display: block;
	font-size: var(--fs-sm);
	color: var(--c-text-muted);
}

/* Its own target, in the corner, because it does something different from the card:
   the card peeks, this leaves. */
.counter__arrow {
	position: absolute;
	top: var(--s-2);
	right: var(--s-3);
	padding: 0 var(--s-1);
	color: var(--c-text-muted);
	text-decoration: none;
	font-size: var(--fs-lg);
	line-height: 1;
}

.counter__arrow:hover {
	color: var(--c-primary);
	text-decoration: none;
}

/* Two columns on a laptop, not three: these panels are lists of sentences, and a
   third column narrows each one to the point where every line wraps. */
.week-grid {
	display: grid;
	gap: var(--s-3);
	margin-bottom: var(--s-3);
	grid-template-columns: repeat(auto-fit, minmax(30rem, 1fr));
	/* Each panel is its own height. Stretching them to match the tallest in the row
	   left a short list sitting in an acre of white while the reader scrolled past
	   nothing. */
	align-items: start;
}

/* One under the other, full width. They were side by side for an afternoon and there
   was not room: a table of five status columns and a table of nine day columns each
   had about 40 characters to live in (20 Aug 2026). */
.week-stack {
	display: flex;
	flex-direction: column;
	gap: var(--s-3);
}

/* A card is a block with something after it. Without this the problems table sat flush
   against the panel below and the two read as one long table. */
.card {
	margin-bottom: var(--s-3);
}

/* The country a run of resorts belongs to, with its own totals. A row, so it scrolls
   with the table it labels — and deliberately quieter than the header above it: it is
   a divider inside the data, not a second set of column names. Styled the same, the
   table read as two tables (20 Aug 2026). */
.table__group td {
	padding-top: var(--s-2);
	padding-bottom: var(--s-2);
	background: var(--c-surface-sunken);
	border-bottom: 1px solid var(--c-border);
	font-weight: 600;
	font-size: var(--fs-sm);
	color: var(--c-text-muted);
}

/* Assigning: the positions on the left, the people on the right. The question is
   "who goes to *these*", and it is easier to answer with both on screen. */
.assign-grid {
	display: grid;
	gap: var(--s-3);
	grid-template-columns: 1fr;
	align-items: start;
	margin-bottom: var(--s-3);
}

@media (min-width: 60rem) {
	.assign-grid {
		grid-template-columns: minmax(18rem, 1fr) 2fr;
	}
}

.assign-list {
	max-height: 22rem;
	overflow-y: auto;
}

.assign-list li {
	display: flex;
	flex-direction: column;
	padding: var(--s-1) 0;
	border-bottom: 1px solid var(--c-border);
	font-size: var(--fs-sm);
}

.assign-list li:last-child { border-bottom: none; }

/* A position that cannot be moved: its reason in red under it, and a rule down the
   side so a long list shows where the blockers are without reading every row. */
.assign-list li.assign-list__blocked {
	padding-left: var(--s-2);
	border-left: 3px solid var(--c-danger);
}

.move-notes {
	display: grid;
	gap: var(--s-1);
	margin: var(--s-3) 0 0;
	padding-left: var(--s-4);
}

/* Three columns of names. Five down one side of a wide card is a lot of scrolling
   past nothing, and the list only grows. */
/* Django wraps a CheckboxSelectMultiple in a plain <div> of <div>s — the grid goes
   on that wrapper. One column on a phone, three once there is room: this screen is
   the office's, but it has to survive being opened on a tablet. */
/* `.assign-people` is gone: it laid out a flat column of checkboxes as a grid, and
 * every screen that had one now uses the people picker, which lays itself out. The
 * rules survived the swap and spread the picker's own parts across five columns
 * (2 Sep 2026).
 */

/* A link that is not the point of the cell — the whole row already opens the thing,
   and an underline under every ID turns a table into a page of links. The colour
   still says it is clickable. */
.link-plain {
	text-decoration: none;
}

.link-plain:hover {
	text-decoration: underline;
}

/* A number in a table cell that opens the list filtered to it. Coloured by the status
   it counts, so the column reads as a status column and not as four columns of
   arithmetic. */
.cell-link {
	display: inline-block;
	min-width: 1.75rem;
	padding: 0 var(--s-1);
	border-radius: var(--radius);
	font-weight: 600;
	text-decoration: none;
}

.cell-link:hover { text-decoration: none; background: var(--c-surface-sunken); }

.cell-link--planned { color: var(--c-text-muted); }
.cell-link--open { color: var(--c-status-open); }
.cell-link--problem { color: var(--c-status-problem); }
.cell-link--done { color: var(--c-status-done); }

/* The sentence under a table that says what its numbers are and are not. */
.card__note {
	margin-top: var(--s-3);
	padding-top: var(--s-2);
	border-top: 1px solid var(--c-border);
}

/* A whole row that opens something. The cell that carries the real <a> is what makes
   it reachable by keyboard and by a middle click; this only widens the target for a
   mouse. */
.row-link { cursor: pointer; }
.row-link:hover { background: var(--c-primary-subtle); }

/* Compact enough to scan: a problem row is read to answer "is any of this mine?",
   and the sentence is the part that answers it. */
.problems td { vertical-align: top; }
.problems td:last-child { width: 40%; }

.monteur-table th,
.monteur-table td,
.resort-table th,
.resort-table td {
	padding-inline: var(--s-2);
	white-space: nowrap;
}

.week-grid .card__title,
.card__title {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--s-3);
	font-size: 0.8125rem;
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--c-text-muted);
	margin-bottom: var(--s-3);
}

/* A second heading inside a card — "Aktionen" under the account facts. Same
   voice as the card's own title, set off by a rule rather than by size, so it
   reads as part of the card and not as a card of its own. */
.card__subtitle {
	margin: var(--s-4) 0 var(--s-2);
	padding-top: var(--s-3);
	border-top: 1px solid var(--c-border);
	font-size: 0.8125rem;
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--c-text-muted);
}

.card__more {
	font-size: var(--fs-sm);
	font-weight: 400;
	letter-spacing: 0;
	text-transform: none;
	white-space: nowrap;
}

/* One row in a panel: an id, a body that takes the slack, and a tail that does not
   wrap. Used by three of the four panels, so they line up with each other. */
.line {
	display: flex;
	align-items: baseline;
	gap: var(--s-3);
	padding: var(--s-2) 0;
	border-bottom: 1px solid var(--c-border);
	color: inherit;
	text-decoration: none;
}

.line:last-child {
	border-bottom: 0;
}

.line:hover {
	color: inherit;
	text-decoration: none;
	background: var(--c-primary-subtle);
}

.line__id {
	flex: none;
	font-weight: 700;
	font-size: var(--fs-sm);
}

.line__body {
	flex: 1;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: 2px;
}

.line__tail {
	flex: none;
	display: flex;
	align-items: center;
	gap: var(--s-2);
}

.bar-row {
	display: grid;
	grid-template-columns: 8rem 1fr 3.5rem;
	align-items: center;
	gap: var(--s-3);
	padding: var(--s-1) 0;
	font-size: var(--fs-sm);
}

.bar-row__track {
	height: 6px;
	border-radius: 999px;
	background: var(--c-surface-sunken);
	overflow: hidden;
}

.bar-row__fill {
	display: block;
	height: 100%;
	border-radius: 999px;
	background: var(--c-primary);
}

.bar-row__value {
	text-align: right;
	font-weight: 600;
}

/* --- Submit matrix -------------------------------------------------------- */

/* Seven columns per person: somebody who stopped submitting on Wednesday is a gap you
   see, rather than a number you have to work out. */
.submit-matrix__day {
	text-align: center;
	width: 4.5rem;
}

.submit-matrix__day span {
	display: block;
	font-weight: 400;
	text-transform: none;
	letter-spacing: 0;
}

.submit-matrix__cell {
	text-align: center;
	font-size: var(--fs-lg);
	line-height: 1;
}

.submit-matrix__cell--submitted { color: var(--c-success); }
.submit-matrix__cell--reopened { color: var(--c-warning); }
.submit-matrix__cell--draft { color: var(--c-text-muted); }

/* --- Drill-down dialog ---------------------------------------------------- */

.drill {
	width: min(56rem, 92vw);
	max-height: 86vh;
	padding: 0;
	border: 0;
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-lg);
	background: var(--c-surface);
	color: var(--c-text);
}

.drill::backdrop {
	background: rgb(20 24 28 / 0.45);
}

.drill__inner {
	position: relative;
	padding: var(--s-5);
	max-height: 86vh;
	overflow-y: auto;
}

.drill h2 {
	font-size: var(--fs-xl);
	margin-bottom: var(--s-4);
	padding-right: var(--s-6);
}

.drill__close {
	position: absolute;
	top: var(--s-3);
	right: var(--s-3);
	display: grid;
	place-items: center;
	width: var(--tap-min);
	height: var(--tap-min);
	border-radius: var(--radius);
	background: transparent;
	font-size: var(--fs-xl);
	line-height: 1;
	cursor: pointer;
}

.drill__close:hover {
	background: var(--c-surface-sunken);
}

/* No ring on the panel itself. Focus is moved here on open so that the close button
   is not what gets outlined, and the global ring would then frame the whole dialog. */
.drill:focus,
.drill:focus-visible {
	outline: none;
}

/* Says which slice of the data is on screen when somebody arrives from elsewhere. */
.scope-banner {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--s-3);
	padding: var(--s-2) var(--s-3);
	margin-bottom: var(--s-3);
	background: var(--c-primary-subtle);
	border-radius: var(--radius);
	font-size: var(--fs-sm);
}


/* --- Reopening a Monteur's week ------------------------------------------- */

/* The hours on one side, the positions on the other. Both halves of the lock on
   screen at once, because the question is "which of these is wrong" and the answer
   is easier to see beside the week it sits in. */
.reopen-grid {
	display: grid;
	gap: var(--s-3);
	grid-template-columns: 1fr;
	margin-bottom: var(--s-3);
}

@media (min-width: 62rem) {
	.reopen-grid {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
		align-items: start;
	}
}

/* A checkbox and its label, aligned on the first line rather than centred: these
   labels run to three lines and a centred box floats away from the word it marks. */
.reopen-pick {
	display: flex;
	align-items: flex-start;
	gap: var(--s-3);
	padding: var(--s-2) 0;
	cursor: pointer;
}

.reopen-pick > span {
	display: flex;
	flex-direction: column;
	gap: var(--s-1);
	min-width: 0;
}

.reopen-pick input[type="checkbox"] {
	margin-top: 0.2rem;
	flex: none;
}

.reopen-list {
	display: flex;
	flex-direction: column;
	gap: 0;
}

.reopen-item {
	border-bottom: 1px solid var(--c-border);
}

.reopen-item:last-child {
	border-bottom: none;
}

.reopen-item--open {
	background: var(--c-warning-subtle);
}

.reopen-item__head {
	display: flex;
	align-items: center;
	gap: var(--s-2);
	flex-wrap: wrap;
}

/* What the Monteur wrote. Quoted rather than plain, so it reads as their sentence
   and not as the app's. */
.reopen-item__note {
	font-size: var(--fs-sm);
	border-left: 2px solid var(--c-border-strong);
	padding-left: var(--s-2);
	color: var(--c-text);
}

.reopen-item__warn {
	font-size: var(--fs-sm);
	color: var(--c-warning);
}


/* --- Importing a briefing ------------------------------------------------ */

/* A row the import cannot use. Tinted rather than outlined: several in a row would
   otherwise draw a ladder of boxes down the table. */
.import-row--error {
	background: var(--c-danger-subtle);
}

/* One reason, under the badge that summarises the row. Block-level so several stack
   rather than running together into a paragraph, and wrapping rather than clipped —
   "Ändert: location_note, map_url, photos_url, frame_width_mm, frame_heigh…" is the
   half of the sentence that says nothing. */
.import-problem {
	display: block;
	margin-top: var(--s-1);
	font-size: var(--fs-sm);
	line-height: 1.35;
	white-space: normal;
	overflow-wrap: anywhere;
	color: var(--c-warning);
}

/* The outcome column carries the reasons, so it gets the room. The two free-text
   columns beside it give it up: a location and an Auftrag are both readable
   truncated, and a reason is not. */
.import-outcome {
	min-width: 16rem;
}

.import-problem--error {
	color: var(--c-danger);
}

.import-blocked {
	border-left: 4px solid var(--c-danger);
}

/* The preview's counters are a summary above a table, not the week dashboard's row
   of cards — smaller, and without the arrow that opens something. */
.counters--compact {
	grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
	margin-bottom: var(--s-4);
}

.counters--compact .counter {
	padding: var(--s-3);
}

.counters--compact .counter__value {
	font-size: var(--fs-lg);
}

/* Scoped to the compact set. On the week dashboard a counter's tone colours its left
   stripe and nothing else — an unscoped rule here turned Probleme red and Offen gold
   on a screen nobody had asked to change (26 Aug 2026). */
.counters--compact .counter--danger .counter__value {
	color: var(--c-danger);
}


/* A file drop target that is also the picker: a <label> around a hidden input, so it
   works with no JavaScript at all and reads in the app's language rather than the
   browser's. */
.dropzone {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--s-1);
	padding: var(--s-5) var(--s-4);
	border: 2px dashed var(--c-border-strong);
	border-radius: var(--radius);
	background: var(--c-surface-sunken);
	text-align: center;
	cursor: pointer;
}

.dropzone:hover,
.dropzone:focus-within {
	border-color: var(--c-primary);
	background: var(--c-primary-subtle);
}

/* While a file is over it. Deliberately the same treatment as hover — the target is
   the whole box either way, and a second colour would be one distinction too many. */
.dropzone--over {
	border-color: var(--c-primary);
	background: var(--c-primary-subtle);
}

.dropzone--filled {
	border-style: solid;
	border-color: var(--c-success);
	background: var(--c-success-subtle);
}

.dropzone__icon {
	font-size: 1.5rem;
	line-height: 1;
	color: var(--c-text-muted);
}

.dropzone__title {
	font-weight: 600;
}

.dropzone__hint {
	font-size: var(--fs-sm);
	color: var(--c-text-muted);
}

.dropzone__chosen {
	margin-top: var(--s-2);
	font-family: var(--font-mono, inherit);
	font-size: var(--fs-sm);
	color: var(--c-success);
	overflow-wrap: anywhere;
}

/* Changing the week from the preview, on one line above the table. */
.week-bar {
	display: flex;
	align-items: center;
	gap: var(--s-2);
	flex-wrap: wrap;
	margin-bottom: var(--s-3);
}

.week-bar__label {
	font-size: var(--fs-sm);
	color: var(--c-text-muted);
}

.week-bar select {
	width: auto;
	min-height: 0;
	height: 2.5rem;
	padding: var(--s-1) var(--s-2);
	font-size: var(--fs-sm);
}

/* A row the import will skip — already planned in this week. Not a fault, so it is
   tinted rather than flagged, and warmer than the error rows above it. */
.import-row--skip {
	background: var(--c-warning-subtle);
}

.counters--compact .counter--warning .counter__value {
	color: var(--c-warning);
}


/* The file and the week, side by side: one decision in two boxes. */
.upload-grid {
	display: grid;
	gap: var(--s-3);
	grid-template-columns: 1fr;
	margin-bottom: var(--s-3);
}

@media (min-width: 48rem) {
	.upload-grid {
		grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
		align-items: start;
	}
}


/* --- Export --------------------------------------------------------------- */

.export-tile {
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
}

.export-tile .form-actions {
	margin-top: auto;
}

/* The "only these Monteure" list, folded away: the common case is everybody, and
   forty checkboxes above the download button would bury it. */
.export-people {
	margin: var(--s-3) 0;
}

.export-people summary {
	cursor: pointer;
	font-weight: 600;
	padding: var(--s-2) 0;
}

/* Wider than the field app's 28rem info panel: a date range side by side and three
   columns of names do not fit in a phone-sized box. Set on the <dialog> itself —
   widening the *body* instead pushes it out of a panel that stays 28rem, which is
   what put half this form off the left edge (26 Aug 2026). */
#work-time-export,
#position-download {
	width: min(38rem, calc(100vw - 2 * var(--s-4)));
}

/* --- Benachrichtigungen ------------------------------------------------- */

/* The three explanations above the table. Wider than the default card grid: each
   one is a paragraph, and three narrow columns of prose is three columns nobody
   finishes reading. */
.cards--notify {
	grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
	margin-bottom: var(--s-4);
}

/* One block per mail on the edit form. `fieldset` is reset to nothing globally
   (01-reset), so the card styling has to be asked for rather than inherited. */
.notify-group {
	margin-bottom: var(--s-3);
}

/* The people-list cell. A link wrapping badges, so the whole cell is the target
   rather than each badge separately — and an empty one still has something to
   click, which is the row that most needs it. */
.notify-cell {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-1);
	align-items: center;
	text-decoration: none;
	color: inherit;
	min-height: 1.5rem;
}
.notify-cell:hover .badge--quiet {
	border-color: var(--c-primary);
	color: var(--c-primary);
}

/* A badge that names a thing rather than warning about one. The status badges are
   solid colour because a problem should catch the eye; these are three neutral
   labels in a dense table and would only add noise. */
.badge--quiet {
	background: transparent;
	color: var(--c-text-muted);
	border: 1px solid var(--c-border);
	font-weight: 500;
	white-space: nowrap;
	/* Tighter than a status badge: this is a scan column with up to three of them in
	   a cell, and at the normal size two would not sit on one line. */
	font-size: 0.72rem;
	padding: 0.05rem 0.3rem;
	line-height: 1.5;
}

.d-block {
	display: block;
}

/* --- Benachrichtigungen: Standard je Rolle ------------------------------ */

/* The role default lives in the card that explains the mail, so the switch and the
   sentence somebody needs in order to answer it are in one box. */
.notify-default {
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
}
.notify-default__controls {
	margin-top: auto;
	padding-top: var(--s-3);
	border-top: 1px solid var(--c-border);
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
	align-items: flex-start;
}
.notify-default__when {
	display: flex;
	gap: var(--s-2);
	width: 100%;
}
.notify-default__when > .field {
	flex: 1;
	min-width: 0;
}

/* --- Bulk selection ----------------------------------------------------- */

.bulk-bar {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-2);
	align-items: center;
	padding: var(--s-2) var(--s-3);
	margin-bottom: var(--s-2);
	border: 1px solid var(--c-border);
	background: var(--c-surface-muted, #f4f6fa);
}
.bulk-bar__count {
	font-size: var(--fs-sm);
	color: var(--c-text-muted);
	margin-right: auto;
}
/* Selects are full-width by default (05-forms), which turned a one-line bar into
   three stacked rows. They hold short labels and can size to them. */
.bulk-bar select {
	width: auto;
	min-width: 9rem;
	max-width: 100%;
}
/* Armed only once something is ticked, so the bar does not shout at an idle screen. */
.bulk-bar--armed .bulk-bar__count {
	color: var(--c-primary);
	font-weight: 600;
}

.table__check {
	width: 2.5rem;
	text-align: center;
}
.table__check input {
	margin: 0;
}

/* A value that came from the role, not from this person. Muted rather than a second
   colour: the column is scanned for the exceptions, so the rule should recede. */
.state--inherited {
	color: var(--c-text-muted);
	font-weight: 400;
}

/* The three-way radio, laid out as a row rather than a stack — it is one question
   with three short answers, and three lines for it reads as three questions. */
.field--choice ul,
.field--choice > div {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-3);
	list-style: none;
	margin: 0;
	padding: 0;
}
.field--choice label {
	display: inline-flex;
	align-items: center;
	gap: var(--s-1);
	font-weight: 500;
}

/* --- Hinweise (Slice 13) ------------------------------------------------ */

/* The nav entry, red only while something is open. A permanent badge is furniture,
   and furniture is not noticed. */
.site-nav__alert {
	color: var(--c-danger-text, #ffd7d3) !important;
	font-weight: 600;
}
.site-nav__badge {
	display: inline-block;
	min-width: 1.25rem;
	padding: 0 0.3rem;
	margin-left: 0.3rem;
	border-radius: 999px;
	background: var(--c-danger, #99231f);
	color: #fff;
	font-size: 0.72rem;
	font-weight: 700;
	line-height: 1.25rem;
	text-align: center;
	font-variant-numeric: tabular-nums;
}

.all-clear {
	padding: var(--s-5) var(--s-4);
	text-align: center;
	border: 1px solid var(--c-border);
	background: var(--c-surface, #fff);
}
.all-clear__title {
	font-size: 1.15rem;
	font-weight: 600;
	color: var(--c-status-done, #1b6e45);
	margin: 0 0 var(--s-1);
}

.alert-group {
	margin-bottom: var(--s-5);
}
.alert-group__title {
	display: flex;
	align-items: baseline;
	gap: var(--s-2);
	font-size: 0.8rem;
	font-weight: 600;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--c-text-muted);
	margin: 0 0 var(--s-2);
}
.alert-group__count {
	font-variant-numeric: tabular-nums;
	font-weight: 400;
}

.alert-list {
	list-style: none;
	margin: 0;
	padding: 0;
	border: 1px solid var(--c-border);
	background: var(--c-surface, #fff);
}

/* The row: what is wrong on the left, what to do on the right. The severity stripe
   is on the left edge so a column of them can be scanned without reading. */
.alert-row {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-2) var(--s-3);
	align-items: center;
	justify-content: space-between;
	padding: var(--s-3);
	border-bottom: 1px solid var(--c-border);
	border-left: 3px solid transparent;
}
.alert-row:last-child {
	border-bottom: 0;
}
.alert-row--danger {
	border-left-color: var(--c-danger, #99231f);
}
.alert-row--warning {
	border-left-color: var(--c-warning, #9a5e00);
}

.alert-row__what {
	flex: 1 1 22rem;
	min-width: 0;
}
.alert-row__title {
	margin: 0;
	font-weight: 600;
}
.alert-row__detail {
	margin: 0.15rem 0 0;
	color: var(--c-text-muted);
	font-size: var(--fs-sm);
}
/* What to do when there is no button — the office cannot close somebody else's week. */
.alert-row__hint {
	margin: 0.25rem 0 0;
	font-size: var(--fs-sm);
	font-style: italic;
	color: var(--c-text-muted);
}

.alert-row__do {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-2);
	align-items: center;
}

.dismissed {
	margin-top: var(--s-5);
	padding-top: var(--s-3);
	border-top: 1px solid var(--c-border);
}
.dismissed > summary {
	cursor: pointer;
	font-weight: 600;
	color: var(--c-text-muted);
}
.alert-list--quiet {
	margin-top: var(--s-2);
	opacity: 0.75;
}

/* --- Fläche picker ------------------------------------------------------
 *
 * A search plus a short list of radios, in place of a <select> over 1720 boards.
 * The list is scrollable rather than paginated: the search is the way to narrow
 * it, and a second control to operate would be one too many.
 */
/* A fixed height, not a maximum: empty, showing one match or showing twenty-five,
 * the box is the same size, so choosing a board never moves the fields under it and
 * the three tiles across the top keep their common height. It scrolls inside — unlike
 * the Monteur list, this one can be 25 rows and is not the end of the page. */
.picker__options {
	border: 1px solid var(--c-border);
	border-radius: var(--radius);
	/* Matched to the search input plus its hint above it (44px + 43px), so the whole
	 * Fläche control is exactly twice that block and never taller. */
	height: 5.5rem;
	overflow-y: auto;
	margin-top: var(--s-2);
	background: var(--c-surface);
}

.picker__opt {
	display: grid;
	grid-template-columns: auto auto 1fr auto;
	align-items: baseline;
	gap: 0 var(--s-3);
	padding: var(--s-2) var(--s-3);
	border-bottom: 1px solid var(--c-border);
	cursor: pointer;
}

.picker__opt:last-child { border-bottom: 0; }
.picker__opt:hover { background: var(--c-surface-sunken); }

.picker__opt:has(input:checked) { background: var(--c-surface-sunken); }

.picker__opt:focus-within {
	outline: 2px solid var(--c-focus);
	outline-offset: -2px;
}

.picker__opt input { margin: 0; }

.picker__id { font-weight: 600; }

/* One line each. The box is a fixed height now, and a board whose location note runs
 * to three lines filled the whole of it by itself — so the list showed one option
 * where it has room for three. The full note is in `title`, and the Alpdest-ID beside
 * it is what somebody is matching on anyway. */
.picker__where {
	color: var(--c-text-muted);
	font-size: .9em;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	min-width: 0;
}

.picker__mark {
	font-size: .78em;
	text-transform: uppercase;
	letter-spacing: .06em;
	color: var(--c-text-muted);
	white-space: nowrap;
}

.picker__note {
	margin: 0;
	padding: var(--s-3);
	color: var(--c-text-muted);
	font-size: .9em;
}

/* --- People picker ------------------------------------------------------
 *
 * Grouped checkboxes with a whole group selectable at once, in place of a flat
 * column of names. Shares `.picker__options` with the Fläche picker above.
 */
.picker__head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--s-3);
	flex-wrap: wrap;
}

.picker__group-by select {
	font-size: var(--fs-sm);
	padding: var(--s-1) var(--s-2);
}

/* The picker lays itself out from the width it is given, with no modifier to
 * choose: one column inside a narrow form column, three or four across a card that
 * spans the page. `auto-fill` is what decides, so the same markup is right on the
 * position form and on the import preview.
 *
 * Grouped by organisation or team, each *group* is a column. Alphabetically there is
 * only one group, so the second grid takes over and the *people* flow into columns —
 * which is the layout that list actually wants.
 */
/* No `max-height` and no scroll: a crew list that scrolls hides people behind a
 * gesture nobody makes on a form, and "who is going" is a question you answer by
 * looking at everybody at once. It is the last thing on the page, so length costs
 * nothing but scrolling the page itself. */
.picker__options--people {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
	align-items: start;
	gap: 0;
	/* `height: auto` and not just `max-height: none`: the base rule now sets a fixed
	 * height for the Fläche box, and inheriting it here left the crew spilling out
	 * below the border instead of the box growing to hold them. */
	height: auto;
	max-height: none;
	overflow: visible;
}

.pgroup { min-width: 0; }
.pgroup + .pgroup { border-top: 1px solid var(--c-border); }

/* One group — alphabetically there is always exactly one — spans the whole width, so
 * the *people* flow into columns instead of the group sitting in a 15rem column with
 * the rest of the card empty beside it. The same applies to a single organisation. */
.picker__options--people > .pgroup:only-child { grid-column: 1 / -1; }

/* Only when they are stacked. Side by side, a rule between columns is noise. */
@supports (grid-template-columns: subgrid) {
	.picker__options--people > .pgroup + .pgroup { border-top: 0; }
	.picker__options--people > .pgroup { border-top: 1px solid var(--c-border); }
	.picker__options--people > .pgroup:first-child { border-top: 0; }
}

.pgroup__list {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
}

/* The same three columns as `.pgroup__opt`, so the group's checkbox sits directly
 * above the ones it controls and every heading is the same shape whatever its name. */
.pgroup__head {
	display: grid;
	grid-template-columns: auto 1fr auto;
	align-items: baseline;
	gap: 0 var(--s-3);
	padding: var(--s-2) var(--s-3);
	background: var(--c-surface-sunken);
	position: sticky;
	top: 0;
	z-index: 1;
	cursor: pointer;
}

.pgroup__head:has(input:disabled) { cursor: default; }
.pgroup__label { cursor: pointer; }
.pgroup__head:has(input:disabled) .pgroup__label { cursor: default; }
.pgroup__head:focus-within { outline: 2px solid var(--c-focus); outline-offset: -2px; }

.pgroup__label {
	font-weight: 600;
	font-size: var(--fs-sm);
}

.pgroup__count {
	color: var(--c-text-muted);
	font-size: var(--fs-sm);
	font-variant-numeric: tabular-nums;
}

.pgroup__all { margin: 0; }

.pgroup__opt {
	display: grid;
	grid-template-columns: auto 1fr auto;
	align-items: baseline;
	gap: 0 var(--s-3);
	padding: var(--s-2) var(--s-3);
	cursor: pointer;
}

.pgroup__opt:hover { background: var(--c-surface-sunken); }
.pgroup__opt:focus-within { outline: 2px solid var(--c-focus); outline-offset: -2px; }
.pgroup__opt input { margin: 0; }

.pgroup__meta {
	color: var(--c-text-muted);
	font-size: 0.9em;
}

/* --- Two-column form layout --------------------------------------------
 *
 * For a form whose fieldsets would otherwise stack into a page nobody scrolls to
 * the bottom of. Phone-first: one column until there is room for two.
 */
.form-grid { display: grid; gap: var(--s-3); }
.form-grid__col { display: flex; flex-direction: column; gap: var(--s-3); }

/* A band of tiles that belong together, above something full width. `stretch` is the
 * point of it: three cards of three different lengths read as three unrelated things,
 * and the shortest one floating above white space reads as unfinished. `auto-fit`
 * means the edit form's two tiles fill the row without a second layout. */
.form-row {
	display: grid;
	gap: var(--s-3);
	margin-bottom: var(--s-3);
	grid-template-columns: 1fr;
	align-items: stretch;
}

/* Not `display: flex`. A card built from a <fieldset> keeps its title inside the box
 * because `04-components.css` floats the <legend> — and **float does not apply to a
 * flex item**, so making these cards flex containers put every title back on the top
 * border, which is the bug that CSS was written to fix in the first place
 * (25 Aug 2026, and again 5 Sep 2026). The grid's own `align-items: stretch` is what
 * equalises the heights; flex was never needed for it. */
.form-row > .card { margin-bottom: 0; }

@media (min-width: 60rem) {
	.form-row { grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); }
}

/* `.form-page .card` carries a `margin-bottom`, from when these were stacked with no
 * grid to space them. Inside a flex column that margin *adds* to the gap, so the tiles
 * sat 2rem apart where every other grid on the dashboard uses 0.75rem — which is what
 * made this form look loose next to the rest of the app (4 Sep 2026). */
.form-grid .card { margin-bottom: 0; }

@media (min-width: 60rem) {
	.form-grid { grid-template-columns: 1fr 1fr; align-items: start; }

	/* A form on the left, a list on the right. The left column stops at a width a
	 * form is readable in — a single line of fields 40rem wide is worse, not better —
	 * and everything else goes to the people picker, which is what gives it room for
	 * three columns of names instead of one. */
	.form-grid--aside { grid-template-columns: minmax(0, 26rem) minmax(0, 1fr); }

	/* The column you keep returning to stays put while the other is filled in. */
	.form-grid__sticky {
		position: sticky;
		top: var(--s-4);
	}
}

/* --- A second line in a table cell --------------------------------------
 *
 * The e-mail under the name. Its own column was the widest on Personen and pushed
 * the action buttons off the edge; stacked it costs nothing and reads the way a
 * person is written down anyway.
 */
.cell-sub {
	display: block;
	color: var(--c-text-muted);
	font-size: var(--fs-sm);
	line-height: 1.3;
	/* Capped, or a long address makes this the widest column on the table — which
	 * is the problem the stacking was meant to solve. The full value is in `title`. */
	max-width: 15rem;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* --- Hover detail -------------------------------------------------------
 *
 * `data-tip` on anything, one bubble for the page, drawn by dashboard.js into
 * <body> rather than into the element it belongs to. That placement is the whole
 * reason this exists rather than a `::after`: every one of these sits inside a
 * `.table-scroll`, whose `overflow-x: auto` clips a child that reaches outside the
 * cell — which is exactly what a tooltip does.
 *
 * It replaces the native `title` on those cells. `title` works, but it appears after
 * a second or so, in the browser's own styling, and nowhere near reliably enough for
 * somebody who does not already know it is there (alpdest, 9 Sep 2026).
 */
.tip {
	position: absolute;
	left: 0;
	top: 0;
	z-index: 100;
	max-width: min(32rem, 80vw);
	padding: var(--s-2) var(--s-3);
	border-radius: var(--radius);
	background: var(--c-text);
	color: var(--c-text-inverse);
	font-size: var(--fs-sm);
	line-height: 1.45;
	/* The Monteure cells put start, pause and end on three lines. */
	white-space: pre-line;
	box-shadow: var(--shadow-lg);
	/* Never the mouse target: a bubble under the cursor would flicker itself away. */
	pointer-events: none;
	opacity: 0;
	visibility: hidden;
}

.tip--on {
	opacity: 1;
	visibility: visible;
}

/* `help` says there is more to see — the same hint the dotted date carried. */
[data-tip] {
	cursor: help;
}

td [data-tip],
td time[title] {
	cursor: help;
	text-decoration: underline dotted var(--c-border-strong);
	text-underline-offset: 2px;
}

/* --- A photo at full size ------------------------------------------------
 *
 * A thumbnail in a report is 9rem wide, which is enough to see that there is a board
 * and not enough to see whether the poster is straight. It used to open in a new tab,
 * which loses the page behind it; this keeps it.
 */
.lightbox {
	width: min(72rem, 94vw);
	max-height: 92vh;
	padding: 0;
	border: 0;
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-lg);
	background: var(--c-surface);
	color: var(--c-text);
}

.lightbox::backdrop {
	background: rgb(20 24 28 / 0.72);
}

.lightbox:focus,
.lightbox:focus-visible {
	outline: none;
}

.lightbox__inner {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: var(--s-3);
	padding: var(--s-4);
	max-height: 92vh;
}

.lightbox__figure {
	margin: 0;
	min-height: 0;
	display: flex;
	justify-content: center;
}

.lightbox__figure img {
	max-width: 100%;
	/* The header and the footer take the rest; without a cap the image decides the
	   dialog height and pushes the download button off the bottom of the screen. */
	max-height: 70vh;
	width: auto;
	height: auto;
	object-fit: contain;
	border-radius: var(--radius);
	background: var(--c-surface-sunken);
}

.lightbox__foot {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--s-3);
	padding-right: var(--tap-min);
}

.lightbox__caption {
	color: var(--c-text-muted);
	font-size: var(--fs-sm);
	line-height: 1.4;
}

/* --- The photo gallery on the export screen ------------------------------
 *
 * The same thumbnail size as a report's own gallery on the position detail — a
 * reader who has seen one knows what this is, and a page of 50 larger ones would be
 * a scroll rather than a view.
 */
.photo-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
	gap: var(--s-4);
	margin: var(--s-4) 0 var(--s-5);
	padding: 0;
	list-style: none;
}

.photo-card {
	display: flex;
	flex-direction: column;
	border: 1px solid var(--c-border);
	border-radius: var(--radius-lg);
	background: var(--c-surface);
	overflow: hidden;
}

.photo-card--picked {
	border-color: var(--c-primary);
	box-shadow: 0 0 0 1px var(--c-primary);
}

.photo-card__shot {
	display: block;
	border: 0;
	padding: 0;
	background: none;
	cursor: zoom-in;
}

.photo-card__shot img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	background: var(--c-surface-sunken);
}

.photo-card__facts {
	display: flex;
	flex-direction: column;
	gap: 0.15rem;
	padding: var(--s-2) var(--s-3);
	font-size: var(--fs-sm);
	line-height: 1.35;
}

.photo-card__foot {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--s-2);
	margin-top: auto;
	padding: var(--s-2) var(--s-3) var(--s-3);
}

/* The tick and its label are one target: a 1rem checkbox is a small thing to hit
   fifty times. */
.photo-card__pick {
	display: inline-flex;
	align-items: center;
	gap: var(--s-2);
	font-size: var(--fs-sm);
	cursor: pointer;
}

/* The week dashboard's jump dropdown, in the header beside Vorwoche/Folgewoche.

   A bare `select` is `display: block; width: 100%` (05-forms.css), which inside the
   flex row would either stretch to the whole header or collapse to nothing. It sits
   on one line with the buttons and stops growing once the longest option fits. */
.week-jump {
	display: flex;
	gap: var(--s-2);
	align-items: center;
}

/* Dressed as a `.btn--quiet`, because it sits in a row of them and a control half a
   centimetre shorter than its neighbours reads as a different kind of thing. Every
   value here is the button's own: `min-height` and `padding` from `.btn`, the border,
   colour and weight from `.btn--quiet` (04-components.css / above).

   `background-color`, never the `background` shorthand — the shorthand resets
   `background-image` and takes the select's own arrow with it (05-forms.css says so
   too, having been caught by it once). */
.week-jump select {
	width: auto;
	/* The longest label — "KW 49 · 30.11.–06.12.2026 · 24 Positionen" — measures
	   23.6rem with the button's padding and weight, so this is its natural width plus
	   a little slack. Below it the selected week clipped mid-word, which reads as a
	   broken control rather than a long one. */
	max-width: 24rem;
	min-height: var(--tap-min);
	padding: var(--s-2) var(--s-4);
	/* The arrow needs its own room on the right, the same gap the text has on the left. */
	padding-right: calc(var(--s-4) * 2 + 0.625rem);
	background-color: transparent;
	background-position: right var(--s-4) center;
	/* The same chevron as 05-forms.css, restroked in `--c-primary` (#001746) so it
	   belongs to the blue text beside it rather than to the grey of a form field. */
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23001746' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	border-color: var(--c-border-strong);
	color: var(--c-primary);
	font-weight: 600;
}

.week-jump select:hover {
	background-color: var(--c-surface-sunken);
}

@media (max-width: 47.99rem) {
	/* Its own line under the buttons rather than squeezed beside them. `.row-actions`
	   does not wrap — it is also the last cell of a table row, where wrapping would be
	   wrong — so the wrap is asked for here, where this control lives. Without it the
	   select shrank to just its arrow and pushed "Vorwoche" off the left edge. */
	.page-head .row-actions {
		flex-wrap: wrap;
	}

	.week-jump {
		width: 100%;
	}

	.week-jump select {
		width: 100%;
		max-width: none;
	}
}
