/* CPPLC — screen styles.
   Colour roles come from a pre-validated palette; charts reference the role
   variables (--series-N, --ink-*) rather than raw hex. */

:root {
  color-scheme: light;

  --surface:        #fcfcfb;
  --plane:          #f9f9f7;
  --raised:         #ffffff;
  --ink:            #0b0b0b;
  --ink-2:          #52514e;
  --ink-muted:      #898781;
  --grid:           #e1e0d9;
  --baseline:       #c3c2b7;
  --hairline:       rgba(11, 11, 11, 0.10);
  --shadow:         0 1px 2px rgba(11,11,11,.06), 0 8px 24px rgba(11,11,11,.06);

  --series-1:       #2a78d6;
  --series-2:       #eb6834;
  --series-3:       #1baf7a;
  --accent:         #2a78d6;
  --accent-ink:     #ffffff;
  --good:           #0ca30c;
  --warning:        #fab219;
  --critical:       #d03b3b;
  --wash:           rgba(42, 120, 214, 0.08);

  --r-sm: 6px;
  --r-md: 10px;
  --r-lg: 14px;

  /* Both faces come from Settings; without either, the built-in stack applies.
     The screen is stacked the same way as the printed sheets — Latin first,
     Khmer behind it — because a font stack resolves per character: a Latin face
     has no Khmer glyphs, so it takes the roman letters and digits and every
     Khmer character falls through to the Khmer face. That is what makes the
     "CPPLC" in the title and the Khmer beside it obey the two settings
     separately. The Settings hint promises this applies on screen as well as on
     paper; until now only --font-khmer did, and the Latin choice stopped at the
     printer. */
  --font-ui: var(--font-latin, system-ui), var(--font-khmer, "Khmer OS Battambang"),
             "Khmer OS", "Noto Sans Khmer", "Khmer Sangam MN", "Khmer MN",
             "Khmer UI", Hanuman, Battambang,
             system-ui, -apple-system, "Segoe UI", sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --surface:    #1a1a19;
    --plane:      #0d0d0d;
    --raised:     #222221;
    --ink:        #ffffff;
    --ink-2:      #c3c2b7;
    --ink-muted:  #898781;
    --grid:       #2c2c2a;
    --baseline:   #383835;
    --hairline:   rgba(255, 255, 255, 0.10);
    --shadow:     0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35);
    --series-1:   #3987e5;
    --series-2:   #d95926;
    --series-3:   #199e70;
    --accent:     #3987e5;
    --wash:       rgba(57, 135, 229, 0.14);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --surface:    #1a1a19;
  --plane:      #0d0d0d;
  --raised:     #222221;
  --ink:        #ffffff;
  --ink-2:      #c3c2b7;
  --ink-muted:  #898781;
  --grid:       #2c2c2a;
  --baseline:   #383835;
  --hairline:   rgba(255, 255, 255, 0.10);
  --shadow:     0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35);
  --series-1:   #3987e5;
  --series-2:   #d95926;
  --series-3:   #199e70;
  --accent:     #3987e5;
  --wash:       rgba(57, 135, 229, 0.14);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--plane);
  color: var(--ink);
  font-family: var(--font-ui);
  /* Khmer stacks vowels above and coeng below the baseline — tight leading clips them. */
  line-height: 1.75;
  -webkit-font-smoothing: antialiased;
}

body { min-height: 100vh; }

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

.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 10px 20px;
  background: var(--surface);
  border-bottom: 1px solid var(--hairline);
}

.brand { display: flex; align-items: center; gap: 10px; min-width: 0; }

.brand-mark {
  width: 34px; height: 34px;
  flex: none;
  background-image: url("../assets/emblem.png");
  background-repeat: no-repeat;
  background-size: contain;
  background-position: center;
}
.brand-mark.custom { background-size: cover; border-radius: 50%; }

.brand-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.brand-text strong { font-size: 16px; letter-spacing: .04em; }
.brand-text span { font-size: 12px; color: var(--ink-2); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }

.nav { display: flex; gap: 2px; margin-left: auto; flex-wrap: wrap; }

.nav-btn {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--ink-2);
  font: inherit;
  font-size: 14px;
  padding: 7px 14px;
  border-radius: var(--r-sm);
  cursor: pointer;
  white-space: nowrap;
}
.nav-btn:hover { background: var(--wash); color: var(--ink); }
.nav-btn.active { background: var(--accent); color: var(--accent-ink); }

.topbar-actions { display: flex; gap: 6px; margin-left: 8px; }

.icon-btn {
  appearance: none;
  border: 1px solid var(--hairline);
  background: var(--raised);
  color: var(--ink-2);
  font: inherit;
  font-size: 13px;
  min-width: 38px;
  height: 34px;
  padding: 0 10px;
  border-radius: var(--r-sm);
  cursor: pointer;
}
.icon-btn:hover { color: var(--ink); border-color: var(--baseline); }

/* ---------- layout ---------- */

/* Full width, matching the topbar above it, which was never capped. The 1480px
   cap that used to be here left a wide margin down both sides on the office
   monitors this runs on, while the member table — twelve columns and a photo —
   was the thing being squeezed. */
main { padding: 24px 20px 64px; }

.view-head {
  display: flex;
  align-items: flex-end;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 20px;
}
.view-head h1 { margin: 0; font-size: 22px; font-weight: 600; }
.view-head .sub { color: var(--ink-2); font-size: 13px; margin-top: 2px; }
.view-head .spacer { margin-left: auto; }

.card {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  padding: 18px;
}
.card + .card { margin-top: 16px; }
.card h2 { margin: 0 0 4px; font-size: 15px; font-weight: 600; }
.card .card-sub { margin: 0 0 16px; font-size: 12px; color: var(--ink-muted); }

/* align-items: start — each card is as tall as its own contents. Grid items
   stretch to the row height by default, so one long card left the three beside
   it padded out with empty space to match it. */
.grid { display: grid; gap: 16px; align-items: start; }
/* .card + .card above adds a top margin for the stacked layout, where cards
   follow one another down the page. Inside a grid the gap already spaces them,
   and that margin applied to every card except the first — so the first card in
   a row sat 16px higher than the three beside it. */
.grid > .card { margin-top: 0; }
.grid.cols-2 { grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }
.grid.cols-3 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }

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

.btn {
  appearance: none;
  font: inherit;
  font-size: 14px;
  border-radius: var(--r-sm);
  border: 1px solid var(--hairline);
  background: var(--raised);
  color: var(--ink);
  padding: 8px 15px;
  cursor: pointer;
  white-space: nowrap;
}
.btn:hover { border-color: var(--baseline); }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.btn.primary:hover { filter: brightness(1.07); }
.btn.danger { color: var(--critical); }
.btn.danger:hover { border-color: var(--critical); }
.btn.sm { padding: 5px 10px; font-size: 13px; }
.btn:disabled { opacity: .45; cursor: not-allowed; }

.btn-row { display: flex; gap: 8px; flex-wrap: wrap; }

/* ---------- inputs ---------- */

.field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.field > label { font-size: 12px; color: var(--ink-2); }

/* Global, not scoped to .field — hints are used on their own in the Settings
   sections too, where a descendant-only rule left them at full body size. */
.hint { font-size: 11px; line-height: 1.7; color: var(--ink-muted); }

input[type="text"], input[type="date"], input[type="number"],
input[type="search"], select, textarea {
  font: inherit;
  font-size: 14px;
  color: var(--ink);
  background: var(--raised);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  padding: 8px 10px;
  width: 100%;
  min-width: 0;
}
input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
  border-color: transparent;
}

.toolbar {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: center;
  margin-bottom: 14px;
}
.toolbar .grow { flex: 1 1 240px; }
.toolbar select { width: auto; min-width: 130px; }

/* A hint that is a warning rather than an aside. */
.hint.warn-text { color: var(--warning); }

/* The backup banner carries an action, so it lays out as a row rather than a
   paragraph — the button is the point of it. */
.banner.warn {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  border-color: var(--warning);
}
.banner.warn span { flex: 1 1 auto; }

/* ---------- swapping two photos ---------- */

/* Both faces side by side with the exchange arrow between them: the check that
   matters here is "are these two the right way round", and that is a visual
   question, not a textual one. */
.swap-pair {
  display: flex; align-items: center; justify-content: center;
  gap: 16px; margin-top: 14px;
}
.swap-side { text-align: center; min-width: 0; }
.swap-side .preview { margin: 0 auto 6px; }
.swap-side .hint { display: block; }
.swap-arrow { font-size: 22px; color: var(--ink-2); flex: none; }

.io-issue-row { display: flex; align-items: center; gap: 8px; }
.io-issue-row span { flex: 1 1 auto; min-width: 0; word-break: break-word; }

/* ---------- settings ---------- */

/* Two columns of cards, packed rather than gridded.

   A grid puts one card per cell, so every row is as tall as its tallest card and
   the short one beside it leaves a hole — a dozen settings cards of a dozen
   different heights came out as a ragged ladder. CSS columns flow instead: each
   card takes only the height it needs and the next one starts directly beneath,
   so both columns are solid and end level.

   break-inside avoids splitting a card across the column boundary;
   inline-block + width:100% is what makes that hold in every engine. */
.settings-grid { column-count: 1; column-gap: 16px; }
@media (min-width: 1000px) { .settings-grid { column-count: 2; } }
.settings-grid > .card {
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
  display: inline-block;
  width: 100%;
  /* .card + .card carries a top margin for the stacked layout; here the bottom
     margin does the spacing, and both together would double it. */
  margin: 0 0 16px;
}

/* Settings cards are read one after another rather than compared side by side,
   so their headings carry a rule to separate one from the next cleanly. */
.settings-grid > .card > h2 {
  padding-bottom: 10px;
  border-bottom: 1px solid var(--hairline);
  margin-bottom: 12px;
}
.settings-grid > .card > h2 + .card-sub { margin-top: -6px; }

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

/* auto-fill, so the number across follows the window rather than a fixed count:
   the same page of 12 is three rows of four on an office monitor and six rows
   of two on a laptop, without a breakpoint per size. */
.member-grid {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}

.member-card {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.member-card.selected { border-color: var(--accent); background: var(--wash); }

.member-card .mc-top { display: flex; align-items: flex-start; gap: 8px; }
.member-card .mc-no {
  font-size: 11px; color: var(--ink-muted);
  font-variant-numeric: tabular-nums; min-width: 2ch;
}
.member-card .mc-id { flex: 1 1 auto; min-width: 0; }
.member-card .mc-name { font-size: 14px; font-weight: 600; }
/* The identity line wraps rather than being clipped: a name plus its village,
   group and household is the one thing on the card that must stay readable. */
.member-card .mc-sub { font-size: 11px; color: var(--ink-2); margin-top: 2px; }
.member-card .mc-top .row-actions { flex: 0 0 auto; }

/* Two columns of label-over-value. dt/dd rather than a table: these are field
   names and their values, and a card is not a grid of aligned columns. */
.member-card .mc-fields {
  margin: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 10px;
  font-size: 12px;
}
.member-card .mc-field { min-width: 0; }
.member-card .mc-field dt { color: var(--ink-muted); font-size: 10px; }
.member-card .mc-field dd {
  margin: 0; color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.member-card .mc-field dd.num { font-variant-numeric: tabular-nums; }

/* The cards have no header row to hang a select-all off, so it sits above. */
.grid-selectall {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; color: var(--ink-2); margin-bottom: 10px; cursor: pointer;
}

/* Segmented control: two buttons reading as one, the active half filled in. */
.seg { display: inline-flex; }
.seg .btn { border-radius: 0; }
.seg .btn:first-child { border-radius: var(--r-sm) 0 0 var(--r-sm); }
.seg .btn:last-child { border-radius: 0 var(--r-sm) var(--r-sm) 0; margin-left: -1px; }
.seg .btn.on {
  background: var(--wash); border-color: var(--accent); color: var(--accent); z-index: 1;
}

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

.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: var(--surface);
}

table.data {
  border-collapse: collapse;
  width: 100%;
  font-size: 13px;
  white-space: nowrap;
}
table.data th, table.data td {
  padding: 9px 12px;
  text-align: left;
  border-bottom: 1px solid var(--grid);
}
table.data thead th {
  position: sticky;
  top: 0;
  background: var(--surface);
  font-weight: 600;
  font-size: 12px;
  color: var(--ink-2);
  border-bottom: 1px solid var(--baseline);
  z-index: 1;
}
table.data tbody tr:hover { background: var(--wash); }
table.data tbody tr:last-child td { border-bottom: 0; }
table.data td.num { font-variant-numeric: tabular-nums; }
table.data .row-actions { display: flex; gap: 6px; }

.pill {
  display: inline-block;
  font-size: 11px;
  padding: 1px 8px;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  color: var(--ink-2);
  background: var(--plane);
}
.pill.head { border-color: var(--accent); color: var(--accent); background: var(--wash); }
.pill.warn { border-color: var(--warning); color: var(--warning); }

.thumb {
  width: 30px; height: 40px;
  object-fit: cover;
  border-radius: 3px;
  border: 1px solid var(--hairline);
  background: var(--plane);
  display: block;
}
.thumb.empty {
  display: grid; place-items: center;
  font-size: 9px; color: var(--ink-muted);
}

/* ---------- stat tiles ---------- */

.tiles { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }

.tile {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  padding: 14px 16px;
}
.tile .tile-label { font-size: 12px; color: var(--ink-2); }
.tile .tile-value {
  font-size: 30px;
  line-height: 1.25;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.tile .tile-note { font-size: 11px; color: var(--ink-muted); }

/* ---------- charts ---------- */

.chart { display: flex; flex-direction: column; gap: 9px; }

.bar-row {
  display: grid;
  grid-template-columns: minmax(58px, auto) 1fr auto;
  align-items: center;
  gap: 10px;
  font-size: 12px;
}
/* Capped so a long row label cannot starve the bar it belongs to: the label
   column sizes to its content, and household labels now carry village, group
   and number. The full text stays available in the row's title tooltip. */
.bar-row .bar-label {
  color: var(--ink-2); text-align: right; white-space: nowrap;
  max-width: var(--bar-label-max, 26ch); overflow: hidden; text-overflow: ellipsis;
}
.bar-row .bar-track {
  position: relative;
  height: 14px;
  background: var(--grid);
  border-radius: 4px;
  overflow: hidden;
}
.bar-row .bar-fill {
  /* Must be block: the track's children are not grid items, so an inline span
     would ignore width/height and the fill would never paint. */
  display: block;
  height: 100%;
  border-radius: 4px;      /* 4px rounded data-end, anchored to the baseline */
  background: var(--series-1);
  min-width: 3px;
  transition: width .25s ease;
}
.bar-row .bar-fill.s2 { background: var(--series-2); }
.bar-row .bar-value {
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  min-width: 2ch;
  text-align: right;
}
.bar-row:hover .bar-label, .bar-row:hover .bar-value { color: var(--ink); }

.legend { display: flex; gap: 16px; flex-wrap: wrap; font-size: 12px; color: var(--ink-2); margin-top: 4px; }
.legend .key { display: inline-flex; align-items: center; gap: 6px; }
.legend .swatch { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }

.split-bar {
  display: flex;
  height: 20px;
  border-radius: 5px;
  overflow: hidden;
  gap: 2px;                /* 2px surface gap between adjacent fills */
  background: var(--surface);
}
.split-bar > span { display: block; }

/* ---------- misc ---------- */

.empty-state {
  text-align: center;
  padding: 56px 20px;
  color: var(--ink-2);
}
.empty-state .big { font-size: 17px; color: var(--ink); margin-bottom: 6px; }
.empty-state .small { font-size: 13px; color: var(--ink-muted); max-width: 460px; margin: 0 auto 18px; }

.banner {
  border: 1px solid var(--warning);
  background: color-mix(in srgb, var(--warning) 12%, transparent);
  color: var(--ink);
  border-radius: var(--r-md);
  padding: 10px 14px;
  font-size: 13px;
  margin-bottom: 16px;
}

.toast {
  position: fixed;
  left: 50%;
  bottom: 28px;
  transform: translateX(-50%) translateY(20px);
  background: var(--ink);
  color: var(--surface);
  padding: 10px 18px;
  border-radius: 999px;
  font-size: 13px;
  opacity: 0;
  pointer-events: none;
  transition: opacity .2s, transform .2s;
  z-index: 90;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ---------- households ---------- */

.house-grid { display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }

.house-card {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* Wraps: the cards are 300px at their narrowest and the row now carries the
   village as well as the group, and a village name can be long. Without this a
   third item would squash the pills rather than move to a second line. */
.house-card .house-top { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.house-card .house-no { font-size: 16px; font-weight: 600; }
.house-card .house-top .pill { white-space: nowrap; }
.house-card .house-meta { font-size: 12px; color: var(--ink-muted); margin-left: auto; }
.house-card .house-head { font-size: 13px; color: var(--ink-2); }
.house-card .house-head strong { color: var(--ink); font-weight: 600; }
.house-card ul { margin: 0; padding-left: 18px; font-size: 13px; color: var(--ink-2); }
.house-card ul li { line-height: 1.6; }
.house-card .faces { display: flex; gap: 4px; flex-wrap: wrap; }
.house-card .faces img, .house-card .faces .ph {
  width: 27px; height: 36px; border-radius: 3px; object-fit: cover;
  border: 1px solid var(--hairline); background: var(--plane);
}

/* ---------- import / export reports ---------- */

.io-summary {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-bottom: 18px;
}
.io-stat {
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.io-stat strong { font-size: 24px; line-height: 1.2; font-weight: 600; }
.io-stat span { font-size: 12px; color: var(--ink-2); }
.io-stat.ok   strong { color: var(--good); }
.io-stat.warn strong { color: var(--warning); }
.io-stat.bad  strong { color: var(--critical); }

.io-issues {
  border-top: 1px solid var(--hairline);
  padding-top: 12px;
  margin-top: 12px;
}
.io-issue-title { font-size: 12px; font-weight: 600; color: var(--ink-2); margin-bottom: 6px; }
.io-issues ul { margin: 0; padding-left: 18px; }
.io-issues li {
  font-size: 12px;
  color: var(--ink-2);
  line-height: 1.7;
  overflow-wrap: anywhere;
}

.io-progress { display: grid; gap: 10px; padding: 10px 0 4px; }
.io-progress-label { font-size: 13px; color: var(--ink-2); }
.io-progress .bar-track {
  position: relative;
  height: 14px;
  background: var(--grid);
  border-radius: 4px;
  overflow: hidden;
}
.io-progress .bar-fill {
  display: block;
  height: 100%;
  border-radius: 4px;
  background: var(--series-1);
  transition: width .15s linear;
}

/* ---------- about ---------- */

.about-head { display: flex; align-items: center; gap: 16px; margin: 4px 0 14px; }

.about-mark {
  width: 56px; height: 56px; flex: none;
  background: url("../assets/icon-128.png") center/contain no-repeat;
}

.about-name { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.about-name strong { font-size: 20px; letter-spacing: .04em; }

.about-version {
  font-size: 12px;
  color: var(--ink-2);
  background: var(--plane);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  padding: 1px 9px;
  font-variant-numeric: tabular-nums;
}

.about-tagline { font-size: 13px; color: var(--ink-2); }

.about-note {
  margin: 0 0 18px;
  font-size: 13px;
  color: var(--ink-muted);
  max-width: 62ch;
}

.about-block {
  border-top: 1px solid var(--hairline);
  padding-top: 16px;
  display: grid;
  gap: 8px;
  max-width: 460px;
}

.about-heading {
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.about-dev { font-size: 16px; font-weight: 600; margin-bottom: 4px; }

.about-row {
  display: grid;
  grid-template-columns: 88px 1fr auto;
  align-items: center;
  gap: 10px;
  font-size: 13px;
}
.about-row .about-label { color: var(--ink-2); }
.about-row .about-value {
  color: var(--ink);
  text-decoration: none;
  overflow-wrap: anywhere;
}
/* Must out-specify `.about-row .about-value` above, or the links render as
   plain text and stop looking tappable. */
.about-row a.about-value { color: var(--accent); }
.about-row a.about-value:hover { text-decoration: underline; }

.about-copy {
  opacity: 0;
  padding: 2px 8px;
  line-height: 1.4;
  transition: opacity .15s;
}
.about-row:hover .about-copy,
.about-copy:focus-visible { opacity: 1; }

@media (max-width: 520px) {
  .about-row { grid-template-columns: 76px 1fr auto; }
  .about-copy { opacity: 1; }
}

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

.modal-host { position: fixed; inset: 0; z-index: 80; display: grid; place-items: center; padding: 20px; }
/* `display: grid` above outranks the UA stylesheet's [hidden] { display: none },
   so the closed modal must hide itself explicitly. */
.modal-host[hidden] { display: none; }
.modal-backdrop { position: absolute; inset: 0; background: rgba(11,11,11,.5); }
.modal {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  width: min(880px, 100%);
  max-height: 88vh;
  overflow: auto;
  padding: 22px;
}
.modal h2 { margin: 0 0 18px; font-size: 18px; font-weight: 600; }
.modal-actions {
  display: flex; gap: 8px; justify-content: flex-end;
  margin-top: 22px; padding-top: 16px; border-top: 1px solid var(--hairline);
}

.form-grid { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.form-grid .span-2 { grid-column: span 2; }
.form-grid .span-all { grid-column: 1 / -1; }

.photo-picker { display: flex; gap: 14px; align-items: flex-start; }
.photo-picker .preview {
  width: 90px; height: 120px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  object-fit: cover;
  background: var(--plane);
  display: grid; place-items: center;
  color: var(--ink-muted); font-size: 11px;
  flex: none;
}
.photo-picker .picker-side { display: flex; flex-direction: column; gap: 8px; }

.check { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink-2); }
.check input { width: auto; }

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

.form-cards { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }
.form-card {
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  padding: 16px;
  cursor: pointer;
  font: inherit;
  color: var(--ink);
}
.form-card:hover { border-color: var(--accent); }
.form-card.active { border-color: var(--accent); background: var(--wash); }
.form-card .fc-title { font-size: 14px; font-weight: 600; margin-bottom: 4px; }
.form-card .fc-desc { font-size: 12px; color: var(--ink-2); }

/* The preview holds the very same sheet nodes that go to the printer — only
   scaled down. `zoom` (unlike `transform`) also shrinks the layout box, so the
   sheets stack without overlapping. */
.preview-shell {
  margin-top: 18px;
  background: var(--grid);
  border-radius: var(--r-md);
  padding: 18px;
  overflow: auto;
  max-height: 78vh;
}
.preview-shell .sheet {
  zoom: var(--preview-zoom, 1);
  margin: 0 auto 18px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, .25);
}

@media (max-width: 720px) {
  .topbar { flex-wrap: wrap; gap: 10px; }
  .nav { width: 100%; margin-left: 0; overflow-x: auto; }
  main { padding: 16px 12px 48px; }
  .form-grid .span-2 { grid-column: span 1; }
}

.io-subhead {
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: 8px;
}

/* ---------- bulk selection ---------- */

.bulk-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
  padding: 9px 14px;
  border-radius: var(--r-md);
  background: var(--wash);
  border: 1px solid var(--accent);
  font-size: 13px;
  color: var(--ink);
}
.bulk-bar[hidden] { display: none; }
.bulk-bar strong { font-size: 15px; font-variant-numeric: tabular-nums; }
.bulk-bar .spacer { margin-left: auto; }

table.data th.pick, table.data td.pick { width: 34px; padding-right: 0; }
table.data th.pick input, table.data td.pick input {
  width: 15px; height: 15px; cursor: pointer; accent-color: var(--accent);
}
table.data tbody tr.selected { background: var(--wash); }
table.data tbody tr.selected:hover { background: var(--wash); }

/* Compact tiles for the Members header — same components as the dashboard,
   scaled down so they sit above a table rather than dominating the page. */
.tiles.compact { grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); gap: 10px;
                 margin-bottom: 14px; }
.tiles.compact .tile { padding: 9px 12px; }
.tiles.compact .tile-value { font-size: 21px; line-height: 1.3; }
.tiles.compact .tile-label { font-size: 11px; }
.tiles.compact .tile-note { font-size: 10px; }
.tiles.compact .tile.filtered { border-color: var(--accent); background: var(--wash); }

.font-sample {
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  background: #fff;
  color: #0b0b0b;
  padding: 14px 16px;
  line-height: 1.9;
  overflow-wrap: anywhere;
}

/* ---------- activation screen ---------- */

.lic-screen { display: grid; place-items: center; min-height: 72vh; padding: 20px; }
.lic-card {
  width: min(440px, 100%);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  padding: 30px 28px 22px;
  text-align: center;
}
.lic-mark {
  width: 68px; height: 68px; margin: 0 auto 12px;
  background: url("../assets/icon-128.png") center/contain no-repeat;
}
.lic-card h1 { margin: 0; font-size: 22px; letter-spacing: .05em; }
.lic-sub { margin: 2px 0 22px; font-size: 12px; color: var(--ink-2); }
.lic-form { display: grid; gap: 14px; text-align: left; }
.lic-form input[name="licenceKey"] {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  letter-spacing: .12em;
  text-align: center;
  font-size: 15px;
}
.lic-message { min-height: 20px; margin-top: 12px; font-size: 12px; }
.lic-message.bad { color: var(--critical); }
.lic-foot {
  margin-top: 22px; padding-top: 16px;
  border-top: 1px solid var(--hairline);
}
.lic-foot a { color: var(--accent); text-decoration: none; }
.lic-foot a:hover { text-decoration: underline; }

.banner.update {
  border-color: var(--accent);
  background: var(--wash);
}
.banner.update a { color: var(--accent); font-weight: 600; }

.lic-readonly {
  padding: 8px 10px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  background: var(--plane);
  font-size: 14px;
  overflow-wrap: anywhere;
}
.lic-readonly.mono { font-family: ui-monospace, Menlo, monospace; letter-spacing: .08em; }
.lic-status { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.pill.good { border-color: var(--good); color: var(--good); background: transparent; }
.pill.bad { border-color: var(--critical); color: var(--critical); background: transparent; }

.sheet-list { display: grid; gap: 6px; }
.sheet-row {
  display: flex; align-items: baseline; gap: 12px;
  width: 100%; text-align: left; font: inherit; cursor: pointer;
  padding: 10px 12px; border-radius: var(--r-sm);
  border: 1px solid var(--hairline); background: var(--raised); color: var(--ink);
}
.sheet-row:hover { border-color: var(--baseline); }
.sheet-row.active { border-color: var(--accent); background: var(--wash); }
.sheet-row .sheet-name { font-weight: 600; }
.sheet-row .sheet-meta { margin-left: auto; font-size: 12px; color: var(--ink-2); }

/* a checkbox with its label on one line, used where a setting is a plain
   on/off choice rather than a field with a value */
.check-row { display: flex; align-items: center; gap: 8px; margin-bottom: 12px;
             cursor: pointer; font-size: 13px; }
.check-row input { cursor: pointer; }
.form-grid input:disabled { opacity: .55; cursor: not-allowed; }

/* a record that cannot yet fill a printed form — the number is how many fields
   are blank, and the title attribute names them */
.pill.missing { border-color: var(--warning); color: var(--warning);
                background: transparent; cursor: help; }
.tiles.compact .tile.warn { border-color: var(--warning); }
.tiles.compact .tile.warn .tile-value { color: var(--warning); }

/* paging strip under the table */
.pager { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
         margin-top: 12px; }
.pager .spacer { flex: 1; }
.pager select { width: auto; }
.pager .btn[disabled] { opacity: .4; cursor: default; }

/* The cropper sits above the edit form's modal (z-index 80) rather than
   replacing it — the form is still there when the crop is applied. */
.crop-overlay { position: fixed; inset: 0; z-index: 100;
                display: grid; place-items: center; padding: 20px; }
.crop-backdrop { position: absolute; inset: 0; background: rgba(11,11,11,.6); }
.crop-box { position: relative; background: var(--surface); color: var(--ink);
            border: 1px solid var(--line); border-radius: 10px;
            padding: 20px; max-width: 640px; width: 100%;
            max-height: 92vh; overflow: auto;
            box-shadow: 0 18px 50px rgba(0,0,0,.35); }
.crop-box h2 { margin: 0 0 14px; font-size: 17px; }
.crop-stage { display: grid; place-items: center; background: var(--wash);
              border: 1px solid var(--line); border-radius: 8px; padding: 10px; }
.crop-canvas { max-width: 100%; height: auto; touch-action: none;
               display: block; border-radius: 4px; }
.crop-controls { display: flex; align-items: flex-end; gap: 12px;
                 flex-wrap: wrap; margin-top: 14px; }
.crop-controls .field { margin: 0; }
.crop-controls input { width: 100px; }
.crop-presets { display: flex; gap: 6px; }

/* camera capture — same layering rules as the cropper */
.cam-overlay { position: fixed; inset: 0; z-index: 100;
               display: grid; place-items: center; padding: 20px; }
.cam-backdrop { position: absolute; inset: 0; background: rgba(11,11,11,.6); }
.cam-box { position: relative; background: var(--surface); color: var(--ink);
           border: 1px solid var(--line); border-radius: 10px; padding: 20px;
           max-width: 640px; width: 100%; max-height: 92vh; overflow: auto;
           box-shadow: 0 18px 50px rgba(0,0,0,.35); }
.cam-box h2 { margin: 0 0 14px; font-size: 17px; }
.cam-stage { display: grid; place-items: center; background: #000;
             border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
.cam-view { max-width: 100%; max-height: 52vh; display: block; }
.cam-view[hidden] { display: none; }

/* the notice that the preview stopped early — never printed */
.preview-more { display: flex; align-items: center; justify-content: center;
                gap: 12px; flex-wrap: wrap; padding: 14px; margin-top: 10px;
                border: 1px dashed var(--line); border-radius: 8px;
                color: var(--ink-muted); font-size: 12px; }
@media print { .preview-more { display: none !important; } }

/* the machine's code on the licence screen — meant to be read aloud or copied */
.lic-device { margin: 14px 0 4px; text-align: center; }
.lic-device-row { display: flex; align-items: center; justify-content: center;
                  gap: 10px; margin: 6px 0; }
.lic-device-code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
                   font-size: 18px; letter-spacing: 2px; padding: 6px 12px;
                   border: 1px solid var(--line); border-radius: 6px;
                   background: var(--wash); user-select: all; }
