/* Plain and temporary. The look is going to be reworked, so this is arranged to
   be deleted: no framework, no build step, one variable block at the top. */

:root {
  --ink: #1c1f24;
  --muted: #6b7280;
  --line: #d8dbe0;
  /* The separator inside the list — between cells, and between the lines of
     an entry. Lighter than `--line`, because it runs down every row of four
     hundred and should not draw a grid over the data; but only a little, since
     at one pixel anything lighter than this is not there at all. */
  --rule: #d9dce2;
  --bg: #ffffff;
  --panel: #f7f8fa;
  --accent: #2b6cb0;
  /* The same blue at a weight that can sit behind a whole row without
     shouting: the cursor is on screen constantly, unlike an error. */
  --accent-soft: #bcd4ec;
  --dirty: #fff8dd;
  --dirty-line: #e2b842;
  --fail: #d13438;
  --stale: #2b6cb0;
  --radius: 5px;
}

* { box-sizing: border-box; }

/* An author `display` declaration beats the user agent's `[hidden]` rule
   whatever the specificity, so anything with a `display` of its own stays
   visible when hidden. This is the one place to say otherwise. */
[hidden] { display: none !important; }

body {
  margin: 0;
  font: 14px/1.4 system-ui, -apple-system, "Segoe UI", sans-serif;
  color: var(--ink);
  background: var(--bg);
}

button.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
button.link { border: 0; background: none; color: var(--accent); padding: 4px 6px; }

input, select {
  font: inherit;
  padding: 3px 5px;
  border: 1px solid transparent;
  border-radius: 3px;
  background: transparent;
  width: 100%;
  min-width: 0;
}
input:focus, select:focus { outline: 2px solid var(--accent); background: var(--bg); }
input[type="checkbox"] { width: auto; }

.centre { max-width: 460px; margin: 8vh auto; padding: 0 16px; }

.panel { border: 1px solid var(--line); border-radius: var(--radius); padding: 16px; }
.panel h2 { margin: 8px 0 12px; font-size: 16px; }

form { display: flex; flex-direction: column; gap: 6px; }
form h1 { margin: 0 0 12px; font-size: 22px; }
form label { font-size: 12px; color: var(--muted); margin-top: 6px; }
form input { border-color: var(--line); background: var(--bg); }
form button { margin-top: 14px; }

/* The shell ----------------------------------------------------------
   Wide: the side bar is docked at the right edge and the top bar sits over
   it, so the rest of the window is nothing but the view.

       ┌──────────────────────┬────────────┐
       │                      │ user  out  │
       │        view          ├────────────┤
       │                      │ workspace  │
       │                      │ tables…    │
       └──────────────────────┴────────────┘                            */

:root {
  --side: 240px;
  /* One level of nesting: a border and a little padding, not an indent
     step. Three levels must not eat a quarter of the width. */
  --inset: 5px;
  /* The collapse chevron's gutter, which the header reserves too. */
  --gutter: calc(2 * var(--ch, 8px) + 6px);
}

.app {
  display: grid;
  height: 100vh;
  grid-template-columns: minmax(0, 1fr) var(--side);
  grid-template-rows: auto minmax(0, 1fr);
  grid-template-areas:
    "view topbar"
    "view side";
}
.topbar {
  grid-area: topbar;
  display: flex; align-items: center; gap: 8px;
  padding: 6px 10px; border-bottom: 1px solid var(--line); border-left: 1px solid var(--line);
  font-size: 12px; color: var(--muted); background: var(--panel);
}
.view { grid-area: view; display: flex; flex-direction: column; min-width: 0; overflow: hidden; }
.sidebar {
  grid-area: side;
  display: flex; flex-direction: column; gap: 12px;
  padding: 10px; border-left: 1px solid var(--line); background: var(--panel);
  overflow: auto;
}
.side-section { display: flex; flex-direction: column; gap: 4px; min-height: 0; }
.side-section.grow { flex: 1 1 auto; }
.side-label {
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .04em; color: var(--muted);
}
select.workspace { border: 1px solid var(--line); background: var(--bg); width: 100%; }
.tables { display: flex; flex-direction: column; gap: 2px; overflow: auto; }
/* Above the tables, and looking the same: both are lists of things to open,
   and a page is the one you open on an ordinary day. */
.pages { display: flex; flex-direction: column; gap: 2px; }

/* The strip above a view: what this page is called, and what to do with it. */
.pagebar {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding: 5px 10px; border-bottom: 1px solid var(--line); background: var(--panel);
}
.page-name {
  border: 1px solid var(--line); background: var(--bg);
  width: auto; min-width: 14em; font-weight: 600;
}
/* A marker, never a save: an autosave would rewrite a shared page because
   somebody widened a column to read it. */
.page-dirty { color: var(--dirty-line); font-size: 12px; white-space: nowrap; }
.page-busy { color: var(--muted); font-size: 12px; white-space: nowrap; }
/* The second press of delete. Nothing here interrupts, so the button carries
   the confirmation the dialogue would have asked for. */
button.danger { background: var(--fail); border-color: var(--fail); color: #fff; }
.table-link { text-align: left; border-color: transparent; background: none; }
.table-link:hover { background: var(--bg); }
.table-link.active { background: var(--bg); border-color: var(--line); font-weight: 600; }
.placeholder { padding: 16px; }
.spacer { flex: 1; }
.who { padding: 0 4px; }

/* The menu button is only for when the side bar is not already on screen. */
.menu { display: none; }

/* Narrow: one column. The top bar spans it, and the side bar is not there at
   all until the menu asks for it — then it covers everything, because a phone
   has no room to show a list of tables beside a table. */
@media (max-width: 720px) {
  .app {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas: "topbar" "view";
  }
  .topbar { border-left: 0; }
  .menu { display: inline-block; padding: 2px 8px; }
  /* The side bar takes the view's own grid cell rather than being positioned
     against a guessed top-bar height, so it covers exactly the space below the
     bar however tall the bar turns out to be. */
  .sidebar { display: none; grid-area: view; z-index: 20; border-left: 0; }
  .app.menu-open .sidebar { display: flex; }
}

.error { color: var(--fail); margin: 8px 0; }
.error p { margin: 4px 0 0; color: var(--ink); font-weight: normal; }
.empty { color: var(--muted); }
/* Something went wrong and the user has to see it. It had no styling at all,
   which made every failure read as silence: black body text at the foot of a
   scrolling page is indistinguishable from nothing having happened. */
.problem {
  color: var(--fail);
  background: #fff4f4;
  border: 1px solid var(--fail); border-radius: var(--radius);
  padding: 6px 10px;
}
.problem:empty { display: none; }

/* A stat cell: a sum of terms, each added or subtracted, each over another
   table. The reference column that points back at *this* row is what lets one
   request answer for every row on screen. */
.stat-cell { display: flex; flex-direction: column; gap: 4px; min-width: 22em; }
.stat-term {
  display: flex; flex-direction: column; gap: 2px;
  border: 1px solid var(--line); border-radius: var(--radius); padding: 4px;
}
.stat-term-head { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.stat-date, .stat-step, .stat-mode {
  border: 1px solid var(--line); background: var(--bg); width: auto; min-width: 8em;
}
.stat-table, .stat-column, .stat-link {
  border: 1px solid var(--line); background: var(--bg); width: auto; min-width: 7em;
}
/* A total is not a value of the row it sits on: it is the server's answer
   about rows that are not on screen. */
.cell.type-stat { justify-content: flex-end; font-variant-numeric: tabular-nums; }

/* Connector ----------------------------------------------------------
   Two tables in two workspaces, paired and checked. The panel configures it;
   the body is the result. */

.connector { display: flex; flex-direction: column; min-height: 0; flex: 1; }
.connector-panel {
  display: flex; flex-direction: column; gap: 6px;
  padding: 8px 10px; border-bottom: 1px solid var(--line); background: var(--panel);
}
.connector-side { display: flex; flex-direction: column; gap: 4px; }
.connector-workspace, .connector-table, .connector-part {
  border: 1px solid var(--line); background: var(--bg); width: auto; min-width: 9em;
}
.connector-key, .connector-agreement {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
}
.connector-part-box { display: flex; align-items: center; gap: 4px; }
/* A side's three editors — filter, layout, actions — behind a strip of its
   own. A list view leaves this to its tab strip; there is none here. */
.connector-panels { display: flex; flex-direction: column; }
.connector-tabs > .tab { border: 1px solid transparent; border-radius: var(--radius); }
.connector-tabs > .tab.active { box-shadow: none; background: var(--bg); }
/* The rule editor for one side, with its own toggle: a list view leaves this
   to its tab strip, and there is no strip here. */

.connector-hint { color: var(--muted); font-size: 12px; }
.connector-controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.connector-body { overflow: auto; min-height: 0; flex: 1; }
.connector-head, .connector-row { display: flex; align-items: baseline; gap: 0; }
.connector-head {
  position: sticky; top: 0; z-index: 5;
  background: var(--bg); border-bottom: 1px solid var(--line);
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .04em; color: var(--muted);
}
.connector-row {
  border: 0; border-bottom: 1px solid var(--rule); border-radius: 0;
  background: none; width: 100%; text-align: left; font: inherit; color: inherit;
  padding: 0;
}
.connector-row:hover { background: var(--panel); }
.connector-row.open { background: var(--panel); font-weight: 600; }

/* An opened row: the entries behind it, on both sides, as ordinary lists —
   which is what makes a mismatch mendable rather than only visible. */
.connector-open { border-bottom: 2px solid var(--line); }
.connector-detail { display: flex; align-items: stretch; gap: 1px; background: var(--line); }
.connector-half {
  flex: 1 1 0; min-width: 0; display: flex; flex-direction: column;
  background: var(--bg); max-height: 60vh;
}
.connector-half-head {
  display: flex; align-items: baseline; gap: 8px;
  padding: 4px 10px; background: var(--panel); border-bottom: 1px solid var(--line);
}
/* The list inside a half scrolls on its own; two of them side by side must not
   each be as tall as they like. */
.connector-half .list { min-height: 0; }
.connector-cell { padding: 4px 10px; flex: 1 1 0; min-width: 0; }
.connector-cell.key { font-weight: 600; flex: 0 0 22em; }
.connector-cell.state { flex: 0 0 11em; white-space: nowrap; color: var(--muted); }

/* The states, and only the ones worth a colour: agreeing rows are the ones
   nobody is looking for. */
.connector-row.state-differs .state { color: var(--fail); font-weight: 600; }
.connector-row.state-differs { background: #fff4f4; }
.connector-row.state-ambiguous .state { color: var(--dirty-line); font-weight: 600; }
.connector-row.state-ambiguous { background: var(--dirty); }

/* List --------------------------------------------------------------- */

.list { display: flex; flex-direction: column; min-height: 0; flex: 1; }

/* Three cells across the top: what this is, how filtered it is, how much of
   it there is. The status sits in front of the name, where it reads as a
   property of the view rather than as another field. */
.titlebar {
  display: flex; align-items: stretch;
  border-bottom: 1px solid var(--line);
  background: var(--panel);
}
.titlebar .cell {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 12px; border-right: 1px solid var(--line);
  font-size: 13px;
}
.titlebar .name { font-weight: 600; flex: 1 1 auto; min-width: 0; }

/* Tabs. At most one open; the open one is joined to the panel below it. */
.tabs { display: flex; align-items: stretch; }
.tab {
  border: 0; border-right: 1px solid var(--line); border-radius: 0;
  padding: 7px 12px; font-size: 13px;
  background: none; color: var(--muted); white-space: nowrap;
}
.tab:hover { background: #eef1f5; color: var(--ink); }
.tab.active {
  background: var(--bg); color: var(--ink); font-weight: 600;
  box-shadow: inset 0 2px 0 var(--accent);
}
.tab-panels:empty { display: none; }
.titlebar .count { flex: 0 0 auto; color: var(--muted); white-space: nowrap; }

.status { display: inline-block; width: 1.1em; text-align: center; flex: 0 0 auto; }

/* All three tab panels — filter, layout, schema — open between the title and
   the column headers, so the header stays attached to the rows it labels.
   They share one shape, and one rule about size.

   **A panel scrolls; it does not push the list off the screen.** Forty columns
   or a dozen rules is taller than a phone, and a panel that grows to fit them
   takes the rows with it. Capped at half the window and scrolling inside, the
   list stays where it is and the panel stays reachable. `min-content` on the
   rows inside is what makes the sideways half work: a row of fixed-width boxes
   is wider than a narrow screen, and without it the far end is unreachable
   rather than merely off-screen. */
.filter-panel, .config-panel, .schema-panel {
  padding: 8px 12px;
  border-bottom: 1px solid var(--line); background: var(--bg);
  display: flex; flex-direction: column; gap: 6px;
  max-height: 55vh; overflow: auto;
}
.rule-row, .line-block, .schema-table, .schema-draft, .schema-columns,
.filter-actions, .config-actions {
  min-width: min-content;
}
/* Every control in the panel is the same shape and sits at the right, so the
   eye finds the actions in one place and the rules read as a column. */
.rule-row { display: flex; align-items: center; gap: 6px; }
.rule-row > button.tiny:last-child { flex: 0 0 auto; }
.rule { display: flex; align-items: center; gap: 6px; flex: 1 1 auto; min-width: 0; }
.rule select, .rule input {
  border: 1px solid var(--line); background: var(--bg); width: auto;
  padding: 3px 5px; border-radius: 3px;
}
.rule select.path { min-width: 12em; }
.rule select.condition { min-width: 5.5em; text-align: center; }
.rule .value, .rule .value.empty { flex: 1 1 10em; min-width: 0; }
.rule .chips { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
.chip { display: inline-flex; align-items: center; gap: 2px; }
.chip input { width: 8em; }
.chip select.chip-value { width: 10em; }

/*     | OR | some rule  | AND   | × |
       |    | other rule | + AND | × |
       |    |            | + OR  | × |
   Every row ends the same way, so the joiner and the remove control line up
   down the whole block whatever is in the rows. */
.or-block {
  display: flex; align-items: stretch; gap: 8px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); padding: 6px;
}
.or-gutter {
  display: flex; align-items: center; justify-content: center;
  flex: 0 0 2.5em; border-right: 1px solid var(--line); padding-right: 6px;
}
.or-label { font-size: 12px; font-weight: 600; color: var(--muted); }
.or-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
/* An alternative holding more than one rule ANDs them. */
.or-alt { display: flex; flex-direction: column; gap: 3px; }
.or-alt + .or-alt { border-top: 1px dashed var(--line); padding-top: 3px; }

/* The joiner column: a connector to the row below, or the button that adds it. */
.joiner { flex: 0 0 4.6em; text-align: center; }
span.joiner.label {
  font-size: 11px; font-weight: 600; color: var(--muted); line-height: 1.9;
}
button.tiny.joiner { padding: 2px 4px; }

/* One shape for every control, checkbox included, so nothing reads as odd. */
button, .toggle {
  font: inherit; font-size: 13px; line-height: 1.3;
  padding: 4px 10px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg); cursor: pointer; white-space: nowrap;
}
button:hover, .toggle:hover { background: var(--panel); }
button.tiny { padding: 2px 6px; font-size: 12px; }
.toggle {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--muted);
}
.toggle input[type="checkbox"] { margin: 0; }

.filter-actions {
  display: flex; gap: 6px; align-items: center; justify-content: flex-end;
  padding-top: 2px;
}
.filter-actions .error { margin: 0; }

.banner {
  margin: 8px 12px; padding: 8px 10px;
  border: 1px solid var(--dirty-line); background: var(--dirty); border-radius: var(--radius);
}

/* Only the body scrolls; the header stays put. A `wrap: false` line scrolls
   sideways, and the header goes with it because it is in the same scroller. */
.scroll { overflow: auto; flex: 1; }
.probe { position: absolute; visibility: hidden; white-space: pre; }

/* The header: one row per configured line, sized from the same measurement as
   the cells below, which is what puts a label over its values. */
.head {
  position: sticky; top: 0; z-index: 1;
  display: flex; align-items: stretch;
  background: var(--panel); border-bottom: 1px solid var(--line);
  min-width: min-content;
}
.hlines { flex: 1 1 auto; display: flex; flex-direction: column; }
.hline { display: flex; gap: 4px; padding: 2px 4px; }
.hline.wrap { flex-wrap: wrap; }
.hline + .hline { border-top: 1px solid var(--rule); }
.hline.grouping { background: #eef1f5; }

/* Group rectangles contain their entries: a box has an edge, and the edge
   answers "what is in this group?". Levels are told apart by a slightly
   different surface tint, not by indentation and not by a new colour. */
.group {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 4px; margin: 0;
  display: flex; flex-direction: column; gap: 3px;
}
.group.depth-0 { background: #f2f5f8; }
.group.depth-1 { background: #eaeff4; }
.group.depth-2 { background: #e3e9f0; }
.group-head { display: flex; align-items: center; gap: 3px; }
.chevron {
  flex: 0 0 auto; border: 0; background: none; padding: 0 4px;
  color: var(--muted); font-size: 12px; line-height: 1;
}
.chevron:hover { background: none; color: var(--ink); }
.group-head .line { flex: 1 1 auto; min-width: 0; font-weight: 600; }
.group-count {
  flex: 0 0 auto; margin-left: auto; padding-left: 8px;
  font-size: 12px; font-weight: normal; color: var(--muted); white-space: nowrap;
}
.group-body { display: flex; flex-direction: column; gap: 3px; }
.group-head .cell input, .group-head .cell select { font-weight: 600; }
.group-head .cell input.bad { border: 1px solid var(--fail); background: #fff5f5; }

/* Revealed by the expander: this entry's grouping values, editable for this
   entry alone, above its own lines. */
.entry-group-lines { display: flex; flex-direction: column; }
.group-line { background: #f4f6f8; border-bottom: 1px solid var(--line); }
button.expander { padding: 1px 5px; color: var(--muted); }
/* The label and its filter share one line. The label gives way first — a
   truncated name beside a usable box reads better than the reverse. */
.hcell {
  display: flex; align-items: center; gap: 3px; flex: 0 0 auto; min-width: 0;
  font-size: 12px; font-weight: 600;
}
.hcell .label {
  flex: 0 1 auto; min-width: 0;
  border: 0; background: none; font: inherit; font-weight: 600; color: var(--ink);
  padding: 1px 2px; text-align: inherit; cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hcell .label:hover { color: var(--accent); background: none; }
.hcell input, .hcell select {
  flex: 1 1 3em; min-width: 2.5em; width: auto;
  border: 1px solid var(--line); background: var(--bg);
  font-size: 12px; font-weight: normal; padding: 1px 3px;
}
/* A ref is chosen, not typed, so its control needs room for a name. */
.hcell select.quick-ref { flex: 1 1 5em; }
/* A box that cannot be parsed goes red and does not change the query. */
.hcell input.bad { border-color: var(--fail); background: #fff5f5; }
.hactions { flex: 0 0 auto; padding: 3px 6px; display: flex; align-items: flex-start; }
.sort { color: var(--accent); }

/* One entry is one bordered box. With wrapping, the border is the only thing
   that says where one entry ends. */
.rows { display: flex; flex-direction: column; gap: 3px; padding: 3px 4px;
        min-width: min-content; }
.entry {
  display: flex; align-items: flex-start;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg);
}
.entry:hover { border-color: #c3c8d0; }
/* The lines stack inside the box; the actions sit beside them. Same shape as
   .hlines / .hactions in the header, and it has to stay that way. */
.entry-lines { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.line { display: flex; gap: 4px; padding: 1px 4px; min-width: 0; }
.line.wrap { flex-wrap: wrap; }
.entry-lines > .line:first-child { padding-top: 3px; }
.entry-lines > .line:last-child { padding-bottom: 3px; }
.entry-lines > .line + .line { border-top: 1px solid var(--rule); }
.cell {
  flex: 0 0 auto; min-width: 0;
  /* The header is sticky and sits over the top of the scrolling box, so a cell
     brought to the top by the arrow keys would land behind it. This is the one
     thing `scrollIntoView` takes into account. */
  scroll-margin-top: var(--head-height, 3em);
  scroll-margin-bottom: 4px;
}
/* A light rule between cells, in the header and in every row alike — the two
   are laid out from the same widths, so ruling both keeps a column ruled all
   the way down. On the second cell onwards, so a line does not begin with one.
   The gap stays: the rule is a separator, not a box drawn round the value. */
.line > .cell + .cell,
.hline > .hcell + .hcell {
  border-left: 1px solid var(--rule);
}
.cell.align-right, .hcell.align-right { text-align: right; }
.cell.align-centre, .hcell.align-centre { text-align: centre; text-align: center; }
.cell.joined { color: var(--muted); padding: 3px 5px; overflow: hidden;
               text-overflow: ellipsis; white-space: nowrap; }
.cell input, .cell select { width: 100%; text-align: inherit; }
.row-actions {
  flex: 0 0 auto; display: flex; align-items: center; gap: 2px;
  padding: 2px 4px; white-space: nowrap;
}
.row-actions button { padding: 1px 6px; font-size: 12px; }

/* Changed and not yet saved. The row being edited shows it per cell, where the
   change actually is. Every *other* row carries the tint whole — a group edit
   reaches rows that have no cell on screen for the column it changed, since the
   grouping columns live on the group line, and a border colour alone is not
   findable while scanning a screen of them. */
.entry.dirty { border-color: var(--dirty-line); }
.entry.dirty:not(.editing) { background: var(--dirty); }
.entry.failed { border-color: var(--fail); }
.entry.stale { box-shadow: inset 3px 0 0 var(--stale); }
.entry.deleted { color: var(--muted); }
.entry.deleted .cell { text-decoration: line-through; }
.cell.dirty { background: var(--dirty); border-radius: 3px; }

/* Where the keyboard is. Two marks, because arrow keys move two things: the
   entry, which up and down step through, and the cell within it, which left
   and right do. The cell mark has to read as inside the entry mark. */
.rows:focus { outline: none; }
.entry.cursor { border-color: var(--accent); }
.rows:focus .entry.cursor { box-shadow: 0 0 0 2px var(--accent-soft); }
.cell.cursor { box-shadow: inset 0 0 0 2px var(--accent); border-radius: 3px; }
/* A group header is a row the cursor visits too, and it has to say so the same
   way — the mark is on its own header line, not on the box, which contains
   every entry below it and would light up the lot. */
.rows:focus .group.cursor > .group-head { box-shadow: 0 0 0 2px var(--accent-soft); }
.group.cursor > .group-head { border-radius: 3px; }

/* An entry open for editing. It is the only one, so it may take the room. */
.entry.editing { border-color: var(--accent); background: var(--bg); }
.entry.editing .cell.cursor { box-shadow: none; }

/* An open row's fields look like fields. Elsewhere an input is invisible until
   it has focus, which is right for a screen you are reading and wrong for the
   one row you are editing: you cannot see what there is to type in until you
   have already clicked in it. */
.entry.editing .cell input, .entry.editing .cell select,
.group-head .cell input, .group-head .cell select {
  border-color: var(--line);
  background: var(--bg);
}
/* The field with the caret is marked by the accent, which says where you are.
   It says nothing about whether anything has been altered. */
.entry.editing .cell input:focus, .entry.editing .cell select:focus,
.group-head .cell input:focus, .group-head .cell select:focus {
  background: var(--bg);
  outline: 2px solid var(--accent);
}
/* Yellow is only ever "this differs from what was there", never "this is where
   the caret is". It has to survive focus, so it comes after the rule above. */
.entry.editing .cell input.changed, .entry.editing .cell select.changed,
.group-head .cell input.changed, .group-head .cell select.changed,
.entry.editing .cell input.changed:focus, .entry.editing .cell select.changed:focus,
.group-head .cell input.changed:focus, .group-head .cell select.changed:focus {
  background: var(--dirty);
  border-color: var(--dirty-line);
}
/* This entry's own copy of the grouping values, above its own lines. */
.entry.editing .entry-group-lines { border-bottom: 1px dashed var(--accent); }
.entry-group-lines .cell { color: inherit; }
/* A control that writes to the whole group, distinguished from one that does
   not, because the two look identical and do very different things. Marked by
   the accent, never by the dirty tint: yellow means "changed and not saved"
   everywhere else, and using it for scope made an untouched field look edited. */
.group-scope {
  border-color: var(--accent) !important;
  box-shadow: inset 0 -2px 0 var(--accent-soft);
}
input.bad, select.bad { border-color: var(--fail); background: #fff4f4; }


/* The layout editor ------------------------------------------------- */
.line-block {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); padding: 6px;
  display: flex; flex-direction: column; gap: 6px;
}
.line-head { display: flex; align-items: center; gap: 6px; }
.line-name { font-size: 12px; font-weight: 600; color: var(--muted); }
.line-cells { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
/* A cell of the layout, as a card rather than a strip.
   Five things to say about a cell and up to eight cells on a line: side by
   side that is a row wider than any screen, and the line stops reading as a
   line. Stacked, each card is a column of the view and the line above it is a
   row of them, which is the shape the layout actually has. */
.cell-chip {
  display: inline-flex; flex-direction: column; gap: 3px;
  width: 13em; flex: 0 0 auto; vertical-align: top;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg); padding: 4px;
}
.chip-row { display: flex; align-items: center; gap: 3px; }
.chip-row > * { min-width: 0; }
.chip-row.actions { margin-top: 1px; }
.cell-chip select, .cell-chip input {
  border: 1px solid var(--line); background: var(--bg); width: auto; padding: 2px 4px;
}
.cell-chip .cell-path { flex: 1 1 auto; }
.cell-chip .cell-title { flex: 1 1 auto; }
.cell-chip .cell-width { width: 3.5em; text-align: right; }
.cell-chip .cell-align { flex: 1 1 auto; min-width: 4.5em; }
.cell-chip .cell-scale { width: 4em; text-align: right; }
/* The formula field is the width of its card, and wraps inside it. Its
   problem message goes underneath rather than beside, there being no beside. */
.cell-chip .formula-field {
  display: flex; flex-direction: column; align-items: stretch;
  flex: 1 1 auto; min-width: 0;
}
.cell-chip .formula-box { min-width: 0; flex: 1 1 auto; }
.cell-chip .formula-fix { margin-left: 6px; }
.formula-problem { max-width: 12em; }
/* A line's cards run across it and wrap when they run out of room. */
.line-cells { align-items: stretch; }

/* An int_unit is two editors for one value: the amount, then its unit. */
.pair { display: flex; gap: 3px; align-items: center; }
.pair .amount { flex: 1 1 auto; min-width: 4em; }
.pair .unit { flex: 0 0 5.5em; width: 5.5em; color: var(--muted); }
.pair select.unit { padding-right: 0; }

tbody td.dirty { background: var(--dirty); }
/* Typed but not yet committed — the field is still the user's until Enter. */
tbody td input.editing { background: var(--dirty); outline: 1px solid var(--dirty-line); }
tbody tr.dirty td { border-bottom-color: var(--dirty-line); }
tbody tr.failed td { box-shadow: inset 0 -1px 0 var(--fail); }
tbody tr.stale td { box-shadow: inset 2px 0 0 var(--stale); }
tbody tr.deleted td { text-decoration: line-through; color: var(--muted); }

td.actions, th.actions { white-space: nowrap; width: 1%; text-align: right; border-right: 0; }
th.actions { padding-right: 6px; }
td.actions button { padding: 1px 6px; font-size: 12px; }
.sync { display: inline-block; width: 1.2em; text-align: center; }
.sync.synced { color: #3f9142; }
.sync.dirty { color: var(--dirty-line); }
.sync.pending { color: var(--accent); }
.sync.failed { color: var(--fail); }
.sync.stale { color: var(--stale); }

/* The schema editor ---------------------------------------------------
   Same shape and the same scrolling as the other two. */
.schema-table, .schema-draft, .schema-column {
  display: flex; align-items: center; gap: 6px;
}
.schema-label { font-size: 11px; color: var(--muted); text-transform: uppercase; }
.schema-panel input, .schema-panel select {
  border: 1px solid var(--line); background: var(--bg); width: auto;
}
.schema-name { min-width: 12em; }
.schema-description { flex: 1 1 auto; min-width: 8em; }
.schema-new-name { min-width: 12em; }
.schema-type { min-width: 10em; }
.schema-ref { min-width: 10em; }
.schema-scale, .schema-max { width: 7em; }
.schema-units { width: 14em; }
.schema-default { width: 8em; }

.schema-columns {
  display: flex; flex-direction: column; gap: 2px;
  border: 1px solid var(--line); border-radius: var(--radius); padding: 4px;
}
.schema-column { padding: 2px 4px; border-radius: 3px; }
.schema-column:hover { background: var(--panel); }
.schema-column-name { font-weight: 600; width: 12em; flex: 0 0 auto; }
.schema-column-description { flex: 1 1 auto; min-width: 6em; }
/* What a column *is* — the part that can never be changed again, so it is
   spelled out on every row rather than hidden behind the name. */
.schema-label { font-size: 12px; color: var(--muted); white-space: nowrap; }
.schema-column-type { color: var(--muted); font-size: 12px; }
.schema-column.deleted .schema-column-name { text-decoration: line-through; color: var(--muted); }
.schema-column.deleted input { color: var(--muted); }

/* Naming a new table, in the side bar with the list of them — a table is not
   part of the table you are looking at, so it is not in that table's tabs. */
.new-table-row { display: flex; gap: 4px; align-items: center; }
.new-table { border: 1px solid var(--line); background: var(--bg); }
.side-toggle { font-size: 12px; color: var(--muted); }
.table-link.deleted { color: var(--muted); text-decoration: line-through; }

/* Importing --------------------------------------------------------------
   A page rather than a panel: it belongs to the workspace, not to a table,
   and the table it writes into may not exist yet. */
.import-page {
  flex: 1 1 auto; min-height: 0; overflow: auto;
  padding: 12px 16px; display: flex; flex-direction: column; gap: 10px;
}
.import-head { display: flex; align-items: baseline; gap: 8px; }
.import-head h2 { margin: 0; font-size: 16px; }
.import-file { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.import-file input { width: auto; }
.import-said { color: var(--muted); font-size: 13px; }
.import-target { display: flex; flex-direction: column; gap: 6px; }
.import-mode { display: flex; align-items: center; gap: 8px; }
.import-mode input[type="radio"] { width: auto; }
.import-mode > span { min-width: 12em; }
.import-table, .import-new-name { border: 1px solid var(--line); background: var(--bg); width: auto; }

/* One row per heading: what it is called, what it becomes, and enough of its
   values to see that the pairing is right before anything is written. */
.import-mapping {
  display: flex; flex-direction: column; gap: 2px; min-width: min-content;
  border: 1px solid var(--line); border-radius: var(--radius); padding: 6px;
}
.import-row { display: flex; align-items: center; gap: 8px; padding: 2px 4px; }
.import-row:hover { background: var(--panel); }
.import-heading { min-width: 12em; font-weight: 600; }
.import-arrow { color: var(--muted); }
.import-column, .import-type, .import-column-name, .import-scale {
  border: 1px solid var(--line); background: var(--bg); width: auto;
}
.import-column, .import-type { min-width: 9em; }
.import-column-name { min-width: 10em; }
.import-scale { width: 6em; }
.import-draft { display: flex; align-items: center; gap: 6px; }
.import-ref-table { border: 1px solid var(--line); background: var(--bg); min-width: 9em; }
.import-refmatch { border: 1px solid var(--line); background: var(--bg); min-width: 8em; }
.import-sample {
  flex: 1 1 auto; min-width: 0; color: var(--muted); font-size: 12px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.import-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.import-skipped { color: var(--dirty-line); font-size: 13px; }
.import-errors {
  flex: 1 1 100%; max-height: 12em; overflow: auto;
  font-size: 12px; color: var(--fail);
  border: 1px solid var(--line); border-radius: var(--radius); padding: 6px;
}
.import-link { align-self: flex-start; font-size: 12px; }
.import-format { border: 1px solid var(--line); background: var(--bg); width: auto; min-width: 11em; }
/* What the first value becomes, beside what it is. The answer to "did that
   work?", given before anything is written rather than after. */
.import-raw { color: var(--muted); }
.import-became { color: var(--ink); margin-left: 8px; }
.import-became.bad { color: var(--fail); }
.import-default-unit { border: 1px solid var(--line); background: var(--bg); width: 6em; }
/* Which column of the file carries an amount's unit. On the amount, because
   one currency column is commonly the unit of two amounts beside it. */
.import-unit-from { border: 1px solid var(--line); background: var(--bg); width: auto; min-width: 11em; }
/* A computed cell. Not stored, so never editable and never dirty; the dash it
   shows when a formula fails carries the reason in its title. */
.cell.type-computed { font-variant-numeric: tabular-nums; }

/* Writing a formula. A field is chosen from the `$` list and becomes a chip
   holding a uuid, so a rename changes every formula and breaks none — and
   there is no way to half-type one, because it is never typed. */
.formula-field { display: inline-flex; align-items: center; gap: 4px; position: relative; }
.formula-box {
  border: 1px solid var(--line); border-radius: 3px; background: var(--bg);
  min-width: 20em; padding: 3px 5px; min-height: 1.9em;
  font-family: ui-monospace, "Cascadia Code", Menlo, monospace; font-size: 12px;
  white-space: pre-wrap; word-break: break-word;
}
.formula-box:focus { outline: 2px solid var(--accent); }
.formula-box.bad { border-color: var(--fail); background: #fff4f4; }
.formula-box:empty::before {
  content: attr(data-placeholder); color: var(--muted);
}
/* The rectangle. `contenteditable="false"` makes it atomic; this makes it look
   like the one thing it is. */
.formula-chip {
  display: inline-block; padding: 0 5px; margin: 0 1px;
  border: 1px solid var(--accent); border-radius: 9px;
  background: var(--accent-soft); color: var(--ink);
  font-family: inherit; font-size: 11px; white-space: nowrap;
  user-select: none;
}
.formula-chip.bad { border-color: var(--fail); background: #fff4f4; color: var(--fail); }
.formula-fix { margin-left: 6px; }
.formula-problem { color: var(--fail); font-size: 11px; max-width: 16em; }
.formula-picker {
  position: absolute; top: 100%; left: 0; z-index: 30; min-width: 18em; max-height: 14em;
  overflow: auto; display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg);
  box-shadow: 0 2px 6px rgba(0, 0, 0, .12);
}
.formula-option {
  border: 0; border-radius: 0; background: none; text-align: left;
  padding: 3px 8px; font-size: 12px; white-space: nowrap;
}
.formula-option.on, .formula-option:hover { background: var(--accent-soft); }
.cell-chip.computed { border-color: var(--accent); }

/* Actions -----------------------------------------------------------------
   Defined against one entry: a button runs one row, the tab runs every entry
   the filter matched. One definition, two ways to fire it. */
.actions-panel {
  padding: 8px 12px;
  border-bottom: 1px solid var(--line); background: var(--bg);
  display: flex; flex-direction: column; gap: 6px;
  max-height: 55vh; overflow: auto;
}
.action-block, .action-set, .action-add, .action-row { min-width: min-content; }
.action-block {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); padding: 6px;
  display: flex; flex-direction: column; gap: 6px;
}
.action-name { border: 1px solid var(--line); background: var(--bg); width: 14em; font-weight: 600; }
.action-set, .action-add { display: flex; flex-direction: column; gap: 4px; }
/* An added entry is a block of its own: its fields belong to a row that does
   not exist yet, not to the one the action is running against. */
.action-add {
  border: 1px dashed var(--line); border-radius: var(--radius);
  padding: 4px 6px; background: var(--bg);
}
.action-row { display: flex; align-items: center; gap: 6px; }
.action-verb, .action-equals { color: var(--muted); font-size: 12px; }
.action-field {
  border: 1px solid var(--line); background: var(--bg); width: auto; min-width: 10em;
}
/* An added entry is a form: every field of its table, one per row, with the
   name on the left where a form label goes. */
.action-fields { display: flex; flex-direction: column; gap: 4px; }
.action-name-label {
  min-width: 10em; color: var(--muted); font-size: 12px; text-align: right;
}
.action-table {
  border: 1px solid var(--line); background: var(--bg); min-width: 10em;
}
/* Choosing the entry a reference field points at. As wide as the formula field
   it stands in place of, so a row does not jump about as the field changes. */
.action-ref {
  border: 1px solid var(--line); background: var(--bg); flex: 1; min-width: 12em;
}
.action-missing { color: var(--fail); font-size: 12px; }
.actions-report { color: var(--muted); font-size: 12px; }
/* The button in a row. It runs for its own row and nothing else. */
.cell.type-action { display: flex; align-items: center; justify-content: center; }
.action-button { width: 100%; }
.cell-action { border: 1px solid var(--line); background: var(--bg); }
