:root {
  font-size: clamp(14px, 0.5vw + 11.5px, 16.5px);

  /* Neutrals — cool grays, evidence-first */
  --ink: #1b1e24;              /* primary text */
  --sub: #5c6470;              /* secondary text, meta, muted */
  --border: #e3e6ea;           /* card and row separators */
  --border-strong: #cfd4da;    /* inputs, secondary button outlines */
  --bg: #f4f5f7;               /* page background */
  --card: #ffffff;             /* surface */

  /* Semantic — color only carries meaning */
  --accent: #3767cf;           /* primary action, links, active nav */
  --accent-dark: #2a52a8;
  --accent-bg: #eef3fc;
  --accent-border: #c8d7f4;
  --chosen-bg: #f8fafe;        /* resting fill for the chosen claim card — near-white so it doesn't read as hover */
  --surface-quiet: #fbfbfc;    /* header strips and inset boxes inside a white card — reads as a band, not a second surface */
  --approved: #2e7d4f;
  --approved-bg: #eaf5ee;
  --approved-border: #bfdfcb;
  --conflict: #b57310;
  --conflict-bg: #fdf4e5;
  --conflict-border: #ecd4a8;

  /* Evidence marks: chosen (accent tint), competing (warm tint) */
  --mark-chosen: #dbe7fb;
  --mark-competing: #faeacc;

  /* Legacy aliases kept so downstream rules pick up new palette without churn. */
  --text: var(--ink);
  --muted: var(--sub);
  --muted-strong: var(--sub);
  --border-soft: var(--border);
  --ok-bg: var(--approved-bg);
  --ok-border: var(--approved-border);
  --ok-text: var(--approved);
  --warn-bg: var(--conflict-bg);
  --warn-border: var(--conflict-border);
  --warn-text: var(--conflict);
  --highlight: var(--mark-chosen);
  --highlight-soft: var(--accent-bg);
  --mark: var(--mark-chosen);

  /* Type */
  --sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --hand: var(--sans);          /* legacy alias — was Patrick Hand */
  --mono-label: var(--mono);

  --radius: 7px;
  --radius-lg: 10px;

  /* ---- Responsive / mobile tokens ---------------------------------------
     Mobile values live here (once) and are referenced from the media queries
     at the end of this file, so nothing is duplicated. NOTE: custom properties
     cannot be read inside an @media condition, so the breakpoint tokens below
     are reference-only — the px values are hardcoded in the @media rules and
     must be kept in sync with these. */
  --bp-compact: 768px;   /* phones + portrait tablets: hamburger nav, scrollable tables, stacked toolbars */
  --bp-tablet: 900px;    /* two-column detail/split screens collapse to one column */

  /* Touch targets & mobile controls */
  --tap-min: 44px;                  /* iOS/Android minimum touch target */
  --btn-pad-mobile: 0.7rem 1.1rem;  /* larger tap area on buttons */

  /* Mobile type — 16px floor on form controls prevents iOS focus auto-zoom */
  --fs-input-mobile: 1rem;   /* 16px — must stay >= 16px */
  --fs-h1-mobile: 1.35rem;

  /* Mobile layout */
  --nav-h-mobile: 56px;
  --space-page-mobile: 1rem;

  /* Elevation for the mobile nav dropdown panel */
  --shadow-pop: 0 8px 24px rgba(27, 30, 36, 0.1);
}

* { box-sizing: border-box; }

/* The `hidden` attribute must always win. Any author `display` rule outranks the
   UA `[hidden]` stylesheet, so an element toggled by JS or a template keeps its
   layout box and the toggle silently does nothing. This was patched in seven
   places one selector at a time before being fixed here once; `!important` is
   what makes it hold against whatever `display` a later rule introduces. */
[hidden] { display: none !important; }

html, body { margin: 0; padding: 0; min-height: 100vh; }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 0.875rem;    /* 14px @ 16px root — spec body 14/400 */
  line-height: 1.5;
  display: flex;
  flex-direction: column;
}

body > header {
  border-bottom: 1px solid var(--border);
  padding: 0.65rem 1.25rem;
  display: flex;
  align-items: center;
  gap: 1rem;
  background: var(--card);
  height: 52px;
}

body > header a[rel="home"] {
  font-weight: 700;
  padding: 0;
  color: var(--ink);
  text-decoration: none;
  font-size: 0.9rem;
  letter-spacing: 0.02em;
}

body > header nav {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.9rem;
  font-size: 0.85rem;
}
body > header nav a { color: var(--sub); text-decoration: none; }
body > header nav a:hover { color: var(--ink); }
body > header [data-user] { color: var(--sub); font-size: 0.82rem; }

body > header nav[data-tabs] {
  display: flex;
  gap: 0.25rem;
  align-items: center;
  margin-left: 1rem;
  font-size: 0.85rem;
}
body > header nav[data-tabs] a {
  color: var(--sub);
  text-decoration: none;
  padding: 0.35rem 0.75rem;
  border-radius: 6px;
  font-weight: 500;
}
body > header nav[data-tabs] a:hover { color: var(--ink); background: var(--bg); }
body > header nav[data-tabs] a[aria-current="page"] {
  background: var(--accent-bg);
  color: var(--ink);
  font-weight: 600;
  border-bottom: none;
}

main {
  flex: 1;
  padding: 2rem 1.25rem;
  width: 100%;
  max-width: 55rem;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
}
main > * { width: 100%; max-width: 32rem; }

h1 {
  font-size: 1.375rem;    /* 22px — spec page title */
  font-weight: 700;
  margin: 0 0 0.5rem;
  text-align: left;
  color: var(--ink);
  letter-spacing: -0.01em;
}
h2 { font-size: 1rem; font-weight: 600; margin: 1rem 0 0.5rem; color: var(--ink); }
p  { margin: 0 0 0.75rem; }

/* Form labels — clean sans, not the old monospace small-caps */
label,
p[data-label],
main > form > p > label {
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--sub);
  text-transform: none;
  letter-spacing: 0;
  display: block;
  margin-bottom: 0.25rem;
}

form { display: flex; flex-direction: column; gap: 0.75rem; align-items: stretch; }
form p { margin: 0; }

input[type="text"],
input[type="email"],
input[type="password"],
input[type="url"],
input[type="search"],
input[type="date"],
select,
textarea {
  width: 100%;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--card);
  color: var(--ink);
  font: inherit;
  font-family: var(--sans);
}
input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: 0;
  border-color: var(--accent);
}
input::placeholder { color: #9aa1ab; font-family: var(--sans); }

/* Custom dropdown chevron, balanced 0.75rem from the right edge. */
select {
  -webkit-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%235c6470' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  padding-right: 2rem;
}

/* Native checkbox + radio pick up the accent color for the checked state.
   Simpler than reinventing the control with pseudo-elements and works with
   Django admin / allauth forms out of the box. */
input[type="checkbox"],
input[type="radio"] {
  accent-color: var(--accent);
  width: 1rem;
  height: 1rem;
  margin: 0;
}
/* Labels for inline choice controls — sans-serif, aligned with the box. */
label:has(> input[type="checkbox"]),
label:has(> input[type="radio"]) {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 400;
  color: var(--ink);
  text-transform: none;
  letter-spacing: 0;
  margin-bottom: 0.25rem;
  cursor: pointer;
}

form ul.errorlist,
ul.errorlist {
  list-style: none;
  padding: 0;
  margin: 0.25rem 0 0;
  color: var(--warn-text);
  font-size: 0.85rem;
}
.helptext, form .helptext {
  font-family: var(--mono-label);
  font-size: 0.7rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* Primary button — solid accent fill */
button,
a[role="button"],
input[type="submit"] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.55rem 1rem;
  border: none;
  border-radius: var(--radius);
  background: var(--accent);
  color: #fff;
  font-family: var(--sans);
  font-weight: 600;
  font-size: 0.85rem;
  line-height: 1;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  transition: background 0.12s ease;
}
button:hover,
a[role="button"]:hover,
input[type="submit"]:hover { background: var(--accent-dark); }
button:focus-visible,
a[role="button"]:focus-visible,
input[type="submit"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Secondary — outlined, neutral text */
button[data-secondary],
a[role="button"][data-secondary] {
  background: var(--card);
  color: var(--ink);
  border: 1px solid var(--border-strong);
  padding: calc(0.55rem - 1px) calc(1rem - 1px);
}
button[data-secondary]:hover,
a[role="button"][data-secondary]:hover {
  background: var(--bg);
  border-color: var(--sub);
}

/* Ghost / inline — no chrome, accent text */
button[data-ghost],
a[role="button"][data-ghost] {
  background: transparent;
  color: var(--accent);
  padding: 0.5rem 0.25rem;
}
button[data-ghost]:hover,
a[role="button"][data-ghost]:hover {
  background: transparent;
  color: var(--accent-dark);
  text-decoration: underline;
}

/* Confirm / batch — green solid */
button[data-confirm],
a[role="button"][data-confirm] {
  background: var(--approved);
  color: #fff;
}
button[data-confirm]:hover,
a[role="button"][data-confirm]:hover { background: #256b41; }

/* Disabled — solid muted, unclickable */
button[disabled],
button[aria-disabled="true"],
a[role="button"][aria-disabled="true"] {
  background: #eef0f2;
  color: #9aa1ab;
  border-color: transparent;
  cursor: not-allowed;
  opacity: 1;
}
button[disabled]:hover,
button[aria-disabled="true"]:hover { background: #eef0f2; }

/* Content lists — used by memberships and schemas */
ul[data-cards] {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
ul[data-cards] li a {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.65rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  color: var(--ink);
  text-decoration: none;
  font-size: 0.9rem;
  transition: background 0.1s, border-color 0.1s;
}
ul[data-cards] li a:hover { background: var(--bg); border-color: var(--border-strong); }
ul[data-cards] li a small {
  margin-left: auto;
  color: var(--sub);
  font-family: var(--mono);
  font-size: 0.72rem;
  text-transform: none;
  letter-spacing: 0;
}

/* Empty state — soft neutral card. Dashed border in the muted color to say
   "nothing here yet" without shouting; center-aligned so the CTA reads. */
[data-empty] {
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-lg);
  background: var(--bg);
  padding: 1.75rem 1.5rem;
  text-align: center;
  color: var(--sub);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  align-items: center;
}
[data-empty] p { margin: 0; }
[data-empty] p[data-caption] {
  font-family: var(--sans);
  font-size: 0.82rem;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--sub);
}

/* Django messages / toasts */
[role="status"] {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-bottom: 0.75rem;
}
[role="status"] p {
  border: 0.13rem solid var(--muted);
  border-radius: 0.5rem;
  padding: 0.5rem 0.9rem;
  margin: 0;
}
[role="status"] p[data-level="success"] {
  border-color: var(--ok-border);
  background: var(--ok-bg);
  color: var(--ok-text);
}
[role="status"] p[data-level="error"] {
  border-color: var(--warn-border);
  background: var(--warn-bg);
  color: var(--warn-text);
}

/* Pills — status badges. Semantic-only color; shape carries meaning too:
   - rounded pill = live status (pending / approved / conflict / processing)
   - rectangle    = fixed meta (schema pin, draft version, doc type)
*/
[data-pill] {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 3px 10px;
  border-radius: 999px;
  border: 1px solid var(--border);
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 600;
  line-height: 1.35;
  background: var(--bg);
  color: var(--sub);
  white-space: nowrap;
}
/* Live status tones */
[data-pill][data-pill-tone="draft"] {
  color: var(--sub);
  background: var(--bg);
  border-color: var(--border);
}
[data-pill][data-pill-tone="published"] {
  color: var(--approved);
  background: var(--approved-bg);
  border-color: var(--approved-border);
}
[data-pill][data-pill-tone="conflict"] {
  color: var(--conflict);
  background: var(--conflict-bg);
  border-color: var(--conflict-border);
}
[data-pill][data-pill-tone="processing"] {
  color: var(--accent);
  background: var(--accent-bg);
  border-color: var(--accent-border);
}
/* Aliased tones used by the contracts vertical (register "Terms" column, the
   service-agreement cards). The --ok-*/--warn-* variables existed but nothing
   consumed them, so "Standard terms" and "3 deviations" rendered identically. */
[data-pill][data-pill-tone="ok"] {
  color: var(--ok-text);
  background: var(--ok-bg);
  border-color: var(--ok-border);
}
[data-pill][data-pill-tone="warn"] {
  color: var(--warn-text);
  background: var(--warn-bg);
  border-color: var(--warn-border);
}
/* A negotiated clause is not a warning — nothing is wrong, the contract simply
   says something no baseline covers. Accent rather than amber so it reads beside
   a deviation pill as a different KIND of finding, not a lesser one. */
[data-pill][data-pill-tone="bespoke"] {
  color: var(--accent);
  background: var(--accent-bg);
  border-color: var(--accent-border);
}

/* Small mono count pill — used inline in the nav ("Review 3") */
[data-pill][data-pill-count] {
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 500;
  padding: 1px 7px;
}

/* Compact ingestion stage pills — used in doc rows, table rows, and any
   dense context where the full-size pill would crowd the row. Same tones,
   smaller footprint. Also applies to any [data-pill-small] callers. */
ul[data-docs] li [data-pill],
[data-ingestion-pills] [data-pill],
table[data-entities] td [data-pill],
[data-pill][data-pill-small] {
  font-size: 0.68rem;
  font-weight: 500;
  padding: 2px 7px;
}

/* Tiny inline retry button next to a failed stage pill.
   Icon-sized so it doesn't dwarf the pill it's paired with. */
button[data-icon],
a[role="button"][data-icon] {
  padding: 2px 6px;
  font-size: 0.78rem;
  min-width: 0;
  height: auto;
  background: transparent;
  color: var(--sub);
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  line-height: 1;
}
button[data-icon]:hover {
  background: var(--accent-bg);
  color: var(--accent-dark);
  border-color: var(--accent);
}

/* Rectangular meta badges — schema pin, doc type, draft version */
/* Both read as filed-away metadata rather than status, so they share one look:
   quiet mono on the page background. */
[data-pill][data-schema-pin],
[data-pill][data-doc-type] {
  border-radius: 6px;
  padding: 3px 8px;
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 500;
  color: var(--sub);
  background: var(--bg);
  border-color: var(--border);
}
[data-pill][data-draft-version] {
  border-radius: 6px;
  padding: 3px 8px;
  background: var(--card);
  color: var(--sub);
  border: 1px dashed var(--border-strong);
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 500;
}

/* Title row: title + pill + optional action */
[data-title-row] {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-bottom: 0.5rem;
}
[data-title-row] h1 { margin: 0; text-align: left; }
/* Header status-count badges sit inline with the title pills. */
[data-title-row] [data-status-badges] { display: contents; }

/* Field grid — schema editor. Rows use display:contents so every field aligns
   to one shared column grid (name | type | description | remove); a header row
   labels the columns. The editor opts the page into a wider layout below. */
[data-field-grid] {
  display: grid;
  /* 7 cols: Name | Label | Type | Description | References | Name-col | ✕. Col 5
     is auto so it collapses to 0 when no row has entity_reference (References is
     display:none) and grows to fit the reference-type select otherwise — keeps
     rows aligned. Col 6 is the display-label checkbox, fixed narrow. */
  grid-template-columns:
    minmax(0, 1fr) minmax(0, 1fr) 10rem minmax(0, 1.25fr) auto 4.5rem 2rem;
  gap: 0.4rem 0.5rem;
  align-items: center;
}
/* Each row is its own subgrid that inherits the parent's column tracks. This
   isolates auto-placement per row — a row with hidden References doesn't leak
   its empty col-4 into the next row's flow — while column widths stay in lock
   step with the header and every other row via the shared parent tracks. */
[data-field-grid] [data-row] {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 1 / -1;
  gap: inherit;
  align-items: center;
}
[data-field-grid] label[data-field] { display: contents; font: inherit; }
[data-field-grid] label[data-field] > span { display: none; }
[data-field-grid] [data-row]:has([name$="-DELETE"]:checked) { display: none; }
/* The label checkbox sits centred under its narrow header rather than filling
   the track like the text inputs do. */
[data-field-grid] label[data-display-label-wrap] { display: contents; }
[data-field-grid] label[data-display-label-wrap] input {
  width: auto;
  margin: 0 auto;
  justify-self: center;
}
[data-field-grid] [data-col-head] {
  font-family: var(--mono-label);
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted-strong);
}
/* "optional" hint next to a column/field label — softer than the label itself. */
[data-field-grid] [data-optional] {
  font-family: var(--sans);
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--muted);
}
[data-field-grid] input,
[data-field-grid] select,
[data-field-grid] textarea { min-width: 0; padding: 0.4rem 0.6rem; }
[data-field-grid] select { padding-right: 1.9rem; }
/* Description is a textarea so long values wrap; fixed height with an internal
   scrollbar keeps rows aligned (no user-drag resize). */
[data-field-grid] textarea {
  resize: none;
  height: 2.15rem;
  line-height: 1.35;
  white-space: pre-wrap;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}
[data-field-grid] textarea::-webkit-scrollbar { width: 6px; }
[data-field-grid] textarea::-webkit-scrollbar-track { background: transparent; }
[data-field-grid] textarea::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 3px;
}
[data-field-grid] textarea::-webkit-scrollbar-thumb:hover { background: var(--muted-strong); }
[data-field-grid] label[data-remove] {
  display: inline-flex;
  /* Pin to the last col so a row with References hidden still lands the
     ✕ in the same column as rows where References is visible. */
  grid-column: 6;
  align-items: center;
  justify-content: center;
  color: var(--muted-strong);
  padding: 0.2rem;
  font-family: var(--hand);
  font-size: 1.1rem;
  cursor: pointer;
  text-align: center;
}
[data-field-grid] label[data-remove] input { display: none; }
[data-field-grid] label[data-remove]:hover { color: var(--warn-text); }

/* The schema draft editor needs more than the default 32rem reading column, but
   stays within the design system's own content max (55rem) so it reads as one
   with the rest of the app rather than full-bleed. */
main:has([data-field-grid]) { max-width: 62rem; }
main:has([data-field-grid]) > * { max-width: 100%; }

/* Mobile: the 4-column grid gets too narrow on phones (the fixed Type column
   eats the width), so each field row becomes a stacked card with full-width
   controls and its own visible labels. Desktop above is untouched. */
@media (max-width: 768px) {   /* --bp-compact */
  [data-field-grid] { display: block; }
  [data-field-grid] [data-col-head] { display: none; }
  /* Consistent spacing rhythm: card padding 0.9rem; between fields 0.9rem;
     label→control gap 0.35rem (half the between-fields gap). */
  [data-field-grid] [data-row] {
    display: block;
    position: relative;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0.9rem;
    margin-bottom: 0.75rem;
  }
  [data-field-grid] label[data-field] { display: block; margin-bottom: 0.9rem; }
  [data-field-grid] label[data-field]:last-of-type { margin-bottom: 0; }
  [data-field-grid] label[data-field] > span {
    display: block;
    margin-bottom: 0.35rem;
    font-family: var(--mono-label);
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted-strong);
  }
  [data-field-grid] label[data-field] input,
  [data-field-grid] label[data-field] select,
  [data-field-grid] label[data-field] textarea {
    width: 100%;
    min-height: 0;
    padding: 0.45rem 0.65rem;
    line-height: 1.3;
  }
  /* Remove ✕ sits in the card's top-right corner, lifted so it clears the first
     field's input; its right edge lines up with the fields below. */
  [data-field-grid] label[data-remove] {
    position: absolute;
    top: 0.5rem;
    right: 0.65rem;
    width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    font-size: 1.15rem;
  }
  /* Keep the first field's label text clear of the ✕ above it. */
  [data-field-grid] label[data-field]:first-of-type > span { padding-right: 2rem; }

  /* Action buttons stack full-width instead of cramming into one row. */
  [data-form-actions] { flex-direction: column; align-items: stretch; }
}

[data-form-actions] {
  display: flex;
  gap: 0.5rem;
  justify-content: space-between;
  margin-top: 1rem;
}
[data-form-actions] > [data-primary-slot] { margin-left: auto; }

/* Read-only schema view */
[data-schema-fields] {
  width: 100%;
  border-collapse: collapse;
}
[data-field-hint] { margin-top: 0.15rem; color: var(--muted-strong); line-height: 1.3; }
[data-schema-fields] th,
[data-schema-fields] td {
  border: 0.06rem solid var(--border-soft);
  padding: 0.4rem 0.7rem;
  text-align: left;
  font-size: 0.95rem;
}
[data-schema-fields] th {
  background: #f4f3f1;
  font-family: var(--mono-label);
  font-weight: 600;
  color: var(--muted-strong);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.7rem;
}

/* Utilities used in a very small number of places */
small { color: var(--muted-strong); }

[data-back] {
  align-self: flex-start;
  width: fit-content;
  color: var(--muted-strong);
  text-decoration: none;
  font-size: 0.9rem;
}
[data-back]:hover { color: var(--text); text-decoration: underline; }

table[data-review] {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--card);
  font-size: 0.85rem;
}
table[data-review] th,
table[data-review] td {
  border-bottom: 1px solid var(--border);
  padding: 0.6rem 0.9rem;
  text-align: left;
  vertical-align: middle;
}
table[data-review] tr:last-child td { border-bottom: none; }
table[data-review] th {
  background: var(--bg);
  font-family: var(--sans);
  font-weight: 600;
  color: var(--sub);
  text-transform: none;
  letter-spacing: 0;
  font-size: 0.75rem;
  border-bottom: 1px solid var(--border);
}
table[data-review] td { color: var(--ink); }
table[data-review] tbody tr:hover { background: var(--bg); }
/* Bigger multi-select checkboxes for the batch-approve selection. */
table[data-review] input[type="checkbox"] {
  width: 1.3rem;
  height: 1.3rem;
  cursor: pointer;
}
table[data-review] input[type="checkbox"]:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

section[data-review-block] {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  padding: 0.9rem 1.1rem;
  margin: 0.75rem 0;
}
section[data-review-block] header {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  margin-bottom: 0.5rem;
}
/* Field-level action (Approve) sits below the claim card — space it off the card.
   align-items overrides the base `form` rule's `stretch` (line ~190), which in a
   flex column blew the button out to the full card width — next to the content-width
   buttons inside the cards it read as a different kind of control. */
section[data-review-block] > form,
section[data-review-block] > p { margin-top: 0.6rem; }
section[data-review-block] > form { align-items: flex-start; }
/* Claims that agree on a value stack as direct siblings instead of going into
   [data-claims-grid], which is conflict-only — so the gap comes from here. */
section[data-review-block] > article[data-claim] + article[data-claim] { margin-top: 0.6rem; }
section[data-review-block][data-tone="conflict"] {
  border-color: var(--conflict-border);
  background: var(--conflict-bg);
}
section[data-review-block][data-tone="ok"] {
  border-color: var(--approved-border);
  background: var(--approved-bg);
}

[data-claims-grid] {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.8rem;
}
article[data-claim] {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 0.75rem 0.9rem;
  background: var(--card);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
article[data-claim][data-chosen] {
  border-color: var(--accent);
  background: var(--chosen-bg);
}
article[data-claim] blockquote {
  border-left: 2px solid var(--accent);
  padding: 0.35rem 0.6rem;
  margin: 0;
  color: var(--ink);
  font-size: 0.85rem;
  line-height: 1.5;
  background: var(--card);
  border-radius: 0 6px 6px 0;
}
article[data-claim] form {
  margin-top: auto;
}

ul[data-docs] {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
ul[data-docs] li {
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  display: flex;
  align-items: center;
  gap: 0.55rem;
  flex-wrap: wrap;
  font-size: 0.85rem;
  color: var(--ink);
}
ul[data-docs] li strong { min-width: 8rem; font-weight: 600; }
/* Document date sits at the right end of the row (per wireframe). */
ul[data-docs] li [data-doc-date] {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--sub);
  white-space: nowrap;
}

input[type="file"] {
  font: inherit;
  font-family: var(--sans);
  padding: 0.4rem 0;
  color: var(--sub);
}

[data-dropzone] {
  position: relative;
  display: block;
  border: 2px dashed var(--border-strong);
  border-radius: var(--radius-lg);
  padding: 2.25rem 1rem;
  text-align: center;
  color: var(--sub);
  cursor: pointer;
  background: var(--bg);
  transition: border-color 0.15s, background 0.15s, color 0.15s;
}
[data-dropzone]:hover {
  border-color: var(--accent);
  background: var(--accent-bg);
  color: var(--accent-dark);
}
/* Overlay the real file input across the whole zone so a click anywhere in
   the label opens the file picker. Drop-to-append still goes through the
   inline JS listener; the overlay just widens the click target. */
[data-dropzone] input[type="file"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  opacity: 0;
  cursor: pointer;
}
[data-dropzone][data-dragover] {
  border-color: var(--accent);
  border-style: solid;
  background: var(--accent-bg);
  color: var(--accent-dark);
}

/* Upload preview list — staged files shown as cards before the user commits.
   Each row: 📄 filename … size · ✕ */
ul[data-file-list] {
  list-style: none;
  padding: 0;
  margin: 0.75rem 0 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
ul[data-file-list]:empty { display: none; }
ul[data-file-list] li {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.5rem 0.75rem;
  background: var(--card);
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.85rem;
  color: var(--ink);
}
ul[data-file-list] li small {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--sub);
}
ul[data-file-list] [data-remove] {
  background: transparent;
  border: none;
  color: var(--sub);
  padding: 0.15rem 0.4rem;
  cursor: pointer;
  font-family: var(--sans);
  font-size: 0.9rem;
  line-height: 1;
  border-radius: 4px;
  transition: color 0.1s, background 0.1s;
}
ul[data-file-list] [data-remove]:hover {
  color: var(--conflict);
  background: var(--conflict-bg);
}

/* Uploading state. Every file is hashed and written inside the request, so a
   large batch leaves the page looking inert for seconds — long enough for the
   user to press the button again. */
/* Explicit block: `form` is a flex column (line ~183), and letting this <p>
   inherit that layout stacks the spinner above its own label. */
[data-upload-status] {
  display: block;
  margin: 0.55rem 0 0;
  font-size: 0.85rem;
  color: var(--sub);
}
[data-upload-status]:empty { display: none; }

/* The submit button is a flex row, so the markup space before the count
   collapses — "Attach3 documents" without this. One word space, no more: the
   verb and the count read as a single label ("Attach 3 documents"). */
button [data-count]:not(:empty) { margin-left: 0.25rem; }

form[data-uploading] [data-dropzone] {
  opacity: 0.55;
  pointer-events: none;
}
form[data-uploading] [data-upload-status]::before {
  content: "";
  display: inline-block;
  width: 0.7em;
  height: 0.7em;
  margin-right: 0.45em;
  vertical-align: -0.03em;
  border: 2px solid var(--border-strong);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: upload-spin 0.7s linear infinite;
}
@keyframes upload-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  form[data-uploading] [data-upload-status]::before { animation: none; }
}

/* ---- review split panel: document viewer + claims ---- */
/* The review page needs the full window for its two panes — break out of the
   narrow reading column `main` imposes on ordinary pages. */
main:has([data-review-split]) { max-width: min(1720px, 95vw); }
main:has([data-review-split]) > * { max-width: none; }

[data-review-split] {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  margin-top: 1rem;
}

[data-viewer] {
  flex: 5 1 0;
  min-width: 0;
  position: sticky;
  top: 0.75rem;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg);
  overflow: hidden;
  max-height: calc(100vh - 1.5rem);
}
[data-viewer-tabs] {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  padding: 0.55rem 0.7rem;
  border-bottom: 1px solid var(--border);
  background: var(--card);
}
[data-doctab] {
  font: inherit;
  font-family: var(--sans);
  font-size: 0.8rem;
  font-weight: 500;
  cursor: pointer;
  border: 1px solid var(--border-strong);
  background: var(--card);
  color: var(--sub);
  padding: 0.3rem 0.7rem;
  border-radius: 6px;
  user-select: none;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
[data-doctab]:hover {
  border-color: var(--accent);
  background: var(--accent-bg);
  color: var(--accent-dark);
}
[data-doctab][data-active] {
  border-color: var(--accent);
  color: var(--ink);
  background: var(--accent-bg);
  font-weight: 600;
  box-shadow: none;
}

[data-stage] {
  flex: 1;
  overflow: auto;
  padding: 1rem;
  display: flex;
  justify-content: center;
  background: var(--bg);
}
[data-pagewrap] {
  position: relative;
  align-self: flex-start;
  background: #fff;
  box-shadow: 0 2px 8px rgba(27, 30, 36, 0.08);
  border-radius: 4px;
  overflow: hidden;
}
[data-pagewrap] canvas,
[data-pagewrap] img { display: block; }
[data-overlay] {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
/* Every claim's source box is always faintly outlined so all locations are
   visible at a glance; the focused one lights up. */
[data-box] {
  position: absolute;
  border: 0.13rem solid var(--box-color, var(--accent));
  border-radius: 0.15rem;
  background: color-mix(in srgb, var(--box-color, var(--accent)) 6%, transparent);
  opacity: 0.5;
  cursor: pointer;
  pointer-events: auto;
  user-select: none;
  transition: opacity 0.12s, box-shadow 0.12s, background 0.12s;
}
[data-box][data-heat="low"] { border-style: dashed; border-color: var(--conflict); }
[data-box].hl {
  opacity: 1;
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 15%, transparent);
  box-shadow: 0 0 0 2px var(--accent-bg);
  z-index: 3;
}
[data-viewer-empty] { color: var(--sub); align-self: center; margin: auto; font-family: var(--sans); }

/* Loading screen while a source PDF fetches — rail mirrors the confidence bar. */
[data-viewer-loading] {
  align-self: center;
  margin: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--sans);
}
[data-loading-bar] {
  width: 180px;
  height: 5px;
  border-radius: 3px;
  background: #e9ebee;
  overflow: hidden;
}
[data-loading-fill] {
  height: 100%;
  width: 0;
  border-radius: 3px;
  background: var(--accent);
  transition: width 0.2s ease;
}
[data-loading-bar][data-indeterminate] [data-loading-fill] {
  width: 40%;
  transition: none;
  animation: viewer-loading-slide 1.1s ease-in-out infinite;
}
@keyframes viewer-loading-slide {
  0%   { transform: translateX(-120%); }
  100% { transform: translateX(320%); }
}
[data-loading-label] { color: var(--sub); font-size: 0.8rem; }
@media (prefers-reduced-motion: reduce) {
  [data-loading-bar][data-indeterminate] [data-loading-fill] {
    animation: none;
    width: 50%;
  }
}

[data-pagenav] {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 0.75rem;
  border-top: 1px solid var(--border);
  background: var(--card);
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--sub);
}
[data-pagenav] button {
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 500;
  cursor: pointer;
  border: 1px solid var(--border-strong);
  background: var(--card);
  color: var(--ink);
  border-radius: 6px;
  padding: 0.25rem 0.6rem;
  user-select: none;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
[data-pagenav] button:hover:not(:disabled) {
  border-color: var(--accent);
  background: var(--accent-bg);
  color: var(--accent-dark);
}
[data-pagenav] button:disabled {
  opacity: 0.4;
  cursor: default;
  background: var(--card);
  color: var(--sub);
}

[data-thumbs] {
  display: flex;
  gap: 0.4rem;
  padding: 0.5rem 0.75rem;
  overflow-x: auto;
  border-top: 1px solid var(--border);
  background: var(--card);
}
[data-thumbs]:empty { display: none; }
[data-thumb] {
  border: 1.5px solid var(--border-strong);
  border-radius: 4px;
  cursor: pointer;
  background: #fff;
  flex: 0 0 auto;
  transition: border-color 0.12s;
}
[data-thumb]:hover { border-color: var(--accent); }
[data-thumb][data-active] { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-bg); }

/* Flex column with gaps (not per-section margins) so the first card's top
   aligns exactly with the viewer panel beside it. */
[data-claims] { flex: 6 1 0; min-width: 0; display: flex; flex-direction: column; gap: 0.75rem; }
[data-claims] > [data-review-block] { margin: 0; }

/* provenance line + the "view source" navigation cue.
   The whole claim is the click target; this label says where it came from and
   makes the source link obvious. */
[data-source] {
  display: block;
  margin-top: 0.2rem;
  color: var(--sub);
  font-family: var(--mono);
  font-size: 0.72rem;
}
[data-view] {
  margin-left: 0.45rem;
  white-space: nowrap;
  color: var(--accent);
  font-family: var(--sans);
  font-weight: 500;
  font-size: 0.75rem;
  padding: 1px 5px;
  border-radius: 3px;
  transition: background 0.12s;
}
/* The evidence-panel variant is a real link ("open in document ↗") rather than
   the review page's JS-wired span, so it has to shed the underline to match. */
a[data-view] { text-decoration: none; }
a[data-view]:hover,
a[data-view]:focus-visible { background: var(--accent-bg); color: var(--accent-dark); }

/* hover/jump highlight link between a claim and its box */
article[data-claim].hl,
[data-review-block] p[data-claim-pk].hl {
  box-shadow: 0 0 0 2px var(--accent-bg);
  border-radius: var(--radius);
}
/* Persistent "selected" state — set on click/tap so touch (no hover) keeps the
   claim↔box link. Stronger than the hover cue. */
article[data-claim].sel,
[data-review-block] p[data-claim-pk].sel {
  box-shadow: 0 0 0 2px var(--accent);
  border-radius: var(--radius);
}
/* when the claim is active, light up its "view source" cue too */
[data-claim-pk].hl [data-view],
[data-claim-pk].sel [data-view] {
  background: var(--accent-bg);
  color: var(--accent-dark);
}
[data-claims] [data-claim-pk] { cursor: pointer; }
[data-thumb], [data-doctab], article[data-claim] { -webkit-user-select: none; user-select: none; }

@media (max-width: 900px) {   /* --bp-tablet */
  [data-review-split] { flex-direction: column; }
  /* flex-basis:0 proportions width side-by-side but collapses height when
     stacked — size the panes to content instead. */
  [data-review-split] [data-viewer],
  [data-review-split] [data-claims] { flex: none; width: 100%; }
  /* Size the viewer to content and let the whole page scroll, so its inner
     stage doesn't trap touch-scroll over the PDF. */
  [data-viewer] { position: static; height: auto; max-height: none; }
  [data-viewer] [data-stage] { flex: none; overflow: visible; }
  [data-viewer] [data-thumbs] { overflow-y: hidden; }
}
@media (prefers-reduced-motion: reduce) {
  [data-box], [data-doctab] { transition: none; }
}

/* ---- document detail: PDF viewer with element picker + right panel ---- */
main:has([data-doc-split]) { max-width: min(1720px, 95vw); }
main:has([data-doc-split]) > * { max-width: none; }

[data-doc-split] {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  margin-top: 1rem;
}
[data-doc-split] [data-viewer] { flex: 5 1 0; min-width: 0; }
[data-doc-panel] {
  flex: 4 1 0;
  min-width: 0;
  border: 0.09rem solid var(--border);
  border-radius: 0.6rem;
  padding: 0.9rem 1rem;
  background: var(--card);
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
[data-doc-panel-empty] { color: var(--muted-strong); }

/* Every OCR element is faintly outlined so operators can see the pickable
   regions at a glance; boxes that already back a claim are filled; the
   selected one lights up. */
[data-box][data-element-only] {
  opacity: 0.25;
  background: transparent;
  border-width: 0.09rem;
}
[data-box][data-element-only]:hover { opacity: 0.75; }
[data-box][data-has-claims] { opacity: 0.55; }
[data-box][data-selected] {
  opacity: 1;
  border-color: var(--accent);
  border-width: 0.18rem;
  background: color-mix(in srgb, var(--highlight) 22%, transparent);
  box-shadow: 0 0 0 0.2rem var(--highlight);
  z-index: 3;
}

[data-selected-header] {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
[data-selected-header] [data-close] {
  margin-left: auto;
  border: 0;
  background: transparent;
  cursor: pointer;
  font-size: 1.1rem;
  color: var(--muted-strong);
}
[data-selected-element] blockquote {
  margin: 0;
  padding: 0.55rem 0.75rem;
  border-left: 0.18rem solid var(--border);
  background: var(--card);
  font-size: 0.9rem;
  color: var(--muted-strong);
  max-height: 12rem;
  overflow: auto;
}
/* The words a claim was read from, inside the block the OCR returned. The page
   overlay can only outline the whole block — there are no per-token coordinates —
   so this is where "which part of the table" actually gets answered. Underline as
   well as background, since the mark has to survive being read as prose. */
[data-selected-element] mark[data-claim-match] {
  background: var(--warn-bg);
  color: inherit;
  border-radius: 0.15rem;
  padding: 0 0.1rem;
  box-shadow: inset 0 -0.09rem 0 var(--warn-border);
}
/* Explains the mismatch the client reported: the box looked wrong because it
   covered a whole table. Only shown when there is a mark to point at. */
[data-whole-block-note] {
  margin: 0.35rem 0 0;
  color: var(--sub);
}
[data-existing-claims] h3, [data-create-claim] h3 {
  font-size: 0.85rem;
  margin: 0.3rem 0 0.4rem;
  color: var(--muted-strong);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* Leading the panel: when the element carries claims they move above the OCR
   text, so the heading needs to not collide with the header above it. */
[data-selected-element] > [data-existing-claims]:first-of-type { margin-top: 0.2rem; }
[data-existing-list] { display: flex; flex-direction: column; gap: 0.35rem; }
[data-claim-item] {
  border: 0.09rem solid var(--border-soft);
  border-radius: 0.35rem;
  padding: 0.4rem 0.55rem;
  background: #fff;
}
[data-create-claim] form[data-add-claim-form] { gap: 0.4rem; margin-top: 0.25rem; }
[data-create-claim] label { display: block; margin-bottom: 1rem; }
[data-create-claim] label > span { display: block; margin-bottom: 0.35rem; }
[data-create-claim] input, [data-create-claim] select { width: 100%; }
[data-create-claim] [data-add-claim-toggle] { margin-top: 0.25rem; }
[data-create-claim] [data-field-actions] {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-top: 0.25rem;
}

/* Metadata cluster (type pill + date + Edit), right-aligned in the header. */
[data-doc-meta-summary] {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.55rem;
  margin: 0;
  margin-left: auto;
}
[data-doc-meta-summary] [data-edit-meta] { margin-left: 0.25rem; }

/* Right-docked drawer hosting the htmx-swapped metadata edit form. */
[data-edit-drawer]:empty { display: none; }
[data-edit-drawer] {
  position: fixed;
  top: 4.5rem;
  right: 1rem;
  width: 340px;
  max-width: calc(100vw - 2rem);
  max-height: calc(100vh - 5.5rem);
  overflow: auto;
  z-index: 40;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: 0 8px 24px rgba(27, 30, 36, 0.12);
  padding: 1rem 1.1rem 1.1rem;
}

form[data-doc-metadata] {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin: 0;
}
form[data-doc-metadata] [data-inline-label] {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin: 0;
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--sub);
  text-transform: none;
  letter-spacing: 0;
}
form[data-doc-metadata] input[type="text"],
form[data-doc-metadata] input[type="date"] {
  height: 2.1rem;
  padding: 0 0.75rem;
  box-sizing: border-box;
  width: 100%;
}
form[data-doc-metadata] [data-field-actions] {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-top: 0.25rem;
}

@media (max-width: 900px) {   /* --bp-tablet */
  [data-doc-split] { flex-direction: column; }
  /* Size panes to content (same flex-basis:0 collapse as the review split). */
  [data-doc-split] [data-viewer],
  [data-doc-split] [data-doc-panel] { flex: none; width: 100%; }
}

/* ---- global documents list ---- */
body[data-page="documents-list"] main { max-width: 45rem; }
body[data-page="documents-list"] main > * { max-width: 100%; }

/* ---- pagination controls ---- */
nav[data-pagination] {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  margin: 1rem 0;
  font-size: 0.9rem;
  flex-wrap: wrap;
}
nav[data-pagination]:empty { display: none; }
nav[data-pagination] [data-page-num],
nav[data-pagination] [data-nav-arrow] {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.1rem;
  height: 2.1rem;
  padding: 0 0.55rem;
  border: 0.09rem solid var(--border);
  border-radius: 0.4rem;
  background: var(--card);
  color: var(--text);
  text-decoration: none;
  user-select: none;
  transition: border-color 0.12s, background 0.12s, color 0.12s;
}
nav[data-pagination] a[data-page-num]:hover,
nav[data-pagination] a[data-nav-arrow]:hover {
  border-color: var(--accent);
  background: var(--accent-bg);
  color: var(--accent-dark);
}
nav[data-pagination] [data-page-num][data-current] {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
  font-weight: 700;
  cursor: default;
}
nav[data-pagination] [data-disabled] {
  opacity: 0.35;
  cursor: default;
}
nav[data-pagination] [data-page-ellipsis] {
  padding: 0 0.35rem;
  color: var(--muted-strong);
  user-select: none;
}
nav[data-pagination] [data-page-info] {
  margin-left: 0.6rem;
  color: var(--muted-strong);
  font-size: 0.8rem;
}

/* ---- shared cite-a-passage picker (add claim, deviation, correction) ---- */
[data-manual-claim] { display: flex; flex-direction: column; gap: 0.6rem; }
[data-manual-claim] label { display: block; margin: 0; }
[data-manual-claim] label > span { display: block; font-size: 0.85rem; color: var(--muted-strong); margin-bottom: 0.2rem; }
[data-manual-claim] select,
[data-manual-claim] input[type="text"],
[data-manual-claim] input[type="search"] {
  width: 100%;
  box-sizing: border-box;
}
[data-deviation-form] [data-element-picker] { max-width: 32rem; margin: 0 0 0.9rem; }
[data-element-picker] {
  max-height: 22rem;
  overflow-y: auto;
  border: 0.09rem solid var(--border-soft);
  border-radius: 0.4rem;
  background: #fff;
}
ul[data-manual-element-list] {
  list-style: none;
  margin: 0;
  padding: 0;
}
ul[data-manual-element-list] li {
  display: grid;
  grid-template-columns: auto auto 1fr auto;
  gap: 0.5rem;
  align-items: baseline;
  padding: 0.4rem 0.6rem;
  cursor: pointer;
  border-bottom: 0.09rem dashed var(--border-soft);
  transition: background 0.1s;
}
ul[data-manual-element-list] li:last-child { border-bottom: 0; }
ul[data-manual-element-list] li:hover { background: var(--accent-bg); }
ul[data-manual-element-list] li[data-selected] {
  background: color-mix(in srgb, var(--highlight) 25%, transparent);
  box-shadow: inset 0.18rem 0 0 var(--accent);
}
ul[data-manual-element-list] [data-page] { color: var(--muted-strong); font-size: 0.8rem; }
ul[data-manual-element-list] [data-preview] {
  color: var(--text);
  font-size: 0.85rem;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
}
ul[data-manual-element-list] li[data-expanded] [data-preview] {
  -webkit-line-clamp: unset;
  line-clamp: unset;
  overflow: visible;
}
ul[data-manual-element-list] [data-toggle] {
  align-self: start;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--muted-strong);
  font-size: 0.85rem;
  cursor: pointer;
  line-height: 1;
}
ul[data-manual-element-list] [data-toggle]:hover,
ul[data-manual-element-list] [data-toggle]:focus-visible {
  color: var(--accent-dark);
  outline: none;
}
[data-manual-value-row] { display: flex; gap: 0.6rem; }
[data-manual-value-row] label { flex: 1; }
[data-manual-claim] [data-error] { color: var(--warn-border); margin: 0; }

/* ---- entity chooser (multi-attach docs) ---- */
ul[data-entity-picker] {
  list-style: none;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: 0.6rem;
  margin: 1rem 0;
}
ul[data-entity-picker] a {
  display: block;
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  color: var(--ink);
  text-decoration: none;
  transition: border-color 0.12s, background 0.12s;
}
ul[data-entity-picker] a:hover {
  border-color: var(--accent);
  background: var(--accent-bg);
}
ul[data-entity-picker] small {
  display: block;
  margin-top: 0.2rem;
  color: var(--sub);
  font-family: var(--mono);
  font-size: 0.7rem;
}

body[data-page="entities-list"] main { max-width: 45rem; }
body[data-page="entities-list"] main > * { max-width: 100%; }

body[data-page="review-list"] main { max-width: 45rem; }
body[data-page="review-list"] main > * { max-width: 100%; }

form[data-entity-filters] {
  flex-direction: row;
  align-items: center;
  gap: 0.5rem;
  margin: 0.5rem 0 1rem;
  flex-wrap: wrap;
}
/* Inline labels in filter strips — plain sans, not mono-caps.
   Overrides the base `label { font-family: mono, uppercase }` rule. */
form[data-entity-filters] [data-inline-label] {
  display: inline;
  margin: 0;
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--sub);
  text-transform: none;
  letter-spacing: 0;
}
form[data-entity-filters] select,
form[data-entity-filters] input[type="search"],
form[data-entity-filters] input[type="date"] {
  height: 2.1rem;
  padding: 0 0.75rem;
  line-height: normal;
  box-sizing: border-box;
}
form[data-entity-filters] select { width: auto; min-width: 10rem; padding-right: 1.9rem; }
/* Customer names run long — 10rem truncates most of them to "Kiinteistöpalv…". */
form[data-entity-filters] select[data-wide-select] { min-width: 14rem; max-width: 20rem; }
form[data-entity-filters] input[type="search"] {
  flex: 1;
  min-width: 14rem;
  -webkit-appearance: none;
  appearance: none;
}
/* ── Filter menu ───────────────────────────────────────────────────────────
   Everything except the search box collapses into a disclosure panel, so the
   strip stays one row of two controls no matter how many filters a page has.
   <details> rather than a popover: no JS for open/close, and the panel is in
   normal flow so it can be positioned against the trigger. */
details[data-filter-menu] { position: relative; }
details[data-filter-menu] > summary {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  height: 2.1rem;
  padding: 0 0.75rem;
  box-sizing: border-box;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--card);
  color: var(--ink);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  user-select: none;
  list-style: none;
  white-space: nowrap;
}
details[data-filter-menu] > summary::-webkit-details-marker { display: none; }
details[data-filter-menu] > summary:hover { border-color: var(--accent-border); }
details[data-filter-menu] > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Open, or filters applied while closed — both mean "this control is doing
   something", and the second is the one a collapsed panel would otherwise hide. */
details[data-filter-menu][open] > summary,
details[data-filter-menu] > summary:has([data-pill]) {
  border-color: var(--accent-border);
  background: var(--accent-bg);
  color: var(--accent-dark);
}
details[data-filter-menu] [data-filter-chevron] { transition: transform 0.12s ease; }
details[data-filter-menu][open] [data-filter-chevron] { transform: rotate(180deg); }

[data-filter-panel] {
  position: absolute;
  z-index: 20;
  top: calc(100% + 0.35rem);
  left: 0;
  min-width: 17rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 0.9rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  background: var(--card);
  box-shadow: 0 8px 24px rgb(27 30 36 / 0.12);
}
/* Inside the panel each field is its own stacked row, so the labels line up
   down the left edge instead of reading as a sentence across the strip. */
[data-filter-panel] [data-filter-field] { flex-direction: column; align-items: stretch; gap: 0.3rem; }
[data-filter-panel] select,
[data-filter-panel] select[data-wide-select] { width: 100%; min-width: 0; max-width: none; }
[data-filter-panel] [data-date-range] { flex-wrap: wrap; }
[data-filter-panel] [data-date-range] [data-inline-label] { flex-basis: 100%; }
[data-filter-panel] [data-date-range] input[type="date"] { flex: 1; width: auto; min-width: 0; }
[data-filter-panel] [data-filter-clear] { align-self: flex-start; font-size: 0.8rem; }
[data-filter-panel] hr { border: 0; border-top: 1px solid var(--border); margin: 0; }

/* A label and its control travel together when the strip wraps — otherwise a
   "Status" label strands at the end of one line above its own select. */
[data-filter-field],
[data-date-range] {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  flex-shrink: 0;
}
[data-date-range] input[type="date"] { width: 9.5rem; }
[data-date-range] [data-range-sep] { color: var(--sub); font-size: 0.85rem; }

/* Wide tables scroll inside their own wrapper at every width, not just on
   phones. This lived only in the max-width:768px block, which left the div
   inert above it — and a table inside [data-service-agreement] (overflow:
   hidden, for its rounded corners) lost its right-hand columns with no
   scrollbar to reach them in a narrow desktop window. Bare overflow-x, same
   as [data-candidate-scroll]: the mobile block adds overflow-y and nowrap for
   reasons that are specific to touch. */
[data-table-scroll] { overflow-x: auto; }

table[data-entities] {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--card);
  font-size: 0.85rem;
}
table[data-entities] th,
table[data-entities] td {
  padding: 0.6rem 0.9rem;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: middle;
}
table[data-entities] tr:last-child td { border-bottom: none; }
table[data-entities] th {
  font-family: var(--sans);
  font-size: 0.75rem;
  color: var(--sub);
  text-transform: none;
  letter-spacing: 0;
  font-weight: 600;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
table[data-entities] td { color: var(--ink); }
table[data-entities] td a { color: var(--accent); text-decoration: none; font-weight: 500; }
table[data-entities] td a:hover { color: var(--accent-dark); text-decoration: underline; }
table[data-entities] tbody tr[data-clickable] { cursor: pointer; }
table[data-entities] tbody tr:hover { background: var(--bg); }
/* Centered columns: version, docs, pending, conflicts */
table[data-entities] th:nth-child(3),
table[data-entities] th:nth-child(4),
table[data-entities] th:nth-child(5),
table[data-entities] th:nth-child(6),
table[data-entities] td:nth-child(3),
table[data-entities] td:nth-child(4),
table[data-entities] td:nth-child(5),
table[data-entities] td:nth-child(6) { text-align: center; }

/* --- Nested-entity affordances on the entity list ------------------------ */
/* The list shows a count and links through to the detail page rather than
   expanding in place: an expander needs a reserved gutter on every row to keep
   names aligned, and that gutter is dead weight on the many rows with nothing
   nested under them. */

/* Review queue: the entity cell carries a type pill and an ancestry caption, so
   a nested row says what it is and whose it is. */
table[data-review] th:nth-child(2),
table[data-review] td[data-review-entity] { width: 40%; }
td[data-review-entity] { line-height: 1.35; }
td[data-review-entity] > [data-pill] { margin-left: 0.4rem; }

a[data-tree-count] {
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--sub);
  margin-left: 0.4rem;
  white-space: nowrap;
}
table[data-entities] td a[data-tree-count] { font-weight: 400; color: var(--sub); }
a[data-tree-count]:hover { color: var(--accent); }
td[data-review-entity] small[data-ancestry] a { font-weight: 400; }
small[data-ancestry],
small[data-source] {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.72rem;
  color: var(--sub);
}
table[data-entities] small[data-ancestry] a { font-weight: 400; }

label[data-nest-toggle] {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.8rem;
  color: var(--sub);
  white-space: nowrap;
}
label[data-nest-toggle] input { width: auto; margin: 0; }
p[data-flat-notice] { margin: 0 0 0.5rem; font-size: 0.8rem; color: var(--sub); }

/* --- Nested children section on the entity detail page ------------------- */
/* The inner table carries [data-entities] so it inherits every rule above —
   which is why it must keep the same 7 columns. */

[data-entity-children] { margin-top: 1.25rem; }
[data-entity-children] > h2 { margin: 0 0 0.3rem; font-size: 0.95rem; }
[data-children-group] { margin-top: 0.75rem; }
[data-children-group-head] {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  flex-wrap: wrap;
  padding: 0.4rem 0.1rem;
  font-size: 0.85rem;
}
[data-children-group-head] strong { color: var(--ink); }

/* Entity create — fields are rendered one by one (rather than as_p) so the
   parent row can be an htmx swap target, which also means field errors are
   emitted by hand instead of as Django's ul.errorlist. */
body[data-page="entity-create"] [data-error] {
  display: block;
  margin-top: 0.25rem;
  color: var(--warn-text);
  font-size: 0.85rem;
}

/* Entity detail — two-column layout: fields+docs on the left, evidence on the right */
body[data-page="entity-detail"] main { max-width: 84rem; }
body[data-page="entity-detail"] main > * { max-width: 100%; }

[data-entity-detail-split] {
  display: flex;
  gap: 1rem;
  align-items: flex-start;
  /* Left-aligned while the evidence panel is open, so the fields column sits at
     the full-width header's left edge next to the panel. */
  justify-content: flex-start;
}
/* No field selected yet → the evidence panel is hidden (it holds only the empty
   placeholder). Center the lone fields/documents column in that state. */
[data-entity-detail-split]:has(> [data-right] > [data-evidence-empty]) {
  justify-content: center;
}
[data-entity-detail-split] > [data-left] {
  flex: 1 1 44rem;
  min-width: 0;
  max-width: 55rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
/* Reset default block margins on direct children so the flex `gap` above is
   the sole source of vertical rhythm — otherwise <ul>/<h2>/<p>'s browser
   or base-rule margins stack on top of the gap. */
[data-entity-detail-split] > [data-left] > * { margin: 0; }
[data-entity-detail-split] > [data-right] {
  flex: 1 1 38rem;
  min-width: 0;
  max-width: 42rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  padding: 0.9rem 1rem;
  position: sticky;
  top: 1rem;
  max-height: calc(100vh - 6rem);
  overflow-y: auto;
}
/* Panel collapses when it only contains the placeholder sentinel — either the
   initial "no field clicked yet" state, or after ✕ close. Direct-child
   selector so the "no claims yet" empty state (nested one level deeper) is
   still shown when a user does click a claimless field. */
[data-entity-detail-split] > [data-right]:has(> [data-evidence-empty]) { display: none; }

/* Fields table on entity detail — clickable rows, tone tinting on conflicts */
table[data-schema-fields] {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--card);
  font-size: 0.85rem;
}
table[data-schema-fields] th,
table[data-schema-fields] td {
  padding: 0.55rem 0.8rem;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: middle;
}
table[data-schema-fields] tr:last-child td { border-bottom: none; }
table[data-schema-fields] th {
  font-family: var(--sans);
  font-size: 0.75rem;
  color: var(--sub);
  text-transform: none;
  letter-spacing: 0;
  font-weight: 600;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
table[data-schema-fields] td { color: var(--ink); }
table[data-schema-fields] tr[data-clickable] { cursor: pointer; }
table[data-schema-fields] tr[data-clickable]:hover { background: var(--bg); }
table[data-schema-fields] tr[data-row-tone="conflict"] { background: var(--conflict-bg); }
table[data-schema-fields] tr[data-row-tone="conflict"]:hover {
  background: color-mix(in srgb, var(--conflict-bg) 70%, var(--conflict-border));
}
table[data-schema-fields] [data-claim-count] {
  color: var(--sub);
  font-family: var(--mono);
  font-size: 0.7rem;
  text-transform: none;
  letter-spacing: 0;
  margin-left: 0.4rem;
}

/* Evidence panel */
[data-evidence-empty] {
  padding: 1.5rem 0.5rem;
  text-align: center;
  color: var(--sub);
}
[data-evidence-header] {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--border);
}
[data-evidence-header] [data-close] {
  margin-left: auto;
  background: transparent;
  color: var(--sub);
  border: none;
  border-radius: 6px;
  padding: 0.15rem 0.5rem;
  font-family: var(--sans);
  font-size: 0.95rem;
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
}
[data-evidence-header] [data-close]:hover {
  color: var(--ink);
  background: var(--bg);
}
[data-evidence] article[data-claim] {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.6rem 0.8rem;
  margin-bottom: 0.6rem;
  background: var(--card);
}
/* Narrow side panel — stack competing claims rather than a 2-col grid. */
[data-evidence] [data-claims-grid] { grid-template-columns: 1fr; }
[data-evidence] [data-claims-grid] article[data-claim] { margin-bottom: 0; }

/* Competing *value*, with every source that asserts it stacked inside — a
   conflict is a disagreement between values, not between documents. Frames the
   option the way a single-claim card frames itself, so the grid reads as "these
   are the values you can pick"; the sources inside drop their own frame,
   because nested cards read as competing again. */
article[data-value-option] {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 0.75rem 0.9rem;
  background: var(--card);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
article[data-value-option][data-chosen] {
  border-color: var(--accent);
  background: var(--chosen-bg);
}
[data-value-option-head] {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
}
[data-value-option-head] > strong { font-size: 1rem; color: var(--ink); }
article[data-value-option] article[data-claim] {
  border: 0;
  background: transparent;
  margin: 0;
  /* Not 0: the review viewer's claim↔box link is a box-shadow ring, which would
     otherwise sit right on the text. */
  padding: 0.2rem 0.25rem;
  border-radius: var(--radius);
}
article[data-value-option] article[data-claim] + article[data-claim] {
  border-top: 1px dashed var(--border);
  padding-top: 0.55rem;
}
/* Sits last in the option's flex column so several options in one grid row line
   their buttons up, however many sources each carries. */
[data-value-option-actions] { margin-top: auto; }
/* align-items overrides the base `form` rule's `stretch`, which blew this button
   out to the full card width — next to the content-width buttons in the single
   claim cards beside it, it read as a different kind of control. */
[data-value-option-actions] form { margin: 0; align-items: flex-start; }

/* Corroboration past the first two sources folds away — spelled out in full, a
   seven-source option pushed the values still to be compared off the panel. */
details[data-more-sources] > summary {
  cursor: pointer;
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--sub);
  list-style: none;
}
details[data-more-sources] > summary:hover { color: var(--ink); }
details[data-more-sources] > summary::-webkit-details-marker { display: none; }
details[data-more-sources] > summary::before { content: "▸ "; color: var(--sub); }
details[data-more-sources][open] > summary::before { content: "▾ "; }
details[data-more-sources][open] > summary { margin-bottom: 0.5rem; }
details[data-more-sources] article[data-claim] + article[data-claim] {
  margin-top: 0.5rem;
}

/* Claim-card internals — shared by the evidence panel and the review grid. */
article[data-claim] [data-claim-header] {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
}
article[data-claim] [data-source] {
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 400;
  color: var(--sub);
  text-transform: none;
  letter-spacing: 0;
  /* Full-width row of the wrapping header flex: the value, doc pill and
     confidence share the line above, provenance plus its source cue own this
     one. Without the basis the cue wraps to a third line of its own; without
     the order the confidence bar does, because provenance is declared before it. */
  flex: 1 1 100%;
  order: 1;
}
article[data-claim] [data-conf] { margin-left: auto; }

/* Confidence bar — 80px rail + tinted fill + mono number.
   Tier drives the color: high (approved), mid (sub), low (conflict + ⚠). */
[data-conf] {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  white-space: nowrap;
}
[data-conf-bar] {
  display: inline-block;
  width: 80px;
  height: 5px;
  border-radius: 3px;
  background: #e9ebee;
  overflow: hidden;
}
[data-conf-fill] {
  display: block;
  height: 100%;
  border-radius: 3px;
  background: var(--sub);
}
[data-conf-num] {
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 500;
  color: var(--sub);
}
[data-conf][data-tier="high"] [data-conf-fill] { background: var(--approved); }
[data-conf][data-tier="high"] [data-conf-num]  { color: var(--approved); }
[data-conf][data-tier="mid"] [data-conf-fill]  { background: var(--sub); }
[data-conf][data-tier="mid"] [data-conf-num]   { color: var(--sub); }
[data-conf][data-tier="low"] [data-conf-fill]  { background: var(--conflict); }
[data-conf][data-tier="low"] [data-conf-num]   { color: var(--conflict); }
article[data-claim] [data-actions] {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  margin-top: auto;
  padding-top: 0.4rem;
  flex-wrap: wrap;
}
article[data-claim] [data-actions] form { margin: 0; }
/* Reject rests on the plain [data-secondary] outline and only takes colour on
   hover, matching the candidate table's reject affordance. The design system has
   no danger variant: conflict-amber means "look here", not "danger". */
article[data-claim] [data-actions] button[data-reject]:hover {
  border-color: var(--conflict);
  background: var(--conflict-bg);
  color: var(--conflict);
}

/* Highlighted claimed value inside the source snippet. Two tints:
     - default        → accent (blue) tint = a standalone / chosen value
     - competing      → warm (amber) tint  = a losing claim in a conflict, i.e.
                        a non-chosen card inside a conflict grid
   Never uses the .hl class — that belongs to the review viewer's
   claim↔box hover link. */
[data-evidence] mark,
article[data-claim] mark {
  background: var(--mark-chosen);
  color: var(--ink);
  font-weight: 600;
  border-radius: 3px;
  padding: 1px 5px;
}
[data-claims-grid] article[data-claim]:not([data-chosen]) mark {
  background: var(--mark-competing);
}
/* Once sources are grouped the chosen flag lives on the option card, so the
   claims inside it must not be tinted as if they lost. */
[data-claims-grid] [data-value-option][data-chosen] article[data-claim] mark {
  background: var(--mark-chosen);
}

[data-evidence] blockquote {
  margin: 0.4rem 0;
  padding: 0.4rem 0.6rem;
  border-left: 2px solid var(--accent);
  color: var(--ink);
  background: var(--card);
  border-radius: 0 6px 6px 0;
  font-size: 0.85rem;
  line-height: 1.5;
}

/* Manual-claim badge — replaces the confidence bar on hand-entered claims. */
article[data-claim] [data-manual-badge] {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--sub);
  white-space: nowrap;
}

/* Grouped "supporting" header — the agreed value, shown once above the
   corroborating sources. */
[data-evidence] [data-value-head],
[data-evidence] [data-evidence-conflict-head] {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 0.6rem;
}
[data-evidence] [data-value-head] [data-value] { font-size: 1rem; font-weight: 600; color: var(--ink); }
[data-evidence] [data-support-count],
section[data-review-block] [data-support-count] {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--sub);
}

/* Conflict row value — competing values shown as "76 m² vs 74.5 m²".
   Chosen (leading) value reads as a link (accent + dashed underline, since the
   row opens its evidence on click); competing values are amber; "vs" is muted. */
[data-conflict-values] {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  flex-wrap: wrap;
}
[data-conflict-values] [data-cv] {
  font-weight: 600;
  color: var(--conflict);
}
[data-conflict-values] [data-cv][data-chosen] {
  color: var(--accent);
  border-bottom: 1px dashed var(--accent-border);
  cursor: pointer;
}
[data-conflict-values] [data-vs] {
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 500;
  color: var(--sub);
}

[data-evidence] [data-field-actions] { margin-top: 0.5rem; }
[data-evidence] [data-field-actions] form { margin: 0; }
[data-evidence] [data-evidence-add] { margin-top: 0.6rem; margin-bottom: 0; }
[data-evidence] [data-evidence-notes] {
  border: 1px solid var(--conflict-border);
  background: var(--conflict-bg);
  border-radius: var(--radius);
  padding: 0.5rem 0.7rem;
  color: var(--conflict);
  font-size: 0.82rem;
  margin-top: 0.7rem;
}

/* Per-field audit trail — the "resolution entry" card below the claim list. */
[data-field-audit] {
  margin-top: 0.8rem;
  padding: 0.6rem 0.8rem;
  border: 1px solid var(--approved-border);
  background: var(--approved-bg);
  border-radius: var(--radius);
  font-size: 0.84rem;
}
[data-field-audit] [data-audit-tag] {
  display: block;
  font-family: var(--mono);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: var(--approved);
  margin-bottom: 0.2rem;
}
[data-field-audit] [data-audit-value] { font-weight: 600; color: var(--ink); }
[data-field-audit] [data-audit-meta] {
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--sub);
  margin-top: 0.3rem;
}
[data-field-audit] [data-audit-history] { margin-top: 0.5rem; }
[data-field-audit] [data-audit-history] > summary {
  cursor: pointer;
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--sub);
  list-style: none;
}
[data-field-audit] [data-audit-history] > summary:hover { color: var(--ink); }
[data-field-audit] [data-audit-history] > summary::-webkit-details-marker { display: none; }
[data-field-audit] [data-audit-history] > summary::before {
  content: "▸ ";
  color: var(--sub);
}
[data-field-audit] [data-audit-history][open] > summary::before { content: "▾ "; }
[data-field-audit] [data-audit-history] ul {
  list-style: none;
  margin: 0.4rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
[data-field-audit] [data-audit-history] li {
  padding-top: 0.35rem;
  border-top: 1px solid var(--approved-border);
}
[data-field-audit] [data-audit-old-value] { color: var(--sub); }
[data-field-audit] [data-audit-superseded] {
  font-family: var(--mono);
  font-size: 0.64rem;
  color: var(--sub);
  margin-left: 0.35rem;
}

@media (max-width: 900px) {   /* --bp-tablet */
  [data-entity-detail-split] { flex-direction: column; }
  /* Size columns to content when stacked (their flex-basis would inflate height). */
  [data-entity-detail-split] > [data-left],
  [data-entity-detail-split] > [data-right] { flex: none; max-width: 100%; }
  [data-entity-detail-split] > [data-right] { position: static; max-height: none; }
}

/* ── Rules (precedence) page ─────────────────────────────────────────────── */
/* Widen out of the narrow reading column, matching entity/document detail. */
body[data-page="rules"] main { max-width: 84rem; }
body[data-page="rules"] main > * { max-width: 100%; }

[data-rules-scope] {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin: 0.5rem 0 1rem;
}
[data-rules-scope] label {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--sub);
  white-space: nowrap;
  flex: 0 0 auto;
}
[data-rules-scope] select { width: auto; min-width: 16rem; max-width: 28rem; flex: 0 1 auto; }

[data-rule-list] {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

[data-rule-card] {
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--card);
  padding: 0.7rem 0.9rem;
  box-shadow: 0 1px 2px rgba(27, 30, 36, 0.04);
}
[data-rule-card][data-disabled] { opacity: 0.6; }
/* SortableJS: .dragging = the lifted floating clone, .drag-ghost = drop placeholder. */
[data-rule-card].dragging {
  border-color: var(--accent);
  background: var(--accent-bg);
  box-shadow: 0 6px 18px rgba(27, 30, 36, 0.16);
  cursor: grabbing;
}
.drag-ghost { opacity: 0.4; }
body.dragging-active { cursor: grabbing; user-select: none; }

[data-rule-head] {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
[data-rule-head] > strong { font-size: 0.95rem; }

[data-grip] {
  cursor: grab;
  color: var(--border-strong);
  font-size: 0.95rem;
  line-height: 1;
  user-select: none;
  touch-action: none;
}
[data-grip]:active { cursor: grabbing; }
[data-grip]:hover { color: var(--sub); }
/* Touch / narrow screens: bigger drag handle, drag replaces the move buttons. */
@media (max-width: 900px), (pointer: coarse) {
  [data-grip] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    font-size: 1.3rem;
  }
  [data-rank-chip] [data-grip] { width: 1.6rem; height: 1.6rem; font-size: 1.1rem; }
  /* Flexible title keeps the power toggle on the name's row; long names wrap. */
  [data-rule-head] > strong { flex: 1 1 0; min-width: 0; }
  [data-rule-card] { padding-block: 1rem; }
  [data-rule-actions] [data-move],
  [data-rank-chip] [data-move] { display: none; }
}

[data-rank-num] {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--sub);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.05rem 0.4rem;
  min-width: 1.4em;
  text-align: center;
}

[data-rule-actions] {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  margin-left: auto;
}
[data-rule-actions] form,
[data-inline-form] { margin: 0; display: inline-flex; }

/* Power toggle — compact round on/off control; green when on, muted when off. */
[data-power] {
  flex: 0 0 auto;
  width: 2rem;
  height: 2rem;
  min-height: 0;   /* opt out of the mobile 44px button min-height */
  padding: 0;
  border-radius: 50%;
  background: var(--card);
  border: 1px solid var(--border-strong);
  color: var(--sub);
}
[data-power] svg { width: 1.05rem; height: 1.05rem; }
[data-power]:hover { background: var(--card); border-color: var(--accent); color: var(--accent); }
[data-power][aria-pressed="true"] {
  color: var(--approved);
  border-color: var(--approved-border);
  background: var(--approved-bg);
}
[data-power][aria-pressed="true"]:hover { border-color: var(--approved); color: var(--approved); }

[data-rule-desc] { margin: 0.35rem 0 0; color: var(--sub); }

/* Date-direction picker sits inline in the rule header, not full-width. */
[data-rule-head] select[data-direction] {
  width: auto;
  min-width: 11rem;
  flex: 0 0 auto;
}
/* On touch/narrow it drops to its own full-width line below the name row. */
@media (max-width: 900px), (pointer: coarse) {
  [data-rule-head] select[data-direction] {
    order: 1;
    flex: 1 0 100%;
    width: 100%;
    min-width: 0;
  }
}

/* Document-type hierarchy — a horizontal chain of ranked chips. */
[data-rank-list] {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.6rem;
}
[data-rank-chip] {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-family: var(--mono);
  font-size: 0.76rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.2rem 0.5rem;
}
[data-rank-chip].dragging {
  border-color: var(--accent);
  background: var(--accent-bg);
  box-shadow: 0 4px 12px rgba(27, 30, 36, 0.18);
  cursor: grabbing;
}
[data-rank-chip] [data-type-label] { font-family: var(--sans); font-weight: 600; }
/* Chevron between chips, drawn in CSS so the drag container holds only chips. */
[data-rank-chip]:not(:last-child)::after {
  content: "›";
  margin-left: 0.4rem;
  color: var(--border-strong);
}
/* Drop the between-chip separator on touch/narrow. */
@media (max-width: 900px), (pointer: coarse) {
  [data-rank-chip]:not(:last-child)::after { content: none; }
}

[data-add-type-row] {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.7rem;
}

[data-save-bar] {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin-top: 1.3rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}
[data-save-bar] [data-dirty-hint] {
  font-size: 0.8rem;
  color: var(--conflict);
  font-family: var(--mono);
}

/* Evidence panel: why the blue-highlighted claim was chosen by the rules. */
[data-chosen-reason] {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  margin: 0.1rem 0 0.5rem;
  font-size: 0.78rem;
  color: var(--accent-dark);
}
[data-chosen-reason]::before {
  content: "◆";
  color: var(--accent);
  font-size: 0.7rem;
}

/* ═══════════════════════════════════════════════════════════════════════════
   RESPONSIVE / MOBILE — additive @media rules; desktop above is untouched.
   Breakpoints: 768px = compact (phones + portrait tablets); 900px = split-collapse.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Hamburger nav (no JS): the checkbox holds open/closed state, the <label> is
   the burger. Visually hidden but kept keyboard-focusable. */
[data-nav-toggle] {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;   /* label[for] still toggles it; never intercepts clicks */
}
[data-nav-burger] { display: none; }

@media (min-width: 769px) {
  /* Desktop: burger + checkbox gone, and the menu wrapper dissolves so the tabs
     and user nav lay out exactly as they did before this wrapper existed. */
  [data-nav-toggle] { display: none; }
  [data-nav-menu] { display: contents; }
}

@media (max-width: 768px) {   /* --bp-compact */

  /* ── Header + hamburger ─────────────────────────────────────────────────── */
  body > header {
    position: relative;
    height: var(--nav-h-mobile);
    padding: 0 var(--space-page-mobile);
    z-index: 60;
  }
  [data-nav-burger] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-left: auto;
    width: var(--tap-min);
    height: var(--tap-min);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  /* Burger drawn as three bars in CSS (center bar + two pseudo-elements). */
  [data-nav-burger] span,
  [data-nav-burger] span::before,
  [data-nav-burger] span::after {
    content: "";
    display: block;
    width: 20px;
    height: 2px;
    border-radius: 2px;
    background: var(--ink);
    transition: transform 0.15s ease, opacity 0.15s ease;
  }
  [data-nav-burger] span { position: relative; }
  [data-nav-burger] span::before { position: absolute; top: -6px; left: 0; }
  [data-nav-burger] span::after  { position: absolute; top: 6px;  left: 0; }
  /* Keyboard focus lands on the hidden checkbox — surface it on the burger. */
  [data-nav-toggle]:focus-visible + [data-nav-burger] {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 6px;
  }
  /* Open → morph the burger into an ✕. */
  [data-nav-toggle]:checked + [data-nav-burger] span { background: transparent; }
  [data-nav-toggle]:checked + [data-nav-burger] span::before { transform: translateY(6px) rotate(45deg); }
  [data-nav-toggle]:checked + [data-nav-burger] span::after  { transform: translateY(-6px) rotate(-45deg); }

  /* Menu panel: hidden until checked, then drops below the header as a sheet. */
  [data-nav-menu] { display: none; }
  [data-nav-toggle]:checked ~ [data-nav-menu] {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.15rem;
    position: absolute;
    top: var(--nav-h-mobile);
    left: 0;
    right: 0;
    background: var(--card);
    border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow-pop);
    padding: 0.4rem;
  }
  /* Tabs stack full-width, each a comfortable tap target. */
  body > header nav[data-tabs] {
    flex-direction: column;
    align-items: stretch;
    gap: 0.15rem;
    margin: 0;
    font-size: 0.95rem;
  }
  body > header nav[data-tabs] a {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    min-height: var(--tap-min);
    padding: 0.6rem 0.75rem;
  }
  /* User block (email + log out) sits under the tabs, separated by a divider. */
  body > header [data-nav-menu] > nav:not([data-tabs]) {
    flex-direction: column;
    align-items: stretch;
    gap: 0.15rem;
    margin: 0.15rem 0 0;
    padding-top: 0.4rem;
    border-top: 1px solid var(--border);
    font-size: 0.95rem;
  }
  body > header [data-user] {
    padding: 0.5rem 0.75rem;
    font-size: 0.8rem;
    word-break: break-all;
  }
  body > header [data-nav-menu] > nav:not([data-tabs]) a {
    display: flex;
    align-items: center;
    min-height: var(--tap-min);
    padding: 0.6rem 0.75rem;
    border-radius: 6px;
  }
  body > header [data-nav-menu] > nav:not([data-tabs]) a:hover { background: var(--bg); }

  /* ── Page frame ─────────────────────────────────────────────────────────── */
  main { padding: var(--space-page-mobile); gap: 0.9rem; }
  /* Desktop max-width caps go full-width on phones. */
  main > *,
  body[data-page] main,
  body[data-page] main > * { max-width: 100%; }
  h1 { font-size: var(--fs-h1-mobile); }

  /* ── Form controls: 16px floor prevents iOS focus auto-zoom; comfy height. ── */
  input[type="text"], input[type="email"], input[type="password"],
  input[type="url"], input[type="search"], input[type="date"],
  select, textarea {
    font-size: var(--fs-input-mobile);
    padding: 0.6rem 0.75rem;
  }

  /* ── Buttons: full 44px tap target. ─────────────────────────────────────── */
  button, a[role="button"], input[type="submit"] {
    min-height: var(--tap-min);
    padding: var(--btn-pad-mobile);
  }
  button[data-secondary], a[role="button"][data-secondary] {
    padding: calc(0.7rem - 1px) calc(1.1rem - 1px);
  }
  button[data-ghost], a[role="button"][data-ghost] { padding: 0.6rem 0.5rem; }
  /* A primary action alone in its own <p> spans the width. */
  main > p > a[role="button"]:only-child,
  main > p > button:only-child { width: 100%; }

  /* overflow-x is set for every width further up; these two are the touch-only
     half — overflow-y:hidden keeps vertical drags scrolling the page rather
     than the wrapper on iOS, and nowrap stops narrow columns collapsing into
     unreadable word-per-line stacks. */
  [data-table-scroll] { overflow-y: hidden; -webkit-overflow-scrolling: touch; }
  [data-table-scroll] > table { white-space: nowrap; }

  /* ── Toolbars / filter strips stack vertically. ─────────────────────────── */
  form[data-entity-filters] { flex-direction: column; align-items: stretch; }
  form[data-entity-filters] select,
  form[data-entity-filters] select[data-wide-select],
  form[data-entity-filters] input[type="search"],
  form[data-entity-filters] input[type="date"] {
    width: 100%;
    min-width: 0;
    max-width: none;
    height: auto;
    padding: 0.6rem 0.75rem;
  }
  /* Label above its control, full width, like every other stacked field. */
  [data-filter-field] { display: flex; flex-direction: column; align-items: stretch; gap: 0.25rem; }
  /* The panel spans the strip rather than hanging off the trigger's left edge —
     at 390px a 17rem popover would sit half off-screen. */
  details[data-filter-menu] > summary { justify-content: space-between; height: auto; padding: 0.6rem 0.75rem; }
  [data-filter-panel] { position: static; min-width: 0; box-shadow: none; margin-top: 0.35rem; }
  /* The date pair keeps its row — two stacked full-width date fields with a
     dash between them stop reading as a range. */
  [data-date-range] { display: flex; flex-wrap: wrap; }
  [data-date-range] [data-inline-label] { flex-basis: 100%; }
  [data-date-range] input[type="date"] { flex: 1; }
  [data-title-row] { align-items: flex-start; }
  [data-rules-scope] { flex-wrap: wrap; }
  [data-rules-scope] select { width: 100%; min-width: 0; max-width: none; }

  /* ── Multi-column grids collapse to one column. ─────────────────────────── */
  ul[data-entity-picker] { grid-template-columns: 1fr; }
  /* Conflict claims stack vertically — they overflow side-by-side on a phone. */
  [data-claims-grid] { grid-template-columns: 1fr; }

  /* ── Pagination: bigger tap targets. ────────────────────────────────────── */
  nav[data-pagination] [data-page-num],
  nav[data-pagination] [data-nav-arrow] { min-width: var(--tap-min); height: var(--tap-min); }
  [data-pagenav] button { min-height: 2.2rem; padding: 0.4rem 0.8rem; }

  /* ── Entity-detail evidence → bottom sheet on phones (htmx-driven, no JS). ──
     Hidden until it holds real content; then the panel is the dimmed backdrop
     and the card inside is the sheet. */
  #evidence-panel { display: none; }
  #evidence-panel:has(> [data-evidence]),
  #evidence-panel:has(> [data-manual-claim]) {
    /* Column + justify-end gives the card a definite height so it scrolls on iOS. */
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    position: fixed;
    inset: 0;
    z-index: 100;
    margin: 0;
    padding: 0;
    max-width: none;
    max-height: none;
    border: none;
    border-radius: 0;
    background: rgba(27, 30, 36, 0.45);
    overflow: hidden;
  }
  #evidence-panel > [data-evidence],
  #evidence-panel > [data-manual-claim] {
    width: 100%;
    max-height: 85vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;   /* keep the swipe on the sheet, not the page */
    background: var(--card);
    border-radius: 16px 16px 0 0;
    padding: 0 var(--space-page-mobile) 1.5rem;
  }
  /* Sticky header (title + ✕) pins while the sheet scrolls. */
  #evidence-panel [data-evidence-header] {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--card);
    margin: 0 calc(-1 * var(--space-page-mobile)) 0.75rem;
    padding: 1rem var(--space-page-mobile) 0.7rem;
  }
  #evidence-panel [data-evidence-header] [data-close] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: var(--tap-min);
    min-height: var(--tap-min);
    font-size: 1.2rem;
  }
  /* Lock the page behind the open sheet (the sheet scrolls internally). */
  body:has(#evidence-panel > [data-evidence]),
  body:has(#evidence-panel > [data-manual-claim]) { overflow: hidden; }
}

/* ---- Candidate line-item review -------------------------------------------
   Table layout for row-shaped extraction candidates. Row-scoped forms live
   outside the table (see _candidates.html) and are wired via the form=
   attribute on inputs/buttons, so a single "outerHTML" swap on
   #candidates-section keeps counts and rows in sync after every accept/reject.
   ------------------------------------------------------------------------- */
[data-candidates] { margin: 1.25rem 0; }

[data-candidates-bar] {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.55rem 0.7rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  position: sticky;
  top: 0;
  z-index: 2;
}
[data-candidates-heading] {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
[data-candidates-heading] h2 { margin: 0; font-size: 0.95rem; }
[data-candidate-group-head] [data-merges-into] strong { color: var(--ink); }
[data-candidates-actions] {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}
[data-candidates-actions] form { margin: 0; }

[data-candidate-group] {
  margin-top: 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--card);
}
/* Only the table area scrolls horizontally — the group head stays outside
   the scroll container so it always spans the group's full width, and the
   scrollbar is only shown when the table's natural width truly exceeds the
   panel (never as a phantom due to inputs' intrinsic max-content). */
[data-candidate-scroll] {
  overflow-x: auto;
}
[data-candidate-group-head] {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  flex-wrap: wrap;
  padding: 0.5rem 0.75rem;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  font-size: 0.85rem;
}
[data-candidate-group-head] strong { color: var(--ink); }

[data-candidate-table] {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
}
[data-candidate-table] thead th {
  text-align: left;
  font-weight: 500;
  color: var(--sub);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  padding: 0.4rem 0.5rem;
  border-bottom: 1px solid var(--border);
  background: var(--card);
  white-space: nowrap;
}
[data-candidate-table] tbody tr {
  border-bottom: 1px solid var(--border);
}
[data-candidate-table] tbody tr:last-child { border-bottom: none; }
[data-candidate-table] tbody tr:hover { background: var(--accent-bg); }
[data-candidate-table] td {
  padding: 0.25rem 0.5rem;
  vertical-align: middle;
}
[data-candidate-table] td input[type="text"] {
  width: 100%;
  /* Usable floor per input — the table shrinks columns proportionally down to
     this width. Below the sum of column min-widths, the scroll container
     kicks in. Previously ``min-width: 0`` let inputs collapse to unusable
     sizes; combined with the ``min-width: max-content`` on the table above
     (removed), this caused a phantom scrollbar to show even when the table
     visually fit its container. */
  min-width: 5.5rem;
  padding: 0.3rem 0.45rem;
  border: 1px solid var(--border);
  background: var(--bg);
  border-radius: 4px;
  font: inherit;
  color: var(--ink);
}
[data-candidate-table] td input[type="text"]:hover {
  border-color: var(--border-strong);
}
[data-candidate-table] td input[type="text"]:focus {
  border-color: var(--accent);
  background: var(--card);
  outline: none;
}
[data-candidate-table] td input[data-empty] {
  border-color: var(--conflict-border);
  background: var(--conflict-bg);
}
[data-candidate-table] td input[readonly] {
  cursor: default;
  background: transparent;
  border-color: transparent;
}
[data-candidate-table] td input[readonly]:hover,
[data-candidate-table] td input[readonly]:focus {
  border-color: transparent;
  background: transparent;
  outline: none;
}

[data-candidate-table] [data-col-name] {
  max-width: 14rem;
  color: var(--ink);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

[data-candidate-table] [data-col-conf] {
  width: 8.5rem;
  white-space: nowrap;
}
[data-candidate-table] [data-col-conf] [data-conf-bar] { width: 60px; }

[data-candidate-table] [data-col-actions] {
  width: 4.5rem;
  white-space: nowrap;
  text-align: right;
  /* Pinned to the right edge so accept / reject stay clickable even when the
     table is scrolled horizontally. The opaque background hides the field
     inputs scrolling underneath; the box-shadow gives a subtle depth cue so
     users notice the column is "floating" over the scroll area. */
  position: sticky;
  right: 0;
  background: var(--card);
  box-shadow: -6px 0 6px -6px rgba(27, 30, 36, 0.12);
  z-index: 1;
}
/* Keep the sticky cell's background in sync with the row states —
   without these overrides the pinned column would show ``var(--card)``
   over hovered / striped rows and break the visual continuity. */
[data-candidate-table] tbody tr:hover [data-col-actions] {
  background: var(--accent-bg);
}
[data-candidate-table] thead th[data-col-actions] {
  background: var(--card);
}
[data-candidate-table] [data-col-actions] button {
  padding: 0.2rem 0.5rem;
  min-width: 1.9rem;
  line-height: 1.2;
  font-size: 0.95rem;
  border-radius: 4px;
  border: 1px solid var(--border-strong);
  background: var(--card);
  color: var(--ink);
  cursor: pointer;
}
[data-candidate-table] [data-col-actions] button[data-accept]:hover {
  border-color: var(--approved);
  background: var(--approved-bg);
  color: var(--approved);
}
[data-candidate-table] [data-col-actions] button[data-reject]:hover {
  border-color: var(--conflict);
  background: var(--conflict-bg);
  color: var(--conflict);
}

/* Mobile: table already scrolls horizontally on every viewport via the
   default rules above; the mobile bar stack + actions alignment stay
   here so the review page's control cluster doesn't push off-screen. */
@media (max-width: 768px) {
  [data-candidates-bar] {
    position: static;
    flex-direction: column;
    align-items: stretch;
  }
  [data-candidates-actions] { justify-content: flex-start; }
}

/* -------------------------------------------------------------------------
   Contracts app — customer register, detail, deviations, field evidence
   ------------------------------------------------------------------------- */

/* Utilities specific to the contracts UI. Kept scoped by the [data-*] hooks
   the templates emit so they never leak into entities/documents styling. */
[data-muted] { color: var(--sub); }
[data-eyebrow] {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  color: var(--sub);
  margin: 0 0 0.4rem;
  text-transform: uppercase;
}
[data-caption] { color: var(--sub); }

a[data-disabled],
[role="button"][data-disabled] {
  pointer-events: none;
  opacity: 0.5;
  cursor: not-allowed;
}

/* Section-level page wrappers. Everything sits inside a max-width column so
   long tables don't stretch edge-to-edge on ultrawide monitors. */
body[data-page="contracts-register"] main,
[data-customer-detail],
[data-customer-create],
[data-customer-upload],
[data-deviation-chooser],
[data-deviation-form],
[data-field-evidence] {
  max-width: 55rem;
}
body[data-page="contracts-register"] main > * { max-width: 100%; }

/* Every contracts page is a vertical stack — breadcrumb, header, then blocks —
   so each one spaces its own children instead of relying on their margins. The
   profile needs it most (its cards otherwise butt together and read as one
   table with doubled borders), but the rest lost the same layout when the
   register moved into `main` and were left as plain blocks, which had been
   patched one symptom at a time. Same 1rem as `main`, so every page keeps one
   rhythm. */
[data-customer-detail],
[data-customer-create],
[data-customer-upload],
[data-deviation-chooser],
[data-deviation-form],
[data-field-evidence] {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

[data-breadcrumb] {
  margin: 0;
  font-size: 0.85rem;
}
[data-breadcrumb] a { color: var(--accent); text-decoration: none; }
[data-breadcrumb] a:hover { text-decoration: underline; }
[data-crumb-sep] { color: var(--sub); margin: 0 0.3rem; }
[data-crumb-current] { color: var(--sub); }
[data-crumb-alt] { font-size: 0.78rem; margin-left: 0.35rem; }

/* --- Shared review-status notice ---------------------------------------- */
/* Rendered on the entity detail page and the contracts customer profile. The
   left border carries the state so the page's status is legible before reading
   a word of it. */

[data-review-status] {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  border: 1px solid var(--border);
  border-left: 3px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--card);
  padding: 0.75rem 0.95rem;
  margin: 0 0 1rem;
}
[data-review-status] p { margin: 0; }
[data-review-status-head] {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink);
}
[data-review-status-body] {
  font-size: 0.85rem;
  line-height: 1.45;
  color: var(--sub);
}
[data-review-status-actions] {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-top: 0.3rem;
}
[data-review-status-actions] form { margin: 0; }

[data-review-status][data-state="no_documents"] {
  border-left-color: var(--border-strong);
  background: var(--bg);
}
[data-review-status][data-state="extracting"] {
  border-left-color: var(--accent);
  background: var(--accent-bg);
}
[data-review-status][data-state="needs_review"],
[data-review-status][data-state="conflicts"] {
  border-left-color: var(--conflict);
  background: var(--conflict-bg);
}
[data-review-status][data-state="needs_approval"] {
  border-left-color: var(--border-strong);
}
[data-review-status][data-state="settled"] {
  border-left-color: var(--approved);
  background: var(--approved-bg);
}
/* Terminal state — present but quiet, so it teaches the model without shouting. */
[data-review-status][data-state="settled"] [data-review-status-head] {
  font-weight: 500;
  font-size: 0.85rem;
}
/* The close affordance sits in the notice's top-right without taking a row of
   its own — the notice is a flex column, so the form is pulled out of flow and
   the text keeps the full width. */
[data-review-status] { position: relative; }
[data-review-status-dismiss] {
  position: absolute;
  top: 0.35rem;
  right: 0.4rem;
  margin: 0;
}
[data-review-status-dismiss] [data-dismiss] {
  border: 0;
  background: none;
  color: var(--sub);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  padding: 0.15rem 0.35rem;
  border-radius: var(--radius);
}
[data-review-status-dismiss] [data-dismiss]:hover { color: var(--ink); }
[data-review-status-dismiss] [data-dismiss]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
/* Room for the button so a long heading never runs under it. */
[data-review-status][data-state="settled"] [data-review-status-head],
[data-review-status][data-state="settled"] [data-review-status-body] {
  padding-right: 1.4rem;
}

/* --- Contract register ------------------------------------------------- */

body[data-page="contracts-register"] main > header h1,
[data-customer-detail] > header h1,
[data-customer-create] > header h1,
[data-customer-upload] > header h1,
[data-deviation-chooser] > header h1,
[data-deviation-form] > header h1,
[data-field-evidence] > header h1 {
  margin: 0.1rem 0 0.4rem;
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: -0.005em;
}

body[data-page="contracts-register"] main > header p[data-caption] {
  max-width: 46rem;
  margin: 0;
}

body[data-page="contracts-register"] [data-toolbar] {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
}
/* Sizing only — the input itself is the shared form[data-entity-filters]
   control, so the register's search box matches the other list pages. */
body[data-page="contracts-register"] [data-toolbar] form[data-entity-filters] {
  flex: 1 1 20rem;
  margin: 0;
}
body[data-page="contracts-register"] [data-toolbar] p { margin: 0; }

body[data-page="contracts-register"] [data-count] {
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--sub);
  margin: 0;
}

table[data-contracts] {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
table[data-contracts] th,
table[data-contracts] td {
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
  text-align: left;
  font-size: 0.88rem;
}
table[data-contracts] th {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  color: var(--sub);
  text-transform: uppercase;
  background: var(--bg);
}
table[data-contracts] tbody tr:last-child td { border-bottom: none; }
table[data-contracts] tbody tr:hover { background: var(--bg); }
table[data-contracts] a { color: var(--accent); text-decoration: none; font-weight: 500; }
table[data-contracts] a:hover { color: var(--accent-dark); text-decoration: underline; }
/* Any cell holding more than one pill, not just the services column: the Terms
   column gained a second pill when special clauses started counting toward it,
   and bare siblings wrap flush against each other in a column this narrow. */
table[data-contracts] td [data-pill-group] {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}
table[data-contracts] td[data-empty] {
  display: table-cell;
  border: none;
  background: transparent;
  text-align: center;
  padding: 2rem 1rem;
  color: var(--sub);
  font-style: italic;
}

/* --- Customer detail: header + block scaffolding ----------------------- */

[data-customer-header] {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
}
[data-customer-header] [data-summary] {
  margin: 0.3rem 0 0;
  color: var(--sub);
  font-size: 0.85rem;
}
[data-customer-header] [data-actions] {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

/* Each detail section renders as a card on the page. */
[data-block] {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.25rem 1.25rem 1.4rem;
}
[data-block] > h2 {
  margin: 0 0 1rem;
  font-size: 0.98rem;
  font-weight: 600;
  color: var(--ink);
}
/* A card whose heading is followed by a summary line pulls the two together, so
   the sentence reads as the heading's subtitle rather than as body copy. */
[data-block] > h2:has(+ [data-section-intro]) { margin-bottom: 0.35rem; }
[data-block] > [data-section-intro] {
  margin: 0 0 0.9rem;
  font-size: 0.82rem;
}
[data-block-header] {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  margin: 0 0 0.9rem;
}
[data-block-header] > h2 {
  margin: 0;
  font-size: 0.98rem;
  font-weight: 600;
  color: var(--ink);
}
button[data-expand-all] {
  border: 1px solid var(--border-strong);
  background: var(--card);
  color: var(--ink);
  border-radius: 6px;
  padding: 0.3rem 0.7rem;
  font: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
button[data-expand-all]:hover { border-color: var(--accent); color: var(--accent-dark); }

/* --- Customer details ---------------------------------------------------
   Two-column key/value grid. Field rows flagged as deviations pick up a
   subtle amber tint that matches the deviation section below. */
dl[data-fields] {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 0.4rem 1.5rem;
  margin: 0;
}
/* Denser inside an agreement card, which is already indented by the card's own
   padding and carries fewer, shorter values. */
dl[data-agreement-terms] {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
  gap: 0.4rem 1.25rem;
  margin: 0.95rem 0 0;
}
dl[data-fields] > div,
dl[data-agreement-terms] > div {
  display: flex;
  flex-direction: column;
  padding: 0.55rem 0.75rem;
  border-radius: var(--radius);
  border: 1px solid transparent;
  min-width: 0;
}
dl[data-fields] > div[data-deviates],
dl[data-agreement-terms] > div[data-deviates] {
  background: var(--warn-bg);
  border-color: var(--warn-border);
}
dl[data-fields] dt,
dl[data-agreement-terms] dt {
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--sub);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 0;
}
dl[data-fields] dd,
dl[data-agreement-terms] dd { margin: 0.15rem 0 0; font-weight: 500; }
/* :not([data-why]) so the value's rules stop reaching the small trailing arrow:
   these selectors outrank [data-why], and inheriting the value's ink is what made
   the old "why?" link read as loud as the value it hangs off. */
dl[data-fields] dd a:not([data-why]),
dl[data-agreement-terms] dd a:not([data-why]) { color: inherit; text-decoration: none; }
dl[data-fields] dd a:not([data-why]):hover,
dl[data-agreement-terms] dd a:not([data-why]):hover { color: var(--accent); text-decoration: underline; }

/* Negotiated value first, the standard it replaced struck through beside it. */
dl[data-fields] dd:has([data-was]),
dl[data-agreement-terms] dd:has([data-was]) {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  flex-wrap: wrap;
  font-weight: 600;
}
[data-was] {
  font-size: 0.78rem;
  font-weight: 400;
  color: var(--sub);
  text-decoration: line-through;
}

/* --- Deviations ---------------------------------------------------------
   One row per non-standard term: what it is, what changed, what says so. Filling
   each row amber made a customer with three deviations look like a page of
   warnings, so the tint is reduced to the left edge and the color that remains
   sits on the negotiated value itself. */

ul[data-deviation-list] {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
ul[data-deviation-list] li {
  display: grid;
  grid-template-columns: minmax(9rem, 1.1fr) minmax(7rem, 0.8fr) minmax(11rem, 1.3fr);
  align-items: baseline;
  column-gap: 1rem;
  row-gap: 0.2rem;
  padding: 0.6rem 0.8rem;
  border: 1px solid var(--border);
  border-left: 3px solid var(--warn-border);
  border-radius: var(--radius);
}
ul[data-deviation-list] [data-field-name] { font-weight: 600; }
ul[data-deviation-list] [data-field-name] a { color: inherit; text-decoration: none; }
ul[data-deviation-list] [data-field-name] a:hover { color: var(--accent); text-decoration: underline; }
/* Which service the term belongs to — blank for customer-wide terms. */
ul[data-deviation-list] [data-scope] {
  display: block;
  font-weight: 400;
  font-size: 0.78rem;
  color: var(--sub);
}
ul[data-deviation-list] [data-change] {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  font-family: var(--mono);
  font-size: 0.82rem;
}
ul[data-deviation-list] [data-standard] { color: var(--sub); text-decoration: line-through; }
ul[data-deviation-list] [data-actual] { color: var(--warn-text); font-weight: 600; }
ul[data-deviation-list] [data-citation] { color: var(--sub); font-size: 0.82rem; }
ul[data-deviation-list] [data-citation] a { color: var(--accent); text-decoration: none; }
ul[data-deviation-list] [data-citation] a:hover { text-decoration: underline; }

/* --- Contracted services ----------------------------------------------- */

/* Each agreement is a card whose whole header strip is the disclosure control.
   The caret leads, so a stack of collapsed cards has one scannable left edge. */
[data-service-agreement] {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--card);
  margin-bottom: 0.6rem;
  overflow: hidden;
}
[data-service-agreement]:last-child { margin-bottom: 0; }
[data-service-agreement] > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 0.75rem;
  row-gap: 0.35rem;
  padding: 0.85rem 1rem;
  background: var(--surface-quiet);
  cursor: pointer;
  list-style: none;
}
[data-service-agreement] > summary::-webkit-details-marker { display: none; }
[data-service-agreement] > summary::before {
  content: "▸";
  order: 0;
  flex: 0 0 auto;
  display: inline-block;
  width: 0.8rem;
  color: var(--sub);
  font-size: 0.7rem;
  transition: transform 0.15s ease;
}
[data-service-agreement][open] > summary::before { transform: rotate(90deg); }
[data-service-agreement] > summary h3 {
  order: 1;
  flex: 1 1 auto;
  margin: 0;
  font-size: 0.95rem;
  font-weight: 600;
}
[data-service-agreement] > summary [data-code] {
  order: 2;
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  color: var(--sub);
}
[data-service-agreement] > summary [data-pill],
[data-service-agreement] > summary [data-standard-note] {
  order: 3;
  flex: 0 0 auto;
}
/* Standard terms are the expected case — stated, not badged, so the deviation
   pills on sibling cards are the only thing carrying color. */
[data-service-agreement] > summary [data-standard-note] {
  color: var(--sub);
  font-size: 0.78rem;
}
/* Terms at a glance. Indented to the title, so the caret column stays clean. */
[data-service-agreement] > summary [data-term-summary] {
  order: 4;
  flex: 0 0 100%;
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  padding-left: 1.55rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--sub);
}
[data-term-summary] > span:not(:first-child)::before {
  content: "·";
  margin-right: 0.45rem;
  color: var(--border-strong);
}
[data-term-summary] > span[data-deviates] { color: var(--warn-text); font-weight: 600; }

[data-agreement-body] {
  padding: 0 1rem 1.15rem;
  border-top: 1px solid var(--border);
}

table[data-service-lines] {
  width: 100%;
  margin: 0.95rem 0 0;
  border-collapse: separate;
  border-spacing: 0;
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  overflow: hidden;
}
table[data-service-lines] th,
table[data-service-lines] td {
  padding: 0.55rem 0.8rem;
  font-size: 0.85rem;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
  text-align: left;
}
table[data-service-lines] th {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--sub);
  font-weight: 500;
  background: var(--bg);
}
table[data-service-lines] [data-mono] { font-family: var(--mono); font-size: 0.8rem; }
table[data-service-lines] [data-num] { text-align: right; }
table[data-service-lines] tbody tr:last-child td,
/* A collapsed trace row can be the last row in the table, which would leave the
   line above it doubling up against the table's own bottom edge. */
table[data-service-lines] tbody tr:nth-last-child(2):has(+ tr[data-price-trace-row]) td {
  border-bottom: none;
}

/* Pricing cell — the derivation reads left to right: base struck through, then
   the price actually charged. */
[data-price][data-final] {
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
}
a[data-price][data-final]:hover { color: var(--accent); text-decoration: underline; }
[data-price][data-base] {
  font-size: 0.78rem;
  color: var(--sub);
  text-decoration: line-through;
  font-weight: 400;
}

/* A derived price opens its own trace row — a checkbox drives it so the panel
   costs no script and no request for data the page already rendered. */
label[data-price-toggle] {
  position: relative;    /* containing block for the hidden checkbox below */
  display: inline-flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 0.4rem;
  margin: 0;
  font: inherit;
  color: var(--ink);
  cursor: pointer;
}
label[data-price-toggle] > input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}
label[data-price-toggle] [data-price][data-final] { border-bottom: 1px dotted var(--accent); }
label[data-price-toggle]:hover [data-price][data-final] { color: var(--accent); }
label[data-price-toggle]:focus-within [data-price][data-final] {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

tr[data-price-trace-row] { display: none; }
tr:has(label[data-price-toggle] > input:checked) + tr[data-price-trace-row] { display: table-row; }
tr[data-price-trace-row] > td {
  padding: 0.55rem 0.8rem 0.7rem;
  background: var(--accent-bg);
}
ul[data-price-trace] {
  list-style: none;
  padding: 0;
  margin: 0;
  font-size: 0.78rem;
  color: var(--sub);
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}
ul[data-price-trace] [data-doc] {
  font-family: var(--mono);
  color: var(--accent);
}
ul[data-price-trace] a { color: var(--accent); text-decoration: none; }
ul[data-price-trace] a:hover { text-decoration: underline; }

/* An absent value, named rather than dashed.
   Used where absence is a finding — a contract term nobody stated, a clause whose
   body never extracted. Deliberately NOT used in the product-line table: there a
   gap is just a gap, the column is dropped entirely when no line fills it
   (contracts/line_table.py), and a blank cell is the whole point.
   Mono and muted so it never competes with a real value for attention. */
[data-not-stated] {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.02em;
  color: var(--sub);
  font-style: normal;
}
/* Both grids link their value; an unstated one must not read as clickable
   content the way a real value does. */
dl[data-agreement-terms] dd a:has([data-not-stated]),
dl[data-fields] dd a:has([data-not-stated]) { text-decoration: none; }

/* The way back to the evidence trail, kept deliberately quiet. Under auto-accept
   this is the only route to the competing claims and the correction form, so it
   has to stay on the page — but the value's own link goes to the document, and
   two equal-weight links side by side is exactly the confusion this whole change
   set exists to remove. A superscript arrow rather than a word: it rides on the
   value as a footnote mark instead of repeating a label down every row of two
   grids. Padded so the hit area is bigger than the glyph. */
[data-why] {
  position: relative;
  top: -0.35em;
  margin-left: 0.15rem;
  /* Vertical padding buys hit area an inline glyph this small doesn't have on
     its own; font-weight so it looks the same on a bold clause title as it does
     beside a value. */
  padding: 0.1rem 0.15rem;
  font-size: 0.66rem;
  font-weight: 400;
  line-height: 1;
  color: var(--sub);
  text-decoration: none;
}
[data-why]:hover { color: var(--accent); }
[data-why]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 0.2rem;
}
/* A deviating row turns its dd into a flex container, where vertical-align stops
   applying (hence the relative offset above) and the row gap would push the arrow
   halfway to the struck-through standard beside it. Pull it back onto its value. */
dl[data-fields] dd:has([data-was]) > [data-why],
dl[data-agreement-terms] dd:has([data-was]) > [data-why] { margin-left: -0.3rem; }

/* Same slot as [data-why] but on the baseline, and louder — it takes that link's
   place when a value has a reason to be doubted. Auto-accept means nobody read
   these before they landed on the page, so this is the only thing separating a
   value worth trusting from one worth checking. Sized to be noticed in a scan of
   the column without shouting over the value itself: the value is still the
   answer, this is a footnote on how sure we are of it. */
[data-value-flag] {
  margin-left: 0.35rem;
  padding: 0.05rem 0.3rem;
  border: 1px solid transparent;
  border-radius: 0.25rem;
  font-size: 0.68rem;
  font-family: var(--mono);
  letter-spacing: 0.02em;
  text-decoration: none;
  white-space: nowrap;
  vertical-align: baseline;
}
/* Two sources disagree — a fact about the contract set, not a guess, so it gets
   the same tone the rest of the app uses for a conflict. "conflict" is the
   codebase's own word for this (field_has_conflict, data-pill-tone="conflict");
   the label the operator reads says "discrepancy" instead, because "disputed"
   means something else entirely in a contract. */
[data-value-flag][data-flag="conflict"] {
  color: var(--conflict);
  background: var(--conflict-bg);
  border-color: var(--conflict-border);
}
/* The extractor inferred rather than read. Muted, because a self-reported
   confidence score is a hint, not a finding — treating it as loudly as a real
   disagreement would train operators to ignore both. */
[data-value-flag][data-flag="low-confidence"] {
  color: var(--sub);
  background: var(--bg);
  border-color: var(--border);
}
[data-value-flag]:hover { border-color: var(--accent); color: var(--accent); }
[data-value-flag]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Bespoke clauses lead the card body: they are the terms no baseline covers, so
   they have to be read before anything standard below them. */
[data-special-clauses] {
  margin: 0.95rem 0 0;
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface-quiet);
}
[data-special-clauses] > [data-eyebrow] { margin: 0 0 0.55rem; }
[data-special-clauses] [data-clause-title] {
  margin: 0;
  font-weight: 600;
  font-size: 0.88rem;
}
/* The title is the way back to the passage. Inherits ink rather than going
   accent-blue — a stack of clauses would otherwise read as a list of links
   instead of the prose it is. The dotted underline is the same "this explains
   itself" affordance [data-price-toggle] uses.

   :not([data-why]) because these selectors outrank [data-why]: without it the
   trailing arrow inherits the title's weight and picks up an underline of its
   own, which on a superscript glyph reads as a stray mark. */
[data-special-clauses] [data-clause-title] a:not([data-why]) {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px dotted var(--border-strong);
}
[data-special-clauses] [data-clause-title] a:not([data-why]):hover { border-bottom-color: var(--accent); }
[data-special-clauses] [data-clause-title] a:not([data-why]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-bottom-color: transparent;
}
/* Separate one clause from the next now that they run title/body, title/body. */
[data-special-clauses] [data-clause-body] + [data-clause-title] { margin-top: 0.8rem; }
[data-special-clauses] [data-clause-body] {
  margin: 0.1rem 0 0;
  color: var(--sub);
}
[data-special-clauses] [data-clause-body]:not(:last-child) { margin-bottom: 0.7rem; }

/* --- Documents on file ------------------------------------------------- */

table[data-documents] {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}
table[data-documents] th,
table[data-documents] td {
  padding: 0.65rem 0.85rem;
  font-size: 0.85rem;
  border-bottom: 1px solid var(--border);
  text-align: left;
}
table[data-documents] th {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--sub);
  font-weight: 500;
  background: var(--bg);
}
table[data-documents] tbody tr:last-child td { border-bottom: none; }
table[data-documents] [data-stage] {
  display: inline-block;
  margin-right: 0.35rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--sub);
  border: 1px solid var(--border);
  padding: 0.05rem 0.4rem;
  border-radius: 3px;
  background: var(--bg);
}

/* --- Form errors banner (contracts create / deviation form) ------------ */

ul[data-form-errors] {
  list-style: disc inside;
  margin: 0;
  padding: 0.75rem 1rem;
  color: var(--conflict);
  background: var(--conflict-bg);
  border: 1px solid var(--conflict-border);
  border-radius: var(--radius);
}
ul[data-form-errors] li { margin: 0; }

[data-customer-create] label[data-field],
[data-deviation-form] label[data-field] {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin: 0 0 0.9rem;
  max-width: 32rem;
}
[data-customer-create] label[data-field] > span,
[data-deviation-form] label[data-field] > span {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--sub);
}
[data-customer-create] label[data-field] input[type="text"],
[data-customer-create] label[data-field] select,
[data-deviation-form] label[data-field] input[type="text"],
[data-deviation-form] label[data-field] input[type="search"],
[data-deviation-form] label[data-field] select,
[data-deviation-form] label[data-field] textarea {
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--card);
  font-family: var(--sans);
  font-size: 0.9rem;
}

/* --- Deviation chooser cards ------------------------------------------ */

[data-options] {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
  gap: 1rem;
}
a[data-option] {
  display: block;
  padding: 1.25rem 1.35rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  text-decoration: none;
  color: var(--ink);
  transition: border-color 100ms, background 100ms;
}
a[data-option]:hover {
  border-color: var(--accent-border);
  background: var(--accent-bg);
}
a[data-option] h2 { margin: 0 0 0.35rem; font-size: 1rem; }
a[data-option] p { margin: 0; color: var(--sub); font-size: 0.87rem; }
a[data-option][data-primary] {
  border-color: var(--accent-border);
  background: var(--accent-bg);
}

/* --- Field evidence ---------------------------------------------------- */

[data-current-value] {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1.5rem;
  padding: 1.15rem 1.35rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--card);
  flex-wrap: wrap;
}
[data-current-value][data-deviates] {
  background: var(--warn-bg);
  border-color: var(--warn-border);
}
[data-current-value] [data-label] {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  color: var(--sub);
  text-transform: uppercase;
}
[data-current-value] [data-value] {
  margin: 0.35rem 0 0;
  font-size: 1.6rem;
  font-weight: 700;
}
[data-current-value] [data-baseline] {
  margin: 0.35rem 0 0;
  color: var(--sub);
  font-size: 0.83rem;
}

[data-field-evidence] h2 {
  margin: 0.5rem 0 0.5rem;
  font-size: 0.95rem;
  font-weight: 600;
}

ul[data-evidence-list] {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}
ul[data-evidence-list] > li {
  border: 1px solid var(--border);
  background: var(--card);
  border-radius: var(--radius);
  padding: 1rem 1.15rem 1.05rem;
}
ul[data-evidence-list] > li[data-chosen] {
  border-color: var(--accent-border);
  background: var(--chosen-bg);
}
[data-evidence-header] {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 0.45rem;
}
[data-evidence-header] [data-doc-header] {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
[data-evidence-header] [data-doc-name] {
  margin: 0;
  font-weight: 600;
  font-size: 0.95rem;
}
[data-evidence-header] [data-doc-date] {
  margin: 0;
  font-size: 0.82rem;
  color: var(--sub);
}
[data-claim-value] {
  margin: 0.35rem 0 0.35rem;
  font-weight: 600;
}
[data-claim-notes] {
  margin: 0 0 0.35rem;
  color: var(--sub);
  font-size: 0.85rem;
}
/* The page the claim's passage is on, next to the filename it belongs to. The
   filename link now carries the jump itself, so this is a label rather than a
   second link competing with it. */
[data-evidence-header] [data-doc-page] {
  font-weight: 400;
  font-size: 0.82rem;
  color: var(--sub);
}

[data-claim-ref] {
  margin: 0.5rem 0 0.5rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--sub);
  display: flex;
  align-items: baseline;
  gap: 0.3rem;
  flex-wrap: wrap;
}
[data-claim-ref] code {
  font-family: inherit;
  background: var(--bg);
  padding: 0 0.4rem;
  border-radius: 3px;
  color: var(--ink);
}
[data-claim-ref] [data-source],
[data-claim-ref] [data-confidence] {
  /* Override the global [data-source] block/margin from the entities app —
     inside the claim-ref flex row we want them to sit inline on the same
     baseline as <code>, not push themselves down with a top margin. */
  display: inline;
  margin: 0;
  color: var(--sub);
}

[data-evidence-actions] {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.4rem;
}
[data-evidence-actions] button {
  padding: 0.35rem 0.75rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--card);
  /* Amber at rest, unlike the neutral Reject on the shared claim card. This page
     is the one surface that still offers Reject on an approved value, so the
     button carries the conflict tone standing still rather than only on hover. */
  color: var(--conflict);
  font-family: var(--sans);
  font-size: 0.82rem;
  cursor: pointer;
}
[data-evidence-actions] button:hover {
  border-color: var(--conflict);
  background: var(--conflict-bg);
}

/* --- Mobile tweaks ----------------------------------------------------- */

@media (max-width: 768px) {
  body[data-page="contracts-register"] main,
  [data-customer-detail],
  [data-customer-create],
  [data-customer-upload],
  [data-deviation-chooser],
  [data-deviation-form],
  [data-field-evidence] {
    padding: 0.9rem 0.9rem 2rem;
    gap: 1rem;
  }
  [data-customer-header] { flex-direction: column; align-items: stretch; }
  [data-customer-header] [data-actions] { justify-content: flex-start; }
  table[data-contracts],
  table[data-service-lines],
  table[data-documents] {
    font-size: 0.82rem;
  }
  dl[data-fields],
  dl[data-agreement-terms] { grid-template-columns: 1fr; }
  /* Three columns of contract terms don't survive a phone — stack them, which
     also keeps the struck-through baseline next to the value it replaced. */
  ul[data-deviation-list] li { grid-template-columns: 1fr; }

  [data-review-status-actions] > * { flex: 1 1 auto; }
  label[data-nest-toggle] { white-space: normal; }
}


/* --- Rejected claims + the correction form on the evidence page ---------- */
/* Rejected claims stay in the trail rather than vanishing, so they need to read
   as clearly inactive without becoming unreadable. */

[data-claim-rejected-row],
article[data-claim][data-rejected] {
  opacity: 0.62;
  background: color-mix(in srgb, var(--bg) 60%, var(--card));
}
[data-claim-rejected-row] [data-claim-value] { text-decoration: line-through; }
/* Struck value on the shared claim card, whose value sits in a bare <strong>. */
article[data-claim][data-rejected] [data-claim-header] > strong { text-decoration: line-through; }
/* A rejected claim is evidence, not a target: it never gets the chosen accent,
   and nothing about it should look clickable except its source cue. */
article[data-claim][data-rejected] { border-color: var(--border); cursor: default; }

/* The rejected trail under a field's live claims, on the review page and the
   evidence panel. Set apart by a rule rather than a heading — it is a footnote
   to the field, not a section of its own. */
[data-rejected-trail] {
  margin-top: 0.7rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
[data-rejected-trail-head] {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--sub);
}
/* Superseded approval — the log row no longer describes the field's value.
   Scoped to match the base rule above it so it wins on specificity, not on the
   accident of coming later in the file. Colour only: the tag is display:block,
   so a background would draw a full-width bar. */
[data-field-audit] [data-audit-tag][data-audit-stale] { color: var(--conflict); }
p[data-claim-rejected] {
  margin: 0.25rem 0 0;
  font-size: 0.75rem;
  color: var(--conflict);
}

[data-correct-value] {
  margin-top: 1.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--card);
  padding: 1rem;
}
[data-correct-value] > h2 { margin: 0 0 0.2rem; font-size: 0.95rem; }
[data-correct-value] > p[data-caption] { margin: 0 0 0.8rem; }

/* ── Settings ───────────────────────────────────────────────────────────────
   Sub-tabs under the main tenant nav, and the row-per-setting editors. Each row
   is its own HTMX form that swaps itself, so state (saved / errored) lives on
   the row rather than on the page.
   ------------------------------------------------------------------------- */
body[data-page="settings"] main { max-width: 58rem; }
body[data-page="settings"] main > * { max-width: 100%; }

nav[data-subtabs] {
  display: flex;
  gap: 0.25rem;
  align-items: center;
  border-bottom: 1px solid var(--border);
  padding-bottom: 0.5rem;
  margin-bottom: 0.5rem;
  font-size: 0.85rem;
}
nav[data-subtabs] a {
  color: var(--sub);
  text-decoration: none;
  padding: 0.3rem 0.7rem;
  border-radius: 6px;
  font-weight: 500;
}
nav[data-subtabs] a:hover { color: var(--ink); background: var(--bg); }
nav[data-subtabs] a[aria-current="page"] {
  background: var(--accent-bg);
  color: var(--ink);
  font-weight: 600;
}

[data-settings-grid] {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
[data-settings-row] {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--card);
  padding: 0.6rem 0.75rem;
}
/* Confirmation is a resting tint on the row rather than a page-level message —
   several rows can be saved in a row and each needs its own acknowledgement. */
[data-settings-row][data-saved] {
  border-color: var(--ok-border);
  background: var(--ok-bg);
}
/* flex-direction is explicit: the base `form` rule sets `column`, and inheriting
   it here stacks the whole row. */
/* A two-row grid — captions on row 1, controls on row 2 — rather than a flex row.
   Wrapper elements dissolve with display:contents (the same trick the schema
   editor's [data-field-grid] uses) so every caption and every control is a direct
   grid item. Row 2's track then bottom-aligns the inputs and the Save button
   together by definition, instead of depending on each wrapper's own height. */
[data-settings-row] form {
  display: grid;
  grid-template-columns: minmax(8rem, 1fr);
  grid-auto-columns: max-content;
  grid-auto-flow: column;
  grid-template-rows: auto auto;
  align-items: end;
  gap: 0.15rem 0.75rem;
  margin: 0;
}
[data-settings-row] form > [data-name] { grid-column: 1; grid-row: 1 / 3; }
[data-settings-row] label[data-field],
[data-settings-row] [data-action] { display: contents; }
[data-settings-row] label[data-field] > span,
[data-settings-row] [data-caption-spacer] { grid-row: 1; }
[data-settings-row] label[data-field] > input,
[data-settings-row] [data-action-row] { grid-row: 2; }
[data-settings-row] [data-name] {
  display: flex;
  flex-direction: column;
  min-width: 0;
  align-self: center;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--ink);
}
[data-settings-row] [data-code] {
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 400;
  color: var(--sub);
}
/* Inputs default to width:100%, which in a row makes every field fight for the
   line. Size them to what they hold: a short numeric value and a unit code. */
[data-settings-row] label[data-field] input[type="text"] { width: 7rem; }
[data-settings-row] label[data-field]:has(input[name$="-unit"]) input { width: 4rem; }
[data-settings-row] label[data-field]:has(input[name$="-label"]) input { width: 14rem; }
[data-settings-row] label[data-field] > span {
  font-family: var(--mono-label);
  font-size: 0.62rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted-strong);
}
[data-settings-row] input[type="text"] { padding: 0.35rem 0.55rem; }
/* line-height matched to the inputs' (buttons default to their own font-size),
   so Save ends up exactly as tall as the field beside it and their bottoms meet. */
[data-settings-row] button {
  padding: 0.35rem 0.9rem;
  /* Matched to the inputs' computed font metrics so Save is exactly as tall as
     the field beside it — buttons otherwise default to line-height: normal. */
  font-size: 0.78rem;
  line-height: 1.5;
}
/* Mirrors label[data-field]: an empty caption above the control, so this block is
   the same height as every labelled field on the row. */
[data-settings-row] [data-caption-spacer] {
  font-family: var(--mono-label);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
}
/* Non-breaking space: an empty inline has no line box, which would collapse the
   spacer and reintroduce the height difference it exists to cancel. */
[data-settings-row] [data-caption-spacer]::before { content: "\00a0"; }
[data-settings-row] [data-action-row] {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
/* Always rendered at a fixed width, so a saved row's button doesn't shift out of
   the column its unsaved neighbours line up in. */
[data-settings-row] [data-saved-note] {
  flex: 0 0 3.1rem;
  text-align: left;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--ok-text);
}
[data-settings-row] [data-description] {
  margin: 0.45rem 0 0;
  color: var(--sub);
  line-height: 1.4;
}
[data-settings-row] .errorlist {
  margin: 0.45rem 0 0;
  padding-left: 1rem;
  color: var(--warn-text);
  font-size: 0.78rem;
}

[data-settings-group] { margin-bottom: 0.75rem; }
[data-settings-group] > summary {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  padding: 0.4rem 0;
  list-style: none;
}
[data-settings-group] > summary::-webkit-details-marker { display: none; }
[data-settings-group] > summary::before {
  content: "▸";
  color: var(--sub);
  font-size: 0.7rem;
}
[data-settings-group][open] > summary::before { content: "▾"; }
[data-settings-group] > summary h3 {
  margin: 0;
  font-size: 0.9rem;
  font-weight: 600;
}
/* Pushed to the far edge so the schema link doesn't read as part of the title. */
[data-settings-group] > summary a { margin-left: auto; font-size: 0.78rem; }

/* Mobile: the single-line grid can't shrink below its controls, so the row
   becomes a wrapping flex stack — the wrappers reappear (no display:contents) and
   carry their own caption-over-control layout. */
@media (max-width: 768px) {   /* --bp-compact */
  [data-settings-row] form {
    display: flex;
    flex-flow: row wrap;
    align-items: flex-end;
    gap: 0.5rem 0.75rem;
  }
  [data-settings-row] label[data-field],
  [data-settings-row] [data-action] {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
  }
  [data-settings-row] [data-name] { flex: 1 1 100%; margin-bottom: 0.1rem; }
  [data-settings-row] label[data-field]:has(input[name$='-label']) { flex: 1 1 100%; }
  [data-settings-row] label[data-field]:has(input[name$='-label']) input { width: 100%; }
  [data-settings-row] [data-caption-spacer] { display: none; }
}

/* ---- Schema discovery: build schemas from uploaded documents -------------
   The start screen fits the default 32rem column; the review screen (phase 3)
   will need widening, and gets its own body[data-page] block then. */

/* Two routes to a schema sit side by side, so the row reads as a choice rather
   than as one action with an afterthought. Overrides the space-between default
   because there is no cancel here to push away from. */
[data-schema-actions] { justify-content: flex-start; flex-wrap: wrap; }

/* Standing instructions: collapsed by default so a first-time user isn't taxed
   by a field most people leave empty. Same <details> mechanics as the filter
   panels, but static rather than an overlay — it belongs in the form flow. */
details[data-instructions] {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-quiet);
}
details[data-instructions] > summary {
  padding: 0.5rem 0.75rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
details[data-instructions] > summary::-webkit-details-marker { display: none; }
/* The disclosure triangle is drawn rather than inherited, so it can rotate and
   so it sits at a size that matches the label. */
details[data-instructions] > summary::before {
  content: "";
  width: 0;
  height: 0;
  border-left: 4px solid var(--sub);
  border-top: 3.5px solid transparent;
  border-bottom: 3.5px solid transparent;
  transition: transform 0.12s ease;
}
details[data-instructions][open] > summary::before { transform: rotate(90deg); }
details[data-instructions] > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
details[data-instructions] > summary [data-optional] {
  font-weight: 400;
  color: var(--sub);
}
[data-instructions-panel] { padding: 0 0.75rem 0.65rem; }
[data-instructions-panel] textarea { width: 100%; }
[data-instructions-panel] p[data-caption] { margin: 0.35rem 0 0; }

/* Blueprint progress list. ul[data-docs] already styles the rows; the page only
   needs its stage pills compacted the way the entity upload list does. */
body[data-page="discovery-detail"] ul[data-docs] li [data-pill] { font-size: 0.68rem; }

/* ---- Schema discovery: the review screen --------------------------------
   The field table needs far more than the default 32rem child cap — the
   description column alone is the widest thing in the app, because a
   description IS the extraction prompt and hiding it behind a tooltip would
   hide the most important field on the page. */
body[data-page="discovery-review"] main,
body[data-page="discovery-record"] main { max-width: min(96rem, 96vw); }
body[data-page="discovery-review"] main > *,
body[data-page="discovery-record"] main > * { max-width: 100%; }

body[data-page="discovery-review"] h2,
body[data-page="discovery-record"] h2 { margin: 1.5rem 0 0.2rem; }

/* The record is read top to bottom, so its action bar sits in the flow. Pinned,
   it covered the last sections instead of keeping a submit within reach. */
body[data-page="discovery-record"] [data-proposal-bar] { position: static; }
[data-section-head] { margin-top: 1.75rem; }

/* Vertical choice — two cards, the chosen one accented. Radios rather than a
   select because the trade-off between them is the content, not the label. */
[data-vertical-options] { display: flex; flex-wrap: wrap; gap: 0.6rem; }
[data-vertical-option] {
  flex: 1 1 18rem;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.15rem 0.5rem;
  align-items: start;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg);
  padding: 0.7rem 0.85rem;
  cursor: pointer;
}
[data-vertical-option][data-chosen] {
  border-color: var(--accent);
  background: var(--accent-bg);
}
[data-vertical-option] input { grid-row: span 2; margin-top: 0.15rem; }
[data-vertical-option] strong { font-size: 0.9rem; }
[data-vertical-option] small { color: var(--sub); line-height: 1.4; }

/* One card per proposed entity type. Children indent and draw a connector, so
   the hierarchy — the thing hardest to get right and most costly to get wrong —
   is visible without reading the parent select on every card. */
[data-proposal-type] {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-bottom: 0.75rem;
}
[data-proposal-type][data-child] { margin-left: 1.6rem; position: relative; }
[data-proposal-type][data-child]::before {
  content: "";
  position: absolute;
  left: -0.9rem;
  top: -0.85rem;
  bottom: 50%;
  width: 0.8rem;
  border-left: 1.5px solid var(--border-strong);
  border-bottom: 1.5px solid var(--border-strong);
  border-bottom-left-radius: 7px;
  pointer-events: none;
}
/* An excluded type stays legible rather than being hidden: unticking is a
   decision the user has to be able to see and undo. */
[data-proposal-type][data-excluded] { opacity: 0.55; }

[data-proposal-head] {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  background: var(--surface-quiet);
  border-bottom: 1px solid var(--border);
  padding: 0.6rem 0.8rem;
}
[data-proposal-head] input[data-type-name] {
  width: auto;
  flex: 0 1 14rem;
  font-weight: 700;
  padding: 0.25rem 0.45rem;
}
[data-parent-picker] {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.75rem;
  color: var(--sub);
  margin: 0;
}
[data-parent-picker] select { width: auto; min-width: 9rem; padding: 0.2rem 1.6rem 0.2rem 0.4rem; }
/* ``fold in`` sits beside ``remove``; the pair is pushed right together, so
   only the first of them takes the auto margin. */
/* Structural actions on a proposed type. They are real buttons, not icons, so
   they take the [data-secondary] outline and only shrink to fit the card header
   — [data-icon] is for a glyph with no label, like the field row's ✕. */
[data-proposal-head] button[data-fold-type],
[data-proposal-head] button[data-remove-type] {
  padding: calc(0.32rem - 1px) calc(0.6rem - 1px);
  font-size: 0.78rem;
  font-weight: 500;
}
/* Removing is the destructive one, and it says so the way the rest of the app
   does: the plain outline until hover, then conflict amber. See the note on
   article[data-claim] [data-reject] — there is no danger variant by design. */
[data-proposal-head] button[data-remove-type]:hover {
  border-color: var(--conflict);
  background: var(--conflict-bg);
  color: var(--conflict);
}
[data-proposal-head] button[data-fold-type] { margin-left: auto; }
[data-proposal-head] button[data-fold-type] + button[data-remove-type],
[data-proposal-head] button[data-remove-type] { margin-left: auto; }
[data-proposal-head] button[data-fold-type] ~ button[data-remove-type] { margin-left: 0; }
[data-proposal-why] {
  flex-basis: 100%;
  margin: 0;
  font-size: 0.78rem;
  color: var(--sub);
}
[data-existing-fields] {
  margin: 0;
  padding: 0.45rem 0.8rem;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  color: var(--sub);
}
[data-existing-fields] code { font-size: 0.72rem; margin-right: 0.3rem; }

[data-proposal-scroll] { overflow-x: auto; }
table[data-proposal-fields] {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.8rem;
}
table[data-proposal-fields] th {
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--sub);
  text-align: left;
  padding: 0.45rem 0.55rem;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
table[data-proposal-fields] td {
  padding: 0.4rem 0.55rem;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
table[data-proposal-fields] tr:last-child td { border-bottom: 0; }
/* The parent link is real but not editable here, so it reads as furniture. */
table[data-proposal-fields] tr[data-structural] td {
  background: var(--surface-quiet);
  color: var(--sub);
}
table[data-proposal-fields] tr[data-user-added] td { background: #f7faf8; }
table[data-proposal-fields] input[type="text"],
table[data-proposal-fields] select,
table[data-proposal-fields] textarea {
  padding: 0.25rem 0.4rem;
  font-size: 0.78rem;
  min-width: 0;
}
table[data-proposal-fields] [data-col-name] { min-width: 11rem; }
/* Labels carry units the documents print — "Asuinpinta-ala (m²)",
   "Hoitovastike (€/kk)" — and truncating them loses exactly the part that
   distinguishes one fee from another. */
table[data-proposal-fields] th:nth-child(3),
table[data-proposal-fields] td:nth-child(3) { min-width: 10rem; }
table[data-proposal-fields] [data-col-name] code { font-family: var(--mono); font-size: 0.75rem; }
table[data-proposal-fields] [data-col-type] { min-width: 7.5rem; }
table[data-proposal-fields] [data-col-type] select { padding-right: 1.6rem; }
/* The widest column on purpose: this text is the extraction prompt for every
   document the org will ever upload. */
table[data-proposal-fields] [data-col-desc] { min-width: 22rem; }
table[data-proposal-fields] [data-col-desc] textarea { width: 100%; resize: vertical; }
table[data-proposal-fields] [data-col-include],
table[data-proposal-fields] [data-col-star],
table[data-proposal-fields] [data-col-actions] { width: 1%; text-align: center; }
table[data-proposal-fields] [data-col-evidence] { white-space: nowrap; }
table[data-proposal-fields] small[data-caption] {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.68rem;
  line-height: 1.35;
}
table[data-proposal-fields] small[data-confusable] { color: var(--conflict); }

[data-edited] {
  display: inline-block;
  margin-top: 0.15rem;
  font-family: var(--mono);
  font-size: 0.6rem;
  color: var(--accent);
  background: var(--accent-bg);
  border: 1px solid var(--accent-border);
  border-radius: 4px;
  padding: 0 0.25rem;
}

[data-proposal-add] {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  padding: 0.5rem 0.8rem;
  border-top: 1px dashed var(--border-strong);
  background: var(--surface-quiet);
}
[data-proposal-add][data-add-type] {
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-lg);
  margin-bottom: 0.75rem;
}

[data-kind-list] { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.3rem; }
[data-kind-list] li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.35rem 0.6rem;
  font-size: 0.82rem;
}
/* Position, not an input: precedence is per record type and is edited under Rules,
   so this states the proposed order and reuses that editor's own rank badge. */
[data-kind-list] [data-rank-num] { flex: none; }
[data-kind-list] small[data-caption] { flex-basis: 100%; margin: 0; font-size: 0.7rem; }

[data-proposal-routes] label[data-route] {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  flex-wrap: wrap;
  font-size: 0.82rem;
  padding: 0.3rem 0;
}
[data-proposal-routes] code,
[data-route] code {
  font-family: var(--mono);
  font-size: 0.74rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.05rem 0.3rem;
}

[data-proposal-instructions] textarea { width: 100%; }

/* The bar is sticky, so the form has to reserve its height — otherwise it
   parks on top of the last card's rows and hides them. */
form[data-proposal-form] { padding-bottom: 4.5rem; }
[data-proposal-bar] {
  position: sticky;
  bottom: 0.5rem;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  padding: 0.6rem 0.8rem;
  margin-top: 1.25rem;
  box-shadow: 0 -2px 10px rgb(27 30 36 / 0.06);
}
[data-proposal-bar] > [data-primary-slot] { margin-left: auto; }
[data-proposal-bar] small[data-caption] { flex: 1 1 12rem; margin: 0; font-size: 0.7rem; }

[data-revert-form] { margin-top: 0.5rem; }

@media (max-width: 768px) {
  [data-proposal-type][data-child] { margin-left: 0.75rem; }
  [data-proposal-type][data-child]::before { left: -0.5rem; width: 0.45rem; }
  [data-proposal-head] input[data-type-name] { flex-basis: 100%; }
  [data-proposal-head] button[data-remove-type],
  [data-proposal-head] button[data-fold-type] { margin-left: 0; }
  [data-proposal-bar] { position: static; }
  form[data-proposal-form] { padding-bottom: 0; }
}

/* Blocking notice above the apply bar. Tells the user what to fix instead of
   letting them press a button that then refuses — the same reasons preflight()
   would raise, surfaced before the submit. */
[data-proposal-problems] {
  border: 1px solid var(--conflict-border);
  border-left: 3px solid var(--conflict);
  background: var(--conflict-bg);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 0.6rem 0.85rem;
  margin-top: 1rem;
  color: var(--conflict);
  font-size: 0.82rem;
}
[data-proposal-problems] p { margin: 0 0 0.3rem; }
[data-proposal-problems] ul { margin: 0; padding-left: 1.1rem; }
[data-proposal-problems] code { font-family: var(--mono); font-size: 0.75rem; }

[data-create-entity] {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin: 0;
  font-size: 0.78rem;
  color: var(--sub);
}

/* In-flight document batches, listed above the schemas they become. Without
   these a blueprint is only reachable by its own URL — start a batch, navigate
   away, and it is gone. */
[data-blueprints-head] { align-self: flex-start; margin-bottom: 0.3rem; }
ul[data-cards][data-blueprints] { margin-bottom: 1.25rem; }
ul[data-cards][data-blueprints] li > a { gap: 0.5rem; }

/* The applied proposal, kept read-only. Shares the editable view's card and
   table markup, so only the cells that were inputs need restating as text —
   the columns stay in the same places, which is what makes the record
   recognisable as the thing that was reviewed. */
[data-proposal-readonly] table[data-proposal-fields] td { padding: 0.5rem 0.55rem; }
[data-proposal-readonly] [data-col-star],
[data-proposal-readonly] [data-col-include] { color: var(--conflict); }
[data-proposal-readonly] [data-col-include] { color: var(--approved); }
[data-type-name-static] { font-size: 0.95rem; }
[data-open-schema] {
  margin-left: auto;
  font-size: 0.75rem;
  white-space: nowrap;
}
[data-rank-static] {
  font-family: var(--mono);
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--sub);
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  padding: 0.05rem 0.35rem;
}
[data-instructions-record] {
  margin: 0;
  padding: 0.6rem 0.85rem;
  background: var(--surface-quiet);
  border-left: 3px solid var(--border-strong);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 0.85rem;
  color: var(--ink);
}
[data-applied-docs] { margin-top: 1.25rem; }
/* The applied bar holds a form, so it needs the form's own margin cancelled to
   sit on one line with the caption beside it. */
[data-proposal-bar] form { margin: 0; }

/* Advice, not a blocker: the proposal will apply, but something about it will
   disappoint later — a child type no rule will ever fill, say. Distinct from
   [data-proposal-problems], which prevents the write. */
[data-proposal-warnings] {
  border: 1px solid var(--accent-border);
  border-left: 3px solid var(--accent);
  background: var(--accent-soft, var(--accent-bg));
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 0.6rem 0.85rem;
  margin-top: 1rem;
  color: var(--ink);
  font-size: 0.82rem;
}
[data-proposal-warnings] p { margin: 0 0 0.3rem; }
[data-proposal-warnings] ul { margin: 0; padding-left: 1.1rem; }

/* What applying the proposal creates. Sits directly above the sticky apply bar,
   so it is the last thing read before the button. Collapsed by default: the
   one-line count answers it most of the time. */
[data-apply-preview] {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-top: 1rem;
  font-size: 0.85rem;
}
[data-apply-preview] > summary {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
  padding: 0.6rem 0.8rem;
  cursor: pointer;
  list-style: none;
}
[data-apply-preview] > summary::-webkit-details-marker { display: none; }
/* Own marker, so it sits in the flex row rather than being laid out as a list
   bullet — the same reason the service-agreement cards draw their own. */
[data-apply-preview] > summary::before {
  content: "\25B8";
  color: var(--sub);
  font-size: 0.7rem;
}
[data-apply-preview][open] > summary::before { content: "\25BE"; }
[data-apply-preview] > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
[data-apply-preview] > [data-table-scroll] {
  border-top: 1px solid var(--border);
  padding: 0 0.8rem;
}
table[data-creates] {
  width: 100%;
  border-collapse: collapse;
}
table[data-creates] th,
table[data-creates] td {
  text-align: left;
  padding: 0.45rem 0.6rem 0.45rem 0;
  vertical-align: top;
  font-weight: 400;
}
table[data-creates] thead th {
  font-family: var(--mono-label);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--sub);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
  padding-top: 0.55rem;
}
table[data-creates] tbody th {
  white-space: nowrap;
  font-weight: 500;
}
table[data-creates] tbody tr + tr th,
table[data-creates] tbody tr + tr td { border-top: 1px solid var(--border); }
table[data-creates] [data-from] { color: var(--sub); }
/* Box-drawing needs the mono face and its own spaces preserved. */
table[data-creates] [data-tree] {
  font-family: var(--mono);
  color: var(--border-strong);
  white-space: pre;
}
[data-apply-preview] > [data-caption] {
  margin: 0;
  padding: 0.5rem 0.8rem 0.65rem;
  color: var(--sub);
  font-size: 0.78rem;
}
/* The root clause tells the truth about the checkbox below it: untick "also read
   these documents into a first record" and no root entity is created, so
   "Creates 1 Client now" would be a lie. Driven off the checkbox's own state, no
   JavaScript — the same trick [data-price-toggle] uses to open a price trace. */
[data-creates-later] { display: none; }
form:has([name="create_entity"]:not(:checked)) [data-creates-now] { display: none; }
form:has([name="create_entity"]:not(:checked)) [data-creates-later] { display: inline; }

/* Where a schema's field wording came from. Quiet by design — it matters only
   when a value looks wrong, and then it is the only thing that still knows
   which document and element the description was written from. */
[data-generated-from] {
  align-self: flex-start;
  margin: 0 0 0.5rem;
  font-size: 0.78rem;
  color: var(--sub);
}

/* Applied batches: history rather than a queue, so collapsed by default. */
details[data-applied-history] { margin-top: 1rem; }
details[data-applied-history] > summary {
  font-size: 0.8rem;
  color: var(--sub);
  cursor: pointer;
  padding: 0.3rem 0;
}
details[data-applied-history] > summary:hover { color: var(--ink); }
details[data-applied-history] > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
details[data-applied-history] ul[data-cards] { margin-top: 0.4rem; }

/* Which part a type plays in the contracts vertical. Sits beside the parent
   picker because both answer structural questions about the type rather than
   about its fields. */
[data-role-picker] {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.75rem;
  color: var(--sub);
  margin: 0;
}
[data-role-picker] select {
  width: auto;
  min-width: 11rem;
  padding: 0.2rem 1.6rem 0.2rem 0.4rem;
}
