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

html { -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* Never outline:none. A keyboard user who cannot see the focus cannot use the app. */
:focus-visible {
  outline: 2px solid var(--vg-red);
  outline-offset: 2px;
  /* No border-radius here: the outline follows the element's own radius. Forcing 6px made
     a focused card (10px) or chip (999px) squarer than the element itself. */
}

.skip {
  position: absolute;
  left: -999px;
  top: var(--s2);
  background: var(--surface);
  padding: var(--s2) var(--s4);
  border-radius: var(--radius-sm);
  z-index: 100;
}
.skip:focus { left: var(--s4); }

/* ------------------------------------------------------------------ typography */

h1 {
  font-size: var(--t-2xl);
  font-weight: var(--fw-black);        /* Lato Black — the VG's signature weight */
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  text-wrap: balance;
}
h2 {
  font-size: var(--t-xl);
  font-weight: var(--fw-bold);
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-tight);
  margin-bottom: var(--s3);
  text-wrap: balance;
}
/* Was 1rem — identical to body text. The card title is the most important thing in the
   grid and it disappeared between the chip and the muted line. */
h3 {
  font-size: var(--t-lg);
  font-weight: var(--fw-bold);
  line-height: var(--lh-snug);
}

/* Prose gets a reading width. Cards, tables and grids are exempt. */
main p, .panel p, .banner, .empty p {
  max-width: var(--measure);
  text-wrap: pretty;
}
.card p, .vcard p, .table p, .toast-msg, .row-actions { max-width: none; }

/* The reset sets margin:0 on everything, and nothing put it back — so the legal pages were
   a wall of text with invisible bullet points (padding:0 kills the list marker's room). */
main p + p,
main p + ul,
main ul + p,
.panel p + p { margin-top: var(--s3); }

.panel ul,
main ul { padding-left: var(--s5); list-style: disc; }
.panel li + li,
main li + li { margin-top: var(--s1); }

.muted { color: var(--text-muted); font-size: var(--t-sm); }
.hint { font-size: var(--t-xs); color: var(--text-muted); margin-top: var(--s1); max-width: var(--measure); }
.error-text { font-size: var(--t-sm); color: var(--danger); margin-top: var(--s1); }

/* Utility classes instead of style="" — the CSP forbids inline styles (style-src 'self'
   without unsafe-inline), so an inline attribute is not just untidy, it is ignored. */
.stack-tight { display: flex; gap: var(--s1); flex-wrap: wrap; align-items: center; }
.mt-2 { margin-top: var(--s2); }
.mt-3 { margin-top: var(--s3); }
.mt-4 { margin-top: var(--s4); }
.mb-4 { margin-bottom: var(--s4); }
.w-full { width: 100%; }
.break-all { word-break: break-all; }

/* ---------------------------------------------------------------------- header */

.topbar {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  position: sticky;
  top: 0;
  /* Above the overflow menu (30), below toasts (90) and the modal (100). An open <details>
     menu does not close on scroll, so a lower topbar would be covered by it. */
  z-index: 40;
}
/* The red edge is the VG signature. It sits on top so it reads as a brand band, not as a
   border that happens to be red. */
.topbar::before {
  content: '';
  display: block;
  height: 4px;
  background: var(--vg-red);
  box-shadow: 0 1px 0 rgba(177, 5, 15, .35);
}

.topbar-inner {
  max-width: var(--page);
  margin: 0 auto;
  padding: var(--s3) var(--s4);
  display: flex;
  align-items: center;
  gap: var(--s2);
}

.brand { display: flex; align-items: center; gap: var(--s3); text-decoration: none; color: inherit; }
.brand img { height: 40px; width: auto; }
.brand-text strong { display: block; font-size: var(--t-md); line-height: 1.2; }
.brand-text span { font-size: var(--t-2xs); color: var(--text-muted); }

.topbar-spacer { flex: 1; }

@media (max-width: 560px) {
  .brand-text span { display: none; }
  .brand img { height: 32px; }
  .topbar-inner { padding: var(--s2) var(--s3); gap: var(--s1); }
  .topbar-inner .btn { padding-inline: var(--s2); }
}
/* At 320px the brand plus three buttons needed ~358px of 296px available — the page scrolled
   sideways. The logout label goes; the icon and the aria-label stay. */
@media (max-width: 400px) {
  [data-action='logout'] { font-size: 0; padding-inline: var(--s2); }
  [data-action='logout']::before { content: '⏻'; font-size: 1rem; }
}

/* ------------------------------------------------------------------------ tabs */

.tabs {
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 var(--s4);
  display: flex;
  gap: var(--s1);
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs[hidden] { display: none; }
/* The scrollbar is hidden, so a fade is the only hint that there is more to the right. */
@media (max-width: 420px) {
  .tabs { mask-image: linear-gradient(to right, #000 88%, transparent); }
}

.tab {
  appearance: none;
  border: none;
  background: none;
  font: inherit;
  font-weight: var(--fw-bold);
  color: var(--text-muted);
  padding: var(--s3) var(--s4);
  min-height: var(--tap);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: var(--s2);
  transition: color var(--fast), border-color var(--fast), background var(--fast);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
.tab:hover { color: var(--text); background: var(--surface-2); }
.tab[aria-selected='true'] { color: var(--vg-red); border-bottom-color: var(--vg-red); }

.tab .count {
  background: var(--border);
  color: var(--text);   /* grey-on-grey was 4.19:1 */
  border-radius: 999px;
  padding: 1px var(--s2);
  font-size: var(--t-2xs);
  font-weight: var(--fw-bold);
  transition: background var(--fast), color var(--fast);
}
.tab[aria-selected='true'] .count { background: var(--vg-red); color: #fff; }

/* ------------------------------------------------------------------------ main */

main {
  max-width: var(--page);
  margin: 0 auto;
  padding: var(--s5) var(--s4) var(--s7);
  width: 100%;
  flex: 1;
}

.page-head { margin-bottom: var(--s5); }
.page-head h1 { margin-bottom: var(--s1); }

.area-head {
  border-left: 3px solid var(--vg-red);
  padding: var(--s1) 0 var(--s1) var(--s3);
  margin: var(--s4) 0 var(--s5);
}
.area-head h1 { margin-bottom: 2px; }

/* ----------------------------------------------------------------------- cards */

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(268px, 100%), 1fr));
  gap: var(--s4);
}

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--e1);
  padding: var(--s4);
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: box-shadow var(--fast), transform var(--fast), border-color var(--fast);
}
/* A hairline that grows on hover: it signals "this whole card is the target" without
   moving anything else on the page. */
.card::after {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 3px;
  background: var(--vg-red);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--fast);
}
.card:hover {
  box-shadow: var(--e2);
  transform: translateY(-2px);
  border-color: var(--border-strong);
}
.card:hover::after,
.card:focus-visible::after { transform: scaleX(1); }
.card:active { transform: translateY(0); }

.card.is-draft { border-style: dashed; }
.card h3 { margin-top: var(--s1); }

.card-new {
  border: 2px dashed var(--border);
  background: transparent;
  box-shadow: none;
  align-items: center;
  justify-content: center;
  min-height: 148px;
  color: var(--text-muted);
  font-weight: var(--fw-bold);
  gap: var(--s1);
}
.card-new::after { display: none; }
.card-new:hover {
  border-color: var(--vg-red);
  color: var(--vg-red);
  background: var(--vg-red-soft);
  box-shadow: none;
  transform: none;
}
.card-new .plus { font-size: 1.7rem; line-height: 1; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  font-size: var(--t-2xs);
  font-weight: var(--fw-bold);
  padding: 3px var(--s2);
  border-radius: 999px;
  background: var(--no-soft);
  color: var(--text-muted);
  width: fit-content;
  letter-spacing: .01em;
}
.chip.active { background: var(--vg-red); color: #fff; }
.chip.closed { background: var(--no); color: #fff; }
.chip.confidential { background: var(--vg-red-soft); color: var(--vg-red-dark); }
.chip.warn { background: var(--maybe-soft); color: var(--maybe); }
/* Brand red means "this is the app's accent" (a running poll). A user account being active
   is not an accent, it is a plain OK — so it gets the yes-colour, and a red chip stays
   meaningful. */
.chip.ok { background: var(--yes-soft); color: var(--yes); }
.chip.off { background: var(--no-soft); color: var(--text-muted); }

.progress {
  height: 6px;
  background: var(--border);
  border-radius: 999px;
  overflow: hidden;
}
.progress > span {
  display: block;
  height: 100%;
  background: var(--vg-red);
  border-radius: 999px;
  transition: width var(--slow);
}

/* --------------------------------------------------------------------- buttons */

.btn {
  appearance: none;
  font: inherit;
  font-weight: var(--fw-bold);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  padding: var(--s2) var(--s4);
  min-height: var(--tap);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  white-space: nowrap;
  transition: background var(--fast), border-color var(--fast), color var(--fast),
              box-shadow var(--fast), transform var(--fast);
}
.btn:active { transform: translateY(1px); }

.btn-primary { background: var(--vg-red); color: #fff; }
.btn-primary:hover { background: var(--vg-red-dark); box-shadow: 0 0 0 4px var(--vg-red-ring); }

.btn-secondary { background: var(--surface); color: var(--text); border-color: var(--border-strong); }
.btn-secondary:hover { background: var(--surface-2); border-color: var(--text-muted); }

.btn-ghost { background: none; color: var(--text-muted); border: none; }
.btn-ghost:hover { color: var(--text); background: var(--n-100); }

.btn.danger { color: var(--danger); }
.btn.danger:hover { background: var(--danger-soft); color: var(--danger-dark); }

.btn:disabled { opacity: .45; cursor: not-allowed; transform: none; box-shadow: none; }

/* ----------------------------------------------------------------------- forms */

label { display: block; font-weight: var(--fw-bold); font-size: var(--t-sm); margin-bottom: var(--s1); }
.field { margin-bottom: var(--s4); }
.field:last-child { margin-bottom: 0; }

input[type='text'], input[type='email'], input[type='password'],
input[type='number'], input[type='datetime-local'], textarea, select {
  width: 100%;
  font: inherit;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: var(--s2) var(--s3);
  min-height: var(--tap);
  transition: border-color var(--fast), box-shadow var(--fast);
}
input:hover, textarea:hover, select:hover { border-color: var(--text-muted); }
input:focus, textarea:focus, select:focus {
  outline: 2px solid transparent;   /* invisible normally, real in forced-colors mode */
  outline-offset: 0;
  border-color: var(--vg-red);
  box-shadow: 0 0 0 3px var(--vg-red-ring);
}

/* Windows high-contrast discards box-shadow entirely — without this the focus ring is
   simply gone for the people who need it most. */
@media (forced-colors: active) {
  input:focus, textarea:focus, select:focus,
  .btn:focus-visible, .tab:focus-visible { outline: 2px solid Highlight; outline-offset: 2px; }
}
textarea { min-height: 112px; resize: vertical; font-family: inherit; line-height: 1.5; }

/* A checkbox is not a text field — it must not inherit the 44px min-height. */
.check {
  display: flex;
  align-items: flex-start;
  gap: var(--s2);
  font-weight: var(--fw-bold);
  cursor: pointer;
  margin-bottom: var(--s1);
}
.check input {
  width: 18px;
  height: 18px;
  min-height: 0;
  margin-top: 3px;
  accent-color: var(--vg-red);
  flex: none;
  cursor: pointer;
}

/* A panel is a container, not an object: contour instead of shadow.
   And the OUTER gap must exceed the INNER padding — with 24 inside and 16 between, the eye
   reads four panels as one group, and the editor feels cramped despite the air inside. */
.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--s5);
  margin-bottom: var(--s6);
  box-shadow: var(--e0);
}
@media (min-width: 900px) {
  .panel { padding: var(--s6); margin-bottom: var(--s7); }
}
@media (max-width: 560px) { .panel { padding: var(--s4); } }

/* ---------------------------------------------------------------------- banner */

.banner {
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--info);
  background: var(--info-soft);
  border-radius: var(--radius);
  padding: var(--s3) var(--s4);
  margin-bottom: var(--s4);
  font-size: var(--t-sm);
}
.banner.warn { border-left-color: var(--maybe); background: var(--maybe-soft); }
.banner code {
  font-size: var(--t-xs);
  background: var(--surface);
  border: 1px solid var(--border);
  padding: 2px var(--s1);
  border-radius: 4px;
  display: inline-block;
  margin-top: var(--s1);
}

/* --------------------------------------------------- areas outside the workspace */

/* Set by app.js when leaving the poll workspace (the tab bar is hidden then). */
.topbar.in-area { box-shadow: none; }

.admin-btn {
  color: var(--text);
  border-left: 1px solid var(--border);
  border-radius: 0;
  padding-left: var(--s4);
  margin-left: var(--s1);
}
.admin-btn:hover { color: var(--text); background: var(--n-100); }

.profile-btn { gap: var(--s2); color: var(--text); font-weight: var(--fw-bold); padding-inline: var(--s2); }
.profile-btn:hover { background: var(--surface-2); }
.profile-btn:hover .avatar { background: var(--vg-red-dark); }

.avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--vg-red);
  color: #fff;
  display: grid;
  place-items: center;
  font-size: var(--t-2xs);
  font-weight: var(--fw-bold);
  flex: none;
  transition: background var(--fast);
}

/* On a phone the name would push the buttons off the bar. The initials still say who is
   logged in — which is the entire point of showing it. */
@media (max-width: 640px) {
  .profile-btn #who { display: none; }
  .admin-btn span:not([aria-hidden]) { display: none; }
}

.kv { display: grid; grid-template-columns: minmax(88px, auto) 1fr; gap: var(--s2) var(--s5); margin-bottom: var(--s3); }
.kv dt { font-weight: var(--fw-bold); font-size: var(--t-sm); color: var(--text-muted); }
.kv dd { font-size: var(--t-md); }

/* ----------------------------------------------------------------------- table */

/* A long address (vorname.nachname@vg-altenkirchen-flammersfeld.de) has no break
   opportunity, so its min-content width blew the table past the viewport and the WHOLE page
   scrolled sideways. Two defences: let it break, and let the table scroll inside itself. */
.table-wrap { overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; }
.table td { overflow-wrap: anywhere; }
.table th {
  text-align: left;
  font-size: var(--t-2xs);
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--text-muted);
  padding: var(--s2) var(--s3);
  border-bottom: 1px solid var(--border-strong);
}
.table td { padding: var(--s3); border-bottom: 1px solid var(--border); vertical-align: middle; }
.table tbody tr { transition: background var(--fast); }
.table tbody tr:hover { background: var(--surface-2); }
.table tr:last-child td { border-bottom: none; }
.table select { min-height: 38px; padding: var(--s1) var(--s2); }

/* Edit mode: only the row being edited turns into a form. The rest of the table stays a
   table — that is what keeps a list of 30 people scannable. */
.table tr.editing { background: var(--vg-red-soft); }
.table tr.editing td { border-bottom-color: var(--vg-red); }

.cell-input {
  border: 1px solid var(--border-strong);
  background: var(--surface);
  padding: var(--s1) var(--s2);
  min-height: 36px;
  width: 100%;
  max-width: 260px;
  border-radius: var(--radius-sm);
  margin-bottom: var(--s1);
}
.cell-input.small { font-size: var(--t-sm); }

/* The pencil: quiet until hovered, and square, so it does not compete with the text buttons
   next to it. */
.icon-btn {
  width: 36px;
  min-height: 36px;
  padding: 0;
  font-size: 1rem;
  border: 1px solid var(--border);
}
.icon-btn:hover { border-color: var(--n-400); background: var(--n-50); color: var(--text); }

/* Visually hidden, still read by screen readers — an input without a label is a guess. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* A <td> must stay a table-cell.
   `display: flex` on the cell itself takes it OUT of the table layout: its border no longer
   lines up with the rest of the row, and the buttons stack instead of sitting side by side.
   The flexbox belongs to an inner wrapper, not to the cell. */
td.row-actions { white-space: nowrap; text-align: right; }
td.row-actions > * { vertical-align: middle; }
.row-actions .btn { padding: var(--s1) var(--s2); min-height: 36px; font-size: var(--t-xs); }
.row-actions .btn + .btn,
.row-actions .btn + .menu,
.row-actions .menu + .btn { margin-left: var(--s1); }

/* Five columns are unreadable on a phone — the cells become stacked rows. */
@media (max-width: 900px) {
  .table thead { display: none; }
  .table tr { display: block; border-bottom: 1px solid var(--border); padding: var(--s3) 0; }
  .table tr:hover { background: transparent; }
  .table td { display: block; border: none; padding: var(--s1) 0; }
  td.row-actions { text-align: left; margin-top: var(--s2); white-space: normal; }
}


/* -------------------------------------------------------------- empty/skeleton */

.empty {
  text-align: center;
  padding: var(--s7) var(--s4);
  color: var(--text-muted);
  background: var(--surface);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
}
.empty h2 { color: var(--text); }
.empty p { max-width: 46ch; margin-inline: auto; }

/* Skeletons mirror the shape of what is coming, so nothing jumps when it arrives. A spinner
   in the middle of the page cannot do that. */
.skeleton {
  background: linear-gradient(90deg, var(--border) 25%, var(--surface-2) 50%, var(--border) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.3s infinite linear;
  border-radius: var(--radius);
}
.sk-card { height: 152px; }
.sk-line { height: 12px; border-radius: 999px; margin-bottom: var(--s2); }
.sk-line.short { width: 45%; }
.sk-line.medium { width: 70%; }
.sk-row { height: 56px; margin-bottom: var(--s2); border-radius: var(--radius-sm); }

@keyframes shimmer { to { background-position: -200% 0; } }

/* ----------------------------------------------------------------------- modal */

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(16, 24, 40, .45);
  backdrop-filter: blur(3px);
  display: grid;
  place-items: center;
  padding: var(--s4);
  z-index: 100;
  animation: fade-in .15s var(--ease);
}

.modal {
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  width: 100%;
  max-width: 480px;
  padding: var(--s5);
  animation: modal-in .2s var(--ease);
  border-top: 4px solid var(--vg-red);
}
/* Used to be the brand red — i.e. identical to the normal dialog. A danger dialog that
   looks like every other dialog is not a danger dialog. */
.modal.danger { border-top-color: var(--danger); }

.modal h2 { font-size: var(--t-xl); margin-bottom: var(--s3); }
.modal p { color: var(--text-muted); line-height: 1.55; }

.modal-list {
  list-style: none;
  margin: var(--s3) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}
.modal-list li {
  position: relative;
  padding-left: var(--s4);
  font-size: var(--t-sm);
  color: var(--text);
}
/* A consequence list should look like consequences, not like decoration. */
/* A bullet point is not a warning. */
.modal-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: .55em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--n-400);
}

.modal-detail {
  margin-top: var(--s3);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--s3);
  font-size: var(--t-xs);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: pre-wrap;
  word-break: break-word;
  color: var(--text);
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--s2);
  margin-top: var(--s5);
}
@media (max-width: 480px) {
  .modal-actions { flex-direction: column-reverse; }
  .modal-actions .btn { width: 100%; }
}

.btn-danger { background: var(--danger); color: #fff; }
.btn-danger:hover { background: var(--danger-dark); box-shadow: 0 0 0 4px rgba(179, 38, 30, .2); }

@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes modal-in {
  from { opacity: 0; transform: translateY(10px) scale(.98); }
  to { opacity: 1; transform: none; }
}

/* ----------------------------------------------------------------------- toast */

/* A stack, bottom-centred. Newest on top of the pile so it is closest to the eye. */
#toast-root {
  position: fixed;
  left: 50%;
  bottom: var(--s5);
  transform: translateX(-50%);
  z-index: 90;
  display: flex;
  flex-direction: column-reverse;
  gap: var(--s2);
  width: min(460px, calc(100vw - var(--s5)));
  pointer-events: none;
}
#toast-root > * { pointer-events: auto; }

/* On a phone the sticky votebar is ~100px tall; a toast 24px off the bottom sat exactly on
   top of "Antwort absenden". */
@media (max-width: 640px) {
  #toast-root { bottom: calc(var(--s5) + 96px); }
}

.toast {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-left: 3px solid var(--text-muted);
  box-shadow: var(--shadow-lg);
  border-radius: var(--radius);
  padding: var(--s3) var(--s3) var(--s3) var(--s4);
  display: flex;
  align-items: center;
  gap: var(--s3);
  overflow: hidden;
  position: relative;
  animation: toast-in .22s var(--ease);
  font-size: var(--t-sm);
}

/* Colour is the accent, the icon carries the meaning (WCAG 1.4.1). */
.toast.success { border-left-color: var(--yes); }
.toast.error { border-left-color: var(--danger); }
.toast.warn { border-left-color: var(--maybe); }
.toast.info { border-left-color: var(--text-muted); }

.toast-icon {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: var(--t-2xs);
  font-weight: var(--fw-bold);
  color: #fff;
  flex: none;
}
.toast.success .toast-icon { background: var(--yes); }
.toast.error .toast-icon { background: var(--danger); }
.toast.warn .toast-icon { background: var(--maybe); }
.toast.info .toast-icon { background: var(--text-muted); }

.toast-msg { flex: 1; line-height: 1.4; }

.toast-count {
  display: inline-block;
  margin-left: var(--s1);
  background: var(--border);
  color: var(--text);
  border-radius: 999px;
  padding: 0 var(--s2);
  font-size: var(--t-2xs);
  font-weight: var(--fw-bold);
}

.toast-undo {
  appearance: none;
  border: none;
  background: none;
  font: inherit;
  font-weight: var(--fw-bold);
  color: var(--vg-red);
  cursor: pointer;
  padding: var(--s1) var(--s2);
  border-radius: var(--radius-sm);
  flex: none;
}
.toast-undo:hover { background: var(--vg-red-soft); color: var(--vg-red-dark); }

.toast-close {
  appearance: none;
  border: none;
  background: none;
  color: var(--text-muted);
  cursor: pointer;
  font-size: var(--t-xs);
  line-height: 1;
  padding: var(--s1);
  border-radius: var(--radius-sm);
  flex: none;
}
.toast-close:hover { background: var(--surface-2); color: var(--text); }

.toast-bar {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 100%;
  background: var(--border-strong);
}

@keyframes toast-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}

/* ----------------------------------------------------------------------- login */

.login-wrap { min-height: 100vh; display: grid; place-items: center; padding: var(--s4); }
.login-box { width: 100%; max-width: 392px; }
.login-box .brand { justify-content: center; margin-bottom: var(--s5); flex-direction: column; text-align: center; gap: var(--s2); }
.login-box .brand img { height: 76px; }

/* ---------------------------------------------------------------------- footer */

.site-footer {
  max-width: var(--page);
  margin: 0 auto;
  padding: var(--s5) var(--s4);
  border-top: 1px solid var(--border);
  font-size: var(--t-xs);
  color: var(--text-muted);
  display: flex;
  gap: var(--s4);
  flex-wrap: wrap;
  width: 100%;
}
.site-footer a { color: inherit; text-decoration: none; }
.site-footer a:hover { color: var(--text); text-decoration: underline; }

/* --------------------------------------------------- participant entry */

/* Two fields plus a button, side by side on desktop, stacked on a phone. The single-person
   form is the default because that is the common case; the bulk paste hides in a
   <details> so it does not dominate a panel most people use one line at a time. */
.p-add {
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  gap: var(--s3);
  align-items: end;
}
.p-add .field { margin-bottom: 0; }
.p-add .btn { min-height: var(--tap); }

@media (max-width: 640px) {
  .p-add { grid-template-columns: 1fr; }
  .p-add .btn { width: 100%; }
}

.bulk summary {
  cursor: pointer;
  font-weight: var(--fw-bold);
  font-size: var(--t-sm);
  color: var(--vg-red);
  padding: var(--s2) 0;
  list-style: none;
}
.bulk summary::-webkit-details-marker { display: none; }
.bulk summary::before { content: '▸ '; }
.bulk[open] summary::before { content: '▾ '; }
.bulk summary:hover { color: var(--vg-red-dark); }

/* Motion is decoration everywhere, not just on the cards: the modal and toast entrances get
   the same treatment as the flip, so "reduziert Bewegung" means what it says. */
@media (prefers-reduced-motion: reduce) {
  .modal, .modal-backdrop, .toast { animation: none; }
}

/* A button that reads as a link. Used for the withdrawal action on the vote page, which must
   look like a considered choice, not like a primary button next to "Absenden". */
/* A button that reads as a link.
   NOT `font: inherit` — inside a .hint it would inherit 0.82rem grey and look like
   unstyled text. It sets its own size and weight, so it looks the same wherever it lands. */
.linklike {
  appearance: none;
  border: none;
  background: none;
  font-family: var(--font);
  font-size: var(--t-sm);
  font-weight: var(--fw-bold);
  padding: var(--s2) 0;
  cursor: pointer;
  text-decoration: none;
  color: var(--vg-red);
  border-bottom: 1px solid transparent;
  transition: color var(--fast), border-color var(--fast);
}
.linklike:hover { color: var(--vg-red-dark); border-bottom-color: currentColor; }
.linklike.danger { color: var(--danger); }
.linklike.danger:hover { color: var(--danger-dark); }

/* Right-aligned under the submit button — the conventional spot for "forgot password". */
.form-aside { display: flex; justify-content: flex-end; margin-top: var(--s2); }

.login-note {
  text-align: center;
  font-size: var(--t-xs);
  color: var(--text-muted);
  margin-top: var(--s4);
  line-height: 1.5;
}

.sep { border: none; border-top: 1px solid var(--border); margin: var(--s4) 0; }

/* ----------------------------------------------------- table toolbar / menu */

.table-toolbar {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding-bottom: var(--s3);
  margin-bottom: var(--s2);
  border-bottom: 1px solid var(--border);
}
.table-toolbar h2 { margin: 0; display: flex; align-items: center; gap: var(--s2); }

.count-badge {
  background: var(--border);
  color: var(--text);
  border-radius: 999px;
  padding: 1px var(--s2);
  font-size: var(--t-2xs);
  font-weight: var(--fw-bold);
}

.filter-input {
  width: min(220px, 40vw);
  min-height: 38px;
  padding: var(--s1) var(--s3);
  font-size: var(--t-sm);
}

@media (max-width: 720px) {
  .table-toolbar { flex-wrap: wrap; }
  .table-toolbar .topbar-spacer { display: none; }
  .filter-input { width: 100%; order: 3; }
  .table-toolbar .btn { order: 2; }
}

/* The "new" row is the same form as the edit row, tinted so it reads as "not saved yet". */
.table tr.is-new { background: var(--yes-soft); }
.table tr.is-new td { border-bottom-color: var(--yes); }

/* Overflow menu built on <details>: open/close, Escape and focus come from the browser.
   No JS state, nothing for the CSP to block. */
.menu { position: relative; display: inline-block; }
.menu summary::-webkit-details-marker { display: none; }

/* The trigger is a <summary>, not a .btn — it needs the look spelled out, not inherited. */
.menu summary {
  list-style: none;
  cursor: pointer;
  display: grid;
  place-items: center;
  width: 36px;
  min-height: 36px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-muted);
  font-size: 1rem;
  transition: color var(--fast), border-color var(--fast), background var(--fast);
}
.menu summary:hover {
  border-color: var(--n-400);
  color: var(--text);
  background: var(--n-50);
}
.menu[open] summary { border-color: var(--vg-red); color: var(--vg-red); background: var(--vg-red-soft); }

.menu-items {
  position: absolute;
  right: 0;
  top: calc(100% + var(--s1));
  z-index: 30;
  min-width: 220px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: var(--s1);
  display: flex;
  flex-direction: column;
  animation: menu-in .12s var(--ease);
}

.menu-item {
  appearance: none;
  border: none;
  background: none;
  font: inherit;
  font-size: var(--t-sm);
  text-align: left;
  padding: var(--s2) var(--s3);
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--text);
  min-height: 38px;
  white-space: nowrap;
}
.menu-item:hover:not(:disabled) { background: var(--surface-2); }
.menu-item.danger { color: var(--danger); }
.menu-item.danger:hover:not(:disabled) { background: var(--danger-soft); color: var(--danger-dark); }
.menu-item:disabled { opacity: .4; cursor: not-allowed; }

@keyframes menu-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}

/* On a phone the table is a stack of blocks, so an absolutely-positioned menu would fly
   off screen. Let it flow inline instead. */
@media (max-width: 900px) {
  .menu-items { position: static; box-shadow: none; margin-top: var(--s2); min-width: 0; }
}

/* A collapsible panel whose summary is the heading. */
.foldout > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--s2);
}
.foldout > summary::-webkit-details-marker { display: none; }
.foldout > summary::before { content: '▸'; color: var(--vg-red); font-size: var(--t-xs); }
.foldout[open] > summary::before { content: '▾'; }
.foldout-title { font-size: var(--t-lg); font-weight: var(--fw-bold); letter-spacing: -.005em; }
.foldout > summary:hover .foldout-title { color: var(--vg-red); }


/* ------------------------------------------------------------------------ print */

/* A creator wants to take the result to the meeting. Without this they get the topbar, the
   tabs, the sticky bar and — worse — only the FRONT of every card, because the back is
   hidden behind backface-visibility. */
@media print {
  .topbar, .tabs, .votebar, #toast-root, #modal-root, .site-footer,
  .flip-btn, .btn, .menu, .table-toolbar .btn, .filter-input { display: none !important; }

  body { background: #fff; }
  main { padding: 0; max-width: none; }

  .panel, .card, .vcard-front, .vcard-back { box-shadow: none; border: 1px solid #999; }

  /* Print BOTH faces, stacked, instead of one hidden behind the other. */
  .vcard-inner { transform: none !important; display: block; }
  .vcard-front, .vcard-back {
    position: static;
    backface-visibility: visible;
    transform: none !important;
    opacity: 1 !important;
    page-break-inside: avoid;
    margin-bottom: var(--s3);
  }
  .vcard-back[inert] { display: none; }   /* the hidden face carries `inert` */

  .vote-grid, .grid { display: block; }
  .table { page-break-inside: auto; }
  tr { page-break-inside: avoid; }
}

/* Two-cell inline form (label+input, then a button). The participant entry uses .p-add with
   three columns; reusing it here stretched the single button across half the panel. */
.inline-form {
  display: grid;
  grid-template-columns: minmax(220px, 1fr) auto;
  gap: var(--s3);
  align-items: end;
}
.inline-form .field { margin-bottom: 0; }
@media (max-width: 560px) {
  .inline-form { grid-template-columns: 1fr; }
  .inline-form .btn { width: 100%; }
}


/* ---------------------------------------------------------------- polish */

/* Proportional digits make every live counter jitter horizontally on each update. */
.chip, .count, .count-badge, .toast-count, .bar, .answer-line,
.table td, .vcard-time, .kv dd, .progress + p, .muted {
  font-variant-numeric: tabular-nums;
}

/* Browser blue in a red app. */
::selection { background: var(--vg-red-soft); color: var(--vg-red-dark); }

/* The topbar is sticky (~90px). Without this the skip link lands behind it. */
#main { scroll-margin-top: calc(var(--s7) + var(--s5)); }

/* Vertical flow inside a panel: siblings get space, the first child never does. */
.panel > * + * { margin-top: var(--s4); }
.panel > * + h2 { margin-top: var(--s6); }
.panel > h2 + * { margin-top: var(--s3); }
.panel > .field + .field { margin-top: 0; }   /* .field carries its own margin */

/* Empty states used to be text only — a tinted mark lifts them from "nothing here" to
   "something could be here". */
.empty::before {
  content: '';
  display: block;
  width: 44px;
  height: 44px;
  margin: 0 auto var(--s4);
  border-radius: 50%;
  border: 2px dashed var(--n-300);
}

/* -------------------------------------------------------------------- icons */

/* One grid, one stroke, currentColor — so a hover tints the icon and its label together.
   Replaces five Unicode blocks with five different stroke weights and baselines. */
.icon {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  vertical-align: -.18em;   /* optically on the baseline, not mathematically */
}
.icon-lg { width: 1.75em; height: 1.75em; }
.icon-slot { display: inline-flex; }

/* The tally line (✓ 5 · ~ 2 · ✗ 1) needs its icons aligned with the numbers. */
.tally { display: flex; align-items: center; gap: var(--s1); flex-wrap: wrap; }
.tally .icon { width: 1em; height: 1em; }

/* Answer buttons: icon and label are one unit. */
.seg-opt span .icon { width: 1.1em; height: 1.1em; }

/* Deleting is destructive — the trash icon says so, and so does its hover. */
.flip-btn.danger:hover {
  border-color: var(--danger);
  color: var(--danger);
  background: var(--danger-soft);
  transform: none;   /* the rotate belongs to "flip", not to "delete" */
}
