/* MatterData — the single stylesheet.
   No framework, no build step, no inline <style> (the CSP forbids it).

   Design system: docs/09-design-system.md, which is binding. The short version:
   the page is a canvas, the content is the product, the UI gets out of the way.
   Neutral greys carry structure, blue carries links, black carries the one
   primary action, and the tenant's --accent is decorative only (§5): it never
   colours text, never fills a control, never signals state, and is never the
   only signal for anything. TestAccentIsContained fails the build if a fourth
   `var(--accent)` selector appears.

   Light is the default. Dark is opt-in and comes from a server-set cookie
   rendered as <html data-theme="dark"> — never from prefers-color-scheme, so
   there is no flash and the user's explicit choice always wins (§9).

   The engine serves this file from /static/app.<content-hash>.css and the one
   tenant-configured property from /theme.css (engine/internal/httpd/assets.go).
   It is embedded in the binary: ADR-4 is one static binary with no runtime
   file dependencies.

   Sections marked "slice 2" style markup the renderer does not emit yet
   (report tables, pagination, dialogs, stats). They are kept because the
   design system is the spec for them and re-deriving CSS from prose later is
   how a design system rots.
*/

/* ------------------------------------------------------------------ tokens */

:root {
  /* Tenant knob. /theme.css overrides this one value, nothing else. */
  --accent: #1d5b3a;

  /* Type scale (16px root). Six sizes; a seventh means the design is wrong. */
  --text-xs: 0.75rem;    /* 12 — tags, badges, table meta */
  --text-sm: 0.8125rem;  /* 13 — help texts, captions, column labels */
  --text-base: 0.9375rem;/* 15 — body copy, all controls */
  --text-lg: 1.125rem;   /* 18 — h2 */
  --text-xl: 1.5rem;     /* 24 — h1 */
  --text-2xl: 2rem;      /* 32 — stat numbers, error codes */

  /* Spacing scale — 4px base, six steps. */
  --s1: 0.25rem;
  --s2: 0.5rem;
  --s3: 0.75rem;
  --s4: 1rem;
  --s5: 1.5rem;
  --s6: 2rem;
  --s7: 3rem;

  --radius-sm: 4px;
  --radius: 6px;
  --radius-lg: 10px;

  --maxw: 68rem;
  --control-h: 2.125rem; /* 34px — one height for inputs and buttons */

  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* Light — the default for everyone, always. */
:root,
[data-theme="light"] {
  color-scheme: light;

  --bg: #ffffff;         /* the canvas */
  --surface: #ffffff;    /* cards, dialogs, table body */
  --surface-2: #fafafa;  /* the only fill: table head, notes, hover */
  --border: #e6e6e6;     /* hairlines */
  --border-strong: #949494; /* control boundaries — 3:1 against --bg (WCAG 1.4.11) */

  --fg: #101010;         /* 18.9:1 on --bg */
  --fg-muted: #616161;   /*  6.1:1 on --bg — safe for real copy */
  --fg-subtle: #767676;  /*  4.5:1 — the quietest text that still passes AA */

  --link: #0b57d0;       /*  5.9:1 — links are blue, like the platform's own */
  --link-hover: #08429c;

  --solid-bg: #101010;   /* the one hot button: black */
  --solid-fg: #ffffff;
  --solid-bg-hover: #333333;

  --danger: #b42318;
  --danger-bg: #fef3f2;
  --warn: #8a5300;
  --warn-bg: #fff9ed;
  --ok: #0a6a3c;
  --ok-bg: #f0faf4;

  --ring: #0b57d0;       /* focus is blue and neutral, never the tenant accent */
  --shadow-lg: 0 10px 38px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .08);
  --backdrop: rgba(17, 17, 17, .38);
}

/* Dark — opt-in, same structure, same contrast promises. */
[data-theme="dark"] {
  color-scheme: dark;

  --bg: #0a0a0a;
  --surface: #111111;
  --surface-2: #191919;
  --border: #272727;
  --border-strong: #6b6b6b; /* 3:1 against --bg */

  --fg: #ededed;         /* 16.4:1 on --bg */
  --fg-muted: #a1a1a1;   /*  7.4:1 */
  --fg-subtle: #8f8f8f;  /*  5.4:1 — the quietest text that still passes AA */

  --link: #8ab4f8;       /* 10.1:1 */
  --link-hover: #adc9fb;

  --solid-bg: #ededed;   /* black inverts to white; still not the accent */
  --solid-fg: #0a0a0a;
  --solid-bg-hover: #ffffff;

  --danger: #ff7b70;
  --danger-bg: #2a1512;
  --warn: #e8b046;
  --warn-bg: #241c0c;
  --ok: #4ec38a;
  --ok-bg: #0e2018;

  --ring: #8ab4f8;
  --shadow-lg: 0 10px 38px rgba(0, 0, 0, .6);
  --backdrop: rgba(0, 0, 0, .62);
}

/* ------------------------------------------------------------------- base */

* { box-sizing: border-box; }

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

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

a { color: var(--link); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--link-hover); }

:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

h1, h2, h3 { line-height: 1.25; letter-spacing: -0.01em; }
h1 { font-size: var(--text-xl); font-weight: 600; margin: 0 0 var(--s1); }
h2 { font-size: var(--text-lg); font-weight: 600; margin: 0 0 var(--s2); }
h3 { font-size: var(--text-base); font-weight: 600; margin: 0 0 var(--s2); }
p { margin: 0 0 var(--s3); }
.lead { color: var(--fg-muted); margin: 0 0 var(--s4); }
.hint { color: var(--fg-muted); font-size: var(--text-sm); }

hr { border: 0; border-top: 1px solid var(--border); margin: var(--s5) 0; }

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

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--surface); color: var(--fg);
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: var(--s2) var(--s4);
}
.skip-link:focus { left: var(--s2); top: var(--s2); }

/* -------------------------------------------------------------- top bar */

/* The header is chrome, so it is quiet: two thin rows, one hairline, no fill
   other than the page background. Row 1 is identity, row 2 is navigation. */
.topbar {
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  position: relative;
}
/* The tenant's accent, allowed use #1: a 2px identity stripe that carries no
   text, no meaning and no state. */
.topbar::before {
  content: "";
  position: absolute; inset: 0 0 auto 0; height: 2px;
  background: var(--accent);
}

.topbar-row {
  max-width: var(--maxw); margin: 0 auto; padding: 0 var(--s5);
  display: flex; align-items: center; gap: var(--s4);
}
.topbar-identity { min-height: 3rem; }

.brand { display: flex; align-items: center; gap: var(--s2); text-decoration: none; color: inherit; }
/* Allowed accent use #2: the brand mark. A shape, never a label. */
.brand-mark {
  width: 0.875rem; height: 0.875rem; border-radius: 3px;
  background: var(--accent); flex: none;
}
.brand-name { font-weight: 600; font-size: var(--text-base); letter-spacing: -0.01em; }

.bar-end { margin-left: auto; display: flex; align-items: center; gap: var(--s2); }
.user-email { font-size: var(--text-sm); color: var(--fg-muted); }
.user-role { font-size: var(--text-sm); color: var(--fg-subtle); }

/* The app-wide scope selector (docs/09 §6.12, spec §5.2). A form, because the
   value it changes lives on the server and mutates state — so it goes through
   the one CSRF check and works with JavaScript off. Two controls side by side,
   both at --text-sm so the header keeps its 3rem row: the native <select> the
   engine's form fields already style, and a small secondary button. No accent
   anywhere — the scope is state, and §5's containment rule keeps state off the
   accent. */
.scope-switch { display: flex; align-items: center; gap: var(--s2); }
.scope-switch select {
  font-size: var(--text-sm);
  padding: calc(var(--s1) + 1px) var(--s2);
  max-width: 12rem;
}

/* The zone control (spec §7.11). Two shapes: a selector where the page follows
   its reader, and a plain word where the page names a zone of its own. Both
   sit beside the scope switch, because both answer a question about the whole
   page rather than about anything on it.

   No accent anywhere: a zone is neither a state nor an affordance, and
   docs/09's load-bearing rule allows the accent only on surfaces with no text,
   no state and no affordance. --fg-muted is the same weight the role badge in
   the same row carries. */
.tz-switch { display: flex; align-items: center; gap: var(--s2); }
.tz-switch select {
  font-size: var(--text-sm);
  padding: calc(var(--s1) + 1px) var(--s2);
  max-width: 12rem;
}
.tz-indicator {
  font-size: var(--text-sm);
  color: var(--fg-muted);
  white-space: nowrap;
}

/* Navigation: text links with an underline for the current page. The accent
   draws that underline (allowed use #3) but never alone — the current item is
   also the only one in full --fg with weight 500, and carries aria-current. */
.nav { list-style: none; display: flex; margin: 0; padding: 0; gap: var(--s5); }
.topbar-nav { padding-bottom: 0; overflow-x: auto; scrollbar-width: none; }
.topbar-nav::-webkit-scrollbar { display: none; }
.nav a {
  display: block; padding: var(--s2) 0 calc(var(--s2) + 1px);
  margin-bottom: -1px;
  color: var(--fg-muted); text-decoration: none; white-space: nowrap;
  font-size: var(--text-base);
  border-bottom: 2px solid transparent;
}
.nav a:hover { color: var(--fg); }
.nav a[aria-current="page"] {
  color: var(--fg); font-weight: 500; border-bottom-color: var(--accent);
}

/* The region placeholder — slice 2 deletes this.
   Until the renderer emits regions, a page lists what it *would* render as
   machine-readable markers (engine/internal/httpd/render.go). It is styled as
   quiet metadata so a developer running the engine sees a page and not a
   broken one. */
.regions { list-style: none; margin: 0; padding: 0; color: var(--fg-muted); font-size: var(--text-sm); }
.regions li { padding: var(--s2) 0; border-bottom: 1px solid var(--border); }
.not-rendered { color: var(--fg-muted); font-size: var(--text-sm); }

/* ------------------------------------------------------------- page frame */

.container { max-width: var(--maxw); margin: 0 auto; padding: var(--s6) var(--s5) var(--s7); width: 100%; flex: 1; }
.container-narrow {
  max-width: 26rem;
  display: flex; flex-direction: column; justify-content: center;
  padding-bottom: var(--s6);
}
.brand-lg { margin-bottom: var(--s4); font-size: var(--text-lg); }
.brand-lg .brand-mark { width: 1.125rem; height: 1.125rem; border-radius: 4px; }
.brand-lg .brand-name { font-size: var(--text-lg); }

.footer {
  max-width: var(--maxw); margin: 0 auto; padding: var(--s4) var(--s5) var(--s5);
  width: 100%; color: var(--fg-subtle); font-size: var(--text-sm);
  display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  border-top: 1px solid var(--border);
}
.footer p { margin: 0; }

.page-head {
  display: flex; flex-wrap: wrap; gap: var(--s3);
  align-items: flex-start; justify-content: space-between;
  margin-bottom: var(--s5);
}
.page-head h1 { margin: 0; }
.page-head .lead { margin: var(--s1) 0 0; font-size: var(--text-sm); }
.page-actions { display: flex; gap: var(--s2); flex-wrap: wrap; }

section + section { margin-top: var(--s6); }
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3); margin-bottom: var(--s3); }

/* Cards are containers, not decoration: hairline, no shadow, no gradient. */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--s5);
}
.card + .card { margin-top: var(--s4); }
.card-narrow { max-width: 26rem; margin-inline: auto; }
.card h2:first-child, .card h1:first-child { margin-top: 0; }
.card > h1 { font-size: var(--text-lg); margin-bottom: var(--s4); }

/* -------------------------------------------------- notes / help regions */

/* One region component, five levels. Neutral by default: a hairline box with
   a faint fill. Semantic levels add a 2px left rule and colour the title —
   never the body copy, which stays at full --fg contrast. */
.note {
  border: 1px solid var(--border);
  border-left: 2px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface-2);
  padding: var(--s3) var(--s4);
  margin: 0 0 var(--s4);
  font-size: var(--text-base);
}
.note > :last-child { margin-bottom: 0; }
/* The title is an h2 (spec §9.5.3 — a note's heading sits at the page's next
   heading level), so the heading size is overridden back to the note's own. */
.note-title {
  font-weight: 600; font-size: var(--text-sm); line-height: 1.35;
  margin: 0 0 var(--s1); display: flex; align-items: center; gap: var(--s2);
}
.note-body { margin: 0; }
/* The warning's icon: the non-colour visual signal WCAG 1.4.1 asks for
   (spec §9.5.4). It inherits the title's colour, so it is never the only
   signal and never a second one either. */
.note-icon { flex: none; }
.note-info { border-left-color: var(--link); }
.note-info .note-title { color: var(--link); }
.note-warn { border-left-color: var(--warn); background: var(--warn-bg); }
.note-warn .note-title { color: var(--warn); }
.note-danger { border-left-color: var(--danger); background: var(--danger-bg); }
.note-danger .note-title { color: var(--danger); }
.note-ok { border-left-color: var(--ok); background: var(--ok-bg); }
.note-ok .note-title { color: var(--ok); }

/* Flash and validation summary are notes with a different lifetime. */
.flash, .form-error {
  border: 1px solid var(--border);
  border-left: 2px solid var(--ok);
  border-radius: var(--radius);
  background: var(--ok-bg);
  padding: var(--s3) var(--s4);
  margin: 0 0 var(--s5);
}
.form-error { border-left-color: var(--danger); background: var(--danger-bg); color: var(--fg); }

/* Empty states are guidance, not an error: heading, one sentence, one action. */
.empty-state {
  border: 1px dashed var(--border); border-radius: var(--radius);
  padding: var(--s7) var(--s5); text-align: center;
}
.empty-state .empty { font-weight: 600; margin: 0 0 var(--s1); }
.empty-state .hint { display: block; margin-bottom: var(--s4); }
.empty-state .btn { margin-top: 0; }

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

.form { display: grid; gap: var(--s4); }
.field { display: grid; gap: var(--s1); }
.field > label { font-weight: 500; font-size: var(--text-sm); color: var(--fg); }
.req { color: var(--danger); text-decoration: none; border: 0; }

/* Field help sits between label and control (GOV.UK order: read it before you
   fill the field) and is wired up with aria-describedby. */
.field-help { color: var(--fg-muted); font-size: var(--text-sm); margin: 0 0 var(--s1); }

input, select, textarea {
  font: inherit; font-size: var(--text-base); color: var(--fg);
  background: var(--surface);
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  padding: 0 var(--s3); height: var(--control-h);
  width: 100%; max-width: 26rem;
}
textarea { min-height: 5.5rem; height: auto; padding: var(--s2) var(--s3); resize: vertical; }
select { padding-right: var(--s2); }
input::placeholder, textarea::placeholder { color: var(--fg-subtle); }
input:hover, select:hover, textarea:hover { border-color: var(--fg-subtle); }
input:disabled, select:disabled, textarea:disabled { background: var(--surface-2); color: var(--fg-muted); }

.has-error input, .has-error select, .has-error textarea { border-color: var(--danger); }

.field-msg { font-size: var(--text-sm); color: var(--fg-muted); }
.field-msg:empty { display: none; }
.field-msg.error { color: var(--danger); }
.field-msg.ok { color: var(--ok); }

/* Checkbox and radio: a real box on the left, label and help to the right,
   the whole row clickable. accent-color is the *text* colour, not the tenant
   accent — a checked box must never depend on a colour someone else picked. */
.field-check {
  display: grid; grid-template-columns: auto 1fr; gap: var(--s1) var(--s3);
  align-items: start;
}
.field-check input[type="checkbox"],
.field-check input[type="radio"] {
  width: 1rem; height: 1rem; margin: 0.2rem 0 0; padding: 0;
  accent-color: var(--solid-bg);
  flex: none;
}
.field-check input[type="radio"] { border-radius: 50%; }
.field-check > label { grid-column: 2; font-weight: 500; font-size: var(--text-base); cursor: pointer; }
.field-check .field-help { grid-column: 2; margin: 0; }
.field-check .field-msg { grid-column: 2; }

fieldset { border: 0; margin: 0; padding: 0; display: grid; gap: var(--s2); }
legend { padding: 0; font-weight: 500; font-size: var(--text-sm); margin-bottom: var(--s1); }

/* A field with **no control**: a column the author marked `readonly:`, or one
   the engine owns (a slug, an unguessable, an `img`). It is a value and not a
   disabled input, because `disabled` is a statement to a browser and a crafted
   POST is not a browser — see engine/internal/httpd/form.go.

   Its label is a `<span>`, not a `<label for>`: there is nothing to point at.
   Its help is `.field-note`, not `.field-help`, because a `.field-help` is
   help wired to a control with aria-describedby and that rule has no
   exceptions (docs/09 §6.2, TestFieldHelpIsAssociated). */
.field-readonly > .field-label { font-weight: 500; font-size: var(--text-sm); color: var(--fg); }
.field-value { margin: 0; padding: var(--s2) 0; min-height: 1.5rem; }
.field-value:empty::before { content: "—"; color: var(--fg-muted); }
.field-note { color: var(--fg-muted); font-size: var(--text-sm); margin: 0; }

/* A calendar feed's address (docs/09 §6.3): the one read-only input, because
   its value is the thing to copy. Monospace so a subscriber can see what they
   are copying; `readonly` keeps it selectable where `disabled` would not. */
.feed { margin-bottom: var(--s4); }
.feed-url { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: var(--text-sm); }
.feed .page-actions { margin-top: var(--s1); }

/* An `img` field: a native file input, the picture the row has now, and a box
   that removes it (docs/09-design-system.md §6.2).

   A *native* input, styled through ::file-selector-button, because every page
   works with JavaScript off: a drop zone is a JavaScript-only affordance with
   no keyboard path, and on a phone this control is the camera. The button
   inside it is painted like a secondary button and never with the accent —
   nothing legible ever sits on a colour a tenant picked (§5).

   The preview is capped by max-height and not by width/height attributes: the
   thumbnail's dimensions are the engine's business, and a form that hard-coded
   them would be wrong for a portrait. */
.field-upload { display: grid; gap: var(--s2); }
.field-upload .upload-current {
  max-height: 8rem; max-width: 100%; width: auto; height: auto;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface-2);
}
.field-upload input[type="file"] {
  font: inherit; color: var(--fg);
  padding: var(--s2) 0; border: 0; background: none; width: 100%;
}
.field-upload input[type="file"]::file-selector-button {
  font: inherit; cursor: pointer;
  margin-right: var(--s3); padding: var(--s2) var(--s3);
  color: var(--fg); background: var(--surface-2);
  border: 1px solid var(--border-strong); border-radius: var(--radius);
}
.field-upload input[type="file"]::file-selector-button:hover { background: var(--surface); }
/* The focus ring belongs on the button a person actually sees: a file input's
   own box is invisible, so the default ring would land around nothing. */
.field-upload input[type="file"]:focus-visible { outline: none; }
.field-upload input[type="file"]:focus-visible::file-selector-button {
  outline: 2px solid var(--ring); outline-offset: 2px;
}

/* A picture in a report cell or a record: the `thumb` variant, at the size the
   engine derived it (320 px on the longest edge, spec §9.3.7). */
.cell-img, .figure-img {
  max-height: 4rem; max-width: 100%; width: auto; height: auto;
  border-radius: var(--radius); background: var(--surface-2);
}
.figure-img { max-height: 22rem; }

/* A `multi` field is a group of checkboxes under one legend (spec §6.2:
   "checkbox list"), which is why it is a fieldset and not a select. */
.field-group { gap: var(--s2); }

/* The validation summary is a note whose body is a list of links to the fields
   that need correcting (docs/09 §6.6), so it is a way to the problem and not
   only an announcement of it. */
.form-error ul { margin: var(--s2) 0 0; padding-left: var(--s5); font-size: var(--text-sm); }
.form-error li + li { margin-top: var(--s1); }

/* The destructive action is its own form, so the save body and the delete body
   cannot be confused. `details.danger` below is the disclosure. */
.form-delete { margin-top: var(--s2); }

.form-actions {
  display: flex; gap: var(--s2); flex-wrap: wrap; align-items: center;
  padding-top: var(--s2);
}
.form-actions .spacer { margin-left: auto; }
.inline { display: inline; }

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

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  font: inherit; font-size: var(--text-base); font-weight: 500;
  height: var(--control-h); padding: 0 var(--s3);
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  background: var(--surface); color: var(--fg);
  text-decoration: none; cursor: pointer; white-space: nowrap;
}
.btn:hover { background: var(--surface-2); color: var(--fg); }

/* The one hot button. Black in light, white in dark — deliberately not the
   tenant accent, so no tenant can make the primary action ugly or illegible. */
.btn-primary {
  background: var(--solid-bg); color: var(--solid-fg); border-color: var(--solid-bg);
}
.btn-primary:hover { background: var(--solid-bg-hover); border-color: var(--solid-bg-hover); color: var(--solid-fg); }

.btn-quiet { background: transparent; border-color: transparent; color: var(--fg-muted); }
.btn-quiet:hover { background: var(--surface-2); color: var(--fg); }

.btn-danger { background: var(--surface); color: var(--danger); border-color: var(--danger); }
.btn-danger:hover { background: var(--danger-bg); color: var(--danger); }
.btn-danger-text { background: transparent; border-color: transparent; color: var(--danger); }
.btn-danger-text:hover { background: var(--danger-bg); color: var(--danger); }

.btn-sm { height: 1.75rem; font-size: var(--text-sm); padding: 0 var(--s2); }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.75rem; height: 1.75rem; padding: 0;
  border: 1px solid transparent; border-radius: var(--radius);
  background: transparent; color: var(--fg-muted); cursor: pointer;
}
.icon-btn:hover { background: var(--surface-2); color: var(--fg); }
.icon-btn svg { width: 1rem; height: 1rem; display: block; }

/* The delete affordance: a plain disclosure, never a coloured block. */
details.danger { margin-top: var(--s5); border-top: 1px solid var(--border); padding-top: var(--s4); }
details.danger summary {
  cursor: pointer; color: var(--danger); font-weight: 500; font-size: var(--text-sm);
  width: max-content;
}
details.danger p { font-size: var(--text-sm); margin: var(--s3) 0; }

/* --------------------------------------- filters and report (slice 2) */

/* The renderer does not emit tables yet; docs/09 §6.3 is their contract and
   this is it in CSS. `.grid` keeps the old class name deliberately: it is a
   stylesheet hook, not a region kind — the region kind is `report` (spec §7.1).
   No panel, no fill: the filter row is a toolbar sitting on the canvas. */
.filters {
  display: flex; flex-wrap: wrap; gap: var(--s3); align-items: end;
  padding: 0 0 var(--s4); margin: 0 0 var(--s4);
  border-bottom: 1px solid var(--border);
}
.filters .field { min-width: 9rem; }
.filters .field > label { font-size: var(--text-xs); color: var(--fg-muted); }
.filters input, .filters select { max-width: 14rem; }
.field-actions { display: flex; gap: var(--s2); align-items: end; }

.grid {
  width: 100%; border-collapse: collapse;
  background: var(--surface);
  font-size: var(--text-base);
}
.grid th, .grid td { text-align: left; padding: var(--s2) var(--s3); border-bottom: 1px solid var(--border); }
.grid th:first-child, .grid td:first-child { padding-left: 0; }
.grid th:last-child, .grid td:last-child { padding-right: 0; }
.grid thead th {
  background: transparent;
  font-size: var(--text-xs); font-weight: 500; color: var(--fg-muted);
  text-transform: uppercase; letter-spacing: 0.04em;
  border-bottom-color: var(--border-strong);
  padding-top: 0;
}
.grid thead th a { text-decoration: none; color: inherit; display: inline-block; }
.grid thead th a:hover { color: var(--fg); }
.grid tbody tr:hover { background: var(--surface-2); }
.grid td.num, .grid th.num { text-align: right; font-variant-numeric: tabular-nums; }
.sort-arrow { font-size: 0.7em; margin-left: var(--s1); color: var(--fg); }
.row-actions, .grid th.row-actions { white-space: nowrap; text-align: right; }

/* --------------------------------------- the editable table (spec §7.10)

   docs/09 §6.13. A `sheet` is a `.grid` whose cells hold `.field`s, so almost
   everything here is a *reset*: the field's own vertical rhythm is written for
   a stacked form and a table row has no room for it, and the header cell is
   uppercase `--text-xs` which is not what an author's help sentence should look
   like.

   No new colour, no new geometry token, and no accent: §5 forbids the accent
   from carrying state, and every surface in this table carries somebody's
   unsaved edit. */
.sheet td { vertical-align: top; }
.sheet td .field { gap: 0; }
.sheet td .field > label { margin: 0; }
.sheet td input[type="text"], .sheet td input[type="email"], .sheet td input[type="date"],
.sheet td input[type="datetime-local"], .sheet td input[type="number"],
.sheet td select, .sheet td textarea { width: 100%; min-width: 6rem; }
.sheet td .field-msg { margin-top: var(--s1); }
.sheet thead th .field-help {
  text-transform: none; letter-spacing: normal; font-weight: 400;
  margin: var(--s1) 0 0; max-width: 16rem;
}
/* The save button belongs to the whole table, so it sits under it with the
   same hairline the pager gets rather than floating beside the last row. */
.sheet-form .form-actions { margin-top: var(--s4); }

/* --------------------------------------- the crosstab (spec §7.9, §9.13.2)

   A `matrix` is not a `.grid` with a flag: it has headers on two sides, so the
   first column is a `<th>` and the geometry has to hold when the table is
   wider than the page. Everything else is the report's — the same hairline, no
   zebra, no header fill — because a crosstab is a table and docs/09 §6.3 is
   still what a table looks like here.

   The accent appears nowhere in this block. The tenant picks it, so it may
   only paint surfaces with no text, no state and no affordance (§5), and every
   surface in a crosstab carries a number somebody reads. */
.matrix-scroll { overflow-x: auto; }
.matrix {
  width: 100%; border-collapse: collapse;
  background: var(--surface);
  font-size: var(--text-base);
}
.matrix th, .matrix td {
  text-align: left; padding: var(--s2) var(--s3);
  border-bottom: 1px solid var(--border);
}
.matrix th:first-child, .matrix td:first-child { padding-left: 0; }
.matrix th:last-child, .matrix td:last-child { padding-right: 0; }
.matrix thead th {
  background: transparent;
  font-size: var(--text-xs); font-weight: 500; color: var(--fg-muted);
  text-transform: uppercase; letter-spacing: 0.04em;
  border-bottom-color: var(--border-strong);
  padding-top: 0;
}
/* The `group:` band sits above the column headers and belongs to them, so it
   carries no rule of its own between the two rows. */
.matrix-bands th { border-bottom: none; padding-bottom: 0; }
/* A row header is the row's identity: the weight of a name, not of a heading,
   and never wrapped — a crosstab scrolls sideways instead. */
.matrix tbody th {
  font-weight: 500; color: var(--fg); white-space: nowrap;
  text-transform: none; letter-spacing: normal;
}
.matrix tbody tr:hover { background: var(--surface-2); }
.matrix td.num, .matrix th.num { text-align: right; font-variant-numeric: tabular-nums; }
/* A cell with no row. Quiet, and still legible: `--fg-muted` is 6:1 on the
   background, because „keine Angabe" is information and not decoration. */
.matrix .cell-empty { color: var(--fg-muted); }

/* Tags label a row's state. Semantic colour, faint fill, never the accent. */
.tag {
  display: inline-block; font-size: var(--text-xs); line-height: 1.5;
  padding: 0 var(--s2); border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--surface-2); color: var(--fg-muted);
  white-space: nowrap;
}
.tag-active, .tag-ok { border-color: transparent; background: var(--ok-bg); color: var(--ok); }
.tag-warn { border-color: transparent; background: var(--warn-bg); color: var(--warn); }
.tag-danger { border-color: transparent; background: var(--danger-bg); color: var(--danger); }

.pager {
  display: flex; flex-wrap: wrap; gap: var(--s3);
  justify-content: space-between; align-items: center; padding: var(--s3) 0 0;
}
.pager-info { color: var(--fg-muted); font-size: var(--text-sm); margin: 0; }
.pager-links { list-style: none; display: flex; gap: var(--s4); margin: 0; padding: 0; font-size: var(--text-sm); }
.pager-links .disabled { color: var(--fg-subtle); }

/* ------------------------------------------------- dashboard (slice 2) */

.stats { list-style: none; margin: 0 0 var(--s4); padding: 0; display: grid; gap: var(--s4); grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); }
.stat { display: grid; gap: 0; }
.stat-num { font-size: var(--text-2xl); font-weight: 600; letter-spacing: -0.02em; line-height: 1.1; font-variant-numeric: tabular-nums; }
.stat-label { color: var(--fg-muted); font-size: var(--text-sm); }

.event-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
.event-item { padding: var(--s4) 0; border-bottom: 1px solid var(--border); }
.event-item:first-child { padding-top: 0; }
.event-when { color: var(--fg-muted); font-size: var(--text-sm); margin: 0; font-variant-numeric: tabular-nums; }
.event-title { font-weight: 600; margin: var(--s1) 0; font-size: var(--text-base); }
h2.event-title { font-size: var(--text-lg); }
.event-where, .event-meta, .event-desc { color: var(--fg-muted); font-size: var(--text-sm); margin: 0 0 var(--s1); }
.event-item > p:last-child { margin-bottom: 0; }
.event-actions { display: flex; gap: var(--s2); align-items: center; margin-top: var(--s3); }

/* ---------------------------------------- detail / record (spec §7.7) */

/* An article layout, not a read-only form: no labels down the left, no boxes.
   The metadata is one muted line under the heading and the body is prose at a
   readable measure. */
.detail { margin: 0 0 var(--s6); }

.meta {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: var(--s1) var(--s3);
  margin: 0 0 var(--s5); padding: 0;
  color: var(--fg-muted); font-size: var(--text-sm);
}
.meta-item { display: flex; align-items: baseline; gap: var(--s2); }
/* „ · " between entries, drawn rather than written, so the separator is not in
   the accessible name of anything. */
.meta-item + .meta-item::before { content: "·"; color: var(--fg-subtle); }
.meta dd { margin: 0; }
.meta dd.num { font-variant-numeric: tabular-nums; }

/* A picture and its caption. The <img> arrives with the blob route; until then
   the figure says a picture is there rather than drawing an empty box. */
.figure { margin: 0 0 var(--s5); }
.figure-pending {
  margin: 0; padding: var(--s6) var(--s4);
  border: 1px dashed var(--border); border-radius: var(--radius);
  background: var(--surface-2); color: var(--fg-muted);
  text-align: center; font-size: var(--text-sm);
}
.figure figcaption { margin-top: var(--s2); color: var(--fg-muted); font-size: var(--text-sm); }

/* The body: generated markup from engine/internal/richtext, or paragraphs.
   `max-width` is a measure and not a container — 34rem is ~68 German
   characters, which is the upper end of what stays readable. */
.rich { max-width: 34rem; font-size: var(--text-base); }
.rich > :first-child { margin-top: 0; }
.rich > :last-child { margin-bottom: 0; }
.rich p, .rich ul, .rich ol, .rich blockquote, .rich pre, .rich table { margin: 0 0 var(--s4); }
.rich h2, .rich h3, .rich h4, .rich h5, .rich h6 { margin: var(--s5) 0 var(--s2); }
.rich ul, .rich ol { padding-left: var(--s5); }
.rich li + li { margin-top: var(--s1); }
.rich blockquote {
  padding-left: var(--s4); border-left: 2px solid var(--border-strong);
  color: var(--fg-muted);
}
.rich pre {
  padding: var(--s3) var(--s4); overflow-x: auto;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
  font-size: var(--text-sm);
}
.rich code { font-size: 0.9375em; }
.rich pre code { font-size: inherit; }
.rich hr { border: 0; border-top: 1px solid var(--border); margin: var(--s5) 0; }
.rich table { border-collapse: collapse; width: 100%; font-size: var(--text-sm); }
.rich th, .rich td { padding: var(--s2) var(--s3); border-bottom: 1px solid var(--border); text-align: left; }
.rich th { font-weight: 600; }
/* An image reference the engine cannot resolve (spec §9.6.3): the word and the
   alt text, never a URL. */
.rich .img-unresolved {
  display: inline-block; padding: 0 var(--s2);
  border: 1px dashed var(--border); border-radius: var(--radius-sm);
  color: var(--fg-muted); font-size: var(--text-sm);
}

.readonly-grid { display: grid; grid-template-columns: max-content 1fr; gap: var(--s2) var(--s5); margin: 0 0 var(--s4); font-size: var(--text-base); }
.readonly-grid dt { color: var(--fg-muted); }
.readonly-grid dd { margin: 0; }

.error-code { font-size: var(--text-2xl); font-weight: 600; color: var(--fg-subtle); margin: 0; font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------- dialog (slice 2) */

/* Native <dialog>: Esc, focus trapping and focus return are the browser's job.
   Without JavaScript the same route renders as a full page instead. */
.modal-host:empty { display: none; }

dialog.dialog {
  padding: 0; border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--surface); color: var(--fg);
  width: min(36rem, calc(100vw - 2rem));
  max-width: none;
  max-height: min(86vh, 46rem);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
dialog.dialog[open] { display: flex; flex-direction: column; }
dialog.dialog::backdrop { background: var(--backdrop); }

.dialog-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  padding: var(--s4) var(--s5); border-bottom: 1px solid var(--border);
  flex: none;
}
.dialog-head h2 { margin: 0; font-size: var(--text-base); font-weight: 600; }

.dialog-body {
  padding: var(--s5) var(--s5) 0;
  overflow-y: auto; flex: 1 1 auto; min-height: 0;
}
/* The action bar stays put while the fields scroll (a long form must never
   hide its Save button). */
.dialog-body .form-actions {
  position: sticky; bottom: 0;
  margin: var(--s5) calc(var(--s5) * -1) 0;
  padding: var(--s3) var(--s5);
  background: var(--surface); border-top: 1px solid var(--border);
}
.dialog-body > :last-child { margin-bottom: 0; }
.dialog-body details.danger { margin-top: 0; border-top: 0; padding: var(--s4) 0; }

/* ------------------------------------------------------------- responsive */

@media (max-width: 48rem) {
  .topbar-row { padding: 0 var(--s4); }
  .container { padding: var(--s5) var(--s4) var(--s6); }
  .footer { padding: var(--s4); }
  .user-email, .user-role { display: none; }
  .nav { gap: var(--s4); }
}

@media (max-width: 40rem) {
  /* The table becomes a list of records; each cell keeps its column label. */
  .grid thead { display: none; }
  .grid, .grid tbody, .grid tr, .grid td { display: block; width: 100%; }
  .grid tr { border-bottom: 1px solid var(--border); padding: var(--s3) 0; }
  .grid td { border: none; padding: 0; }
  .grid td::before {
    content: attr(data-label);
    display: block; font-size: var(--text-xs); color: var(--fg-muted);
    text-transform: uppercase; letter-spacing: 0.04em;
  }
  .grid td:first-child { font-weight: 600; }
  .grid td + td { margin-top: var(--s2); }
  .grid td.num, .grid th.num { text-align: left; }
  .row-actions { text-align: left; }
  /* A crosstab below 40rem becomes what it is an efficiency layer over: one
     block per row, its header first, then one labelled line per column
     (decision 0020 §6). */
  .matrix thead { display: none; }
  .matrix, .matrix tbody, .matrix tr, .matrix td, .matrix tbody th { display: block; width: 100%; }
  .matrix tr { border-bottom: 1px solid var(--border); padding: var(--s3) 0; }
  .matrix tbody th { border: none; padding: 0 0 var(--s2); font-weight: 600; }
  .matrix td { border: none; padding: 0; }
  .matrix td::before {
    content: attr(data-label);
    display: block; font-size: var(--text-xs); color: var(--fg-muted);
    text-transform: uppercase; letter-spacing: 0.04em;
  }
  .matrix td + td { margin-top: var(--s2); }
  .matrix td.num, .matrix th.num { text-align: left; }
  .matrix-scroll { overflow-x: visible; }
  .filters .field, .filters input, .filters select { min-width: 0; max-width: none; width: 100%; }
  .filters { flex-direction: column; align-items: stretch; }
  dialog.dialog { width: 100vw; max-width: 100vw; max-height: 100vh; height: 100%; border-radius: 0; border: 0; }
}

/* ------------------------------------- the tenant's administration (0033) */

/* The one page in the product with no compiler behind it, so it uses nothing
   this stylesheet did not already have: `.card` per login, `.meta` for its
   facts, `.tag` for its status, `details.danger` for every destructive act and
   `.btn-primary` exactly once. These two rules are the whole addition, and
   neither touches the tenant accent — docs/09 §5's rule applies to this page
   like any other. */

/* The surface's own section row. It is the header's `.nav` list moved into the
   content column, with a rule under it so the cards below start somewhere. */
.admin-tabs { margin: 0 0 var(--s5); padding-bottom: var(--s3); border-bottom: 1px solid var(--border); }

/* A login's status, in the card head where a region puts its actions. */
.admin-tags { display: flex; flex-wrap: wrap; gap: var(--s2); margin: 0; }

/* Two acts on one login are two sibling forms, so they need the gap a
   `.form-actions` row has and do not get it from the form itself. */
.card > .form + .form { margin-top: var(--s3); }

@media (prefers-reduced-motion: no-preference) {
  .btn, .nav a, .icon-btn, input, select, textarea {
    transition: background-color .12s ease, border-color .12s ease, color .12s ease;
  }
}

@media print {
  .topbar, .footer, .page-actions, .filters, .row-actions, .pager-links { display: none; }
  body { background: #fff; color: #000; }
}
