:root {
  --bg: #0e1116;
  --panel: #161b22;
  --panel-2: #1c232c;
  --line: #2a323d;
  --text: #e6edf3;
  --muted: #8b98a8;
  --accent: #4c9aff;
  --ok: #3fb950;
  --warn: #d29922;
  --danger: #f85149;
  --radius: 10px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

h1 { font-size: 1.5rem; margin: 0 0 .5rem; }
h2 { font-size: 1rem; margin: .25rem 0 .75rem; font-weight: 600; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

.muted { color: var(--muted); }
.small { font-size: .85rem; }

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

/* ── login ───────────────────────────────────────────────────────────── */

body.centered {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: 1.5rem;
}

.card {
  width: 100%;
  max-width: 380px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.75rem;
}

label {
  display: block;
  margin: 1rem 0 .35rem;
  font-size: .85rem;
  color: var(--muted);
}

input, select, button, textarea {
  font: inherit;
  color: var(--text);
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: .55rem .7rem;
  width: 100%;
}

input:focus-visible, select:focus-visible, button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

button {
  margin-top: 1.1rem;
  background: var(--accent);
  border-color: var(--accent);
  color: #06131f;
  font-weight: 600;
  cursor: pointer;
}

button:hover { filter: brightness(1.08); }
button:disabled { opacity: .55; cursor: default; }

button.link {
  background: none;
  border: 0;
  color: var(--accent);
  padding: 0;
  margin: 0;
  width: auto;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
}
button.link.danger { color: var(--danger); }

.alert {
  background: rgba(248, 81, 73, .12);
  border: 1px solid rgba(248, 81, 73, .4);
  border-radius: 8px;
  padding: .65rem .8rem;
  margin: 0 0 1rem;
  font-size: .9rem;
}

/* A neutral, non-error confirmation — the forgot-password acknowledgement,
   which must not read as either success or failure (account enumeration). */
.notice {
  background: rgba(56, 139, 253, .10);
  border: 1px solid rgba(56, 139, 253, .35);
  border-radius: 8px;
  padding: .65rem .8rem;
  margin: 0 0 1rem;
  font-size: .9rem;
}

/* ── shell ───────────────────────────────────────────────────────────── */

.shell { display: flex; min-height: 100vh; }

.sidebar {
  width: 210px;
  flex: 0 0 210px;
  background: var(--panel);
  border-right: 1px solid var(--line);
  padding: 1.2rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.brand { font-weight: 700; letter-spacing: .01em; }

.sidebar nav { display: flex; flex-direction: column; gap: .2rem; }
.sidebar nav a { display: flex; align-items: center; gap: .5rem; padding: .35rem .5rem; border-radius: 6px; color: var(--text); }
.sidebar nav a:hover { background: var(--panel-2); text-decoration: none; }

/* The panel is grouped (owner, 2026-10-05): a caption names each section and its
   links sit under it. `.nav-group` is the block, `.nav-group-label` the
   non-clickable caption, `.nav-group-items` the links. The first group drops the
   extra top margin so it sits where the old flat list did. */
.nav-group { display: flex; flex-direction: column; gap: .15rem; margin-top: .6rem; }
.nav-group:first-child { margin-top: 0; }
.nav-group-label {
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
  padding: 0 .5rem;
}
.nav-group-items { display: flex; flex-direction: column; gap: .15rem; }

/* One icon per nav row, from the inline sprite in base_app.html. Stroke-only and
   drawn in `currentColor`, so it takes the link's colour and its hover state. */
.nav-ico { width: 16px; height: 16px; flex: 0 0 auto; }
.sidebar .soon {
  padding: .35rem .5rem;
  color: var(--muted);
  opacity: .55;
  cursor: not-allowed;
}

.sidebar-foot {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: .5rem;
  font-size: .85rem;
}
.sidebar-foot .who { color: var(--muted); }

.content { flex: 1; padding: 1.75rem 2rem; min-width: 0; }
.content.wide { max-width: 1100px; margin: 0 auto; }

/* ── stats ───────────────────────────────────────────────────────────── */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: .75rem;
  margin: 1.25rem 0;
}

.stat {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .9rem 1rem;
}
.stat .n { display: block; font-size: 1.6rem; font-weight: 700; }
.stat .l { color: var(--muted); font-size: .8rem; }

/* ── board ───────────────────────────────────────────────────────────── */

.board {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: .9rem;
  margin-top: 1rem;
}

.job {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .9rem 1rem;
}
.job[draggable="true"] { cursor: grab; }
.job.dragging { opacity: .5; }

.job header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: .5rem;
}
.job .key { color: var(--muted); font-size: .78rem; font-variant-numeric: tabular-nums; }

.pill {
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  padding: .15rem .5rem;
  border-radius: 999px;
  border: 1px solid var(--line);
}
.pill.estimate { color: var(--warn); border-color: rgba(210, 153, 34, .45); }
.pill.active { color: var(--ok); border-color: rgba(63, 185, 80, .45); }
.pill.completed { color: var(--muted); }
/* A cancelled job is closed early — red, the same danger colour a delete uses,
   so it reads as "stopped", not "done". `archived` is not a status; the pill
   is there for any surface that marks an archived row. */
.pill.cancelled { color: var(--danger); border-color: rgba(248, 81, 73, .45); }
.pill.archived { color: var(--muted); border-color: var(--line); }

.crew { list-style: none; margin: 0 0 .6rem; padding: 0; font-size: .9rem; }
.crew li { display: flex; justify-content: space-between; gap: .5rem; padding: .15rem 0; }
.crew li.empty { color: var(--muted); }

.row { display: flex; gap: .5rem; align-items: center; margin-top: .5rem; }
.row select { flex: 1; }
.row button { margin-top: 0; width: auto; padding: .5rem .8rem; }

.rail {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .85rem 1rem;
  margin-top: 1rem;
}
.rail h2 { margin: 0 0 .5rem; }
.rail ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .5rem; }

.chip {
  border: 1px dashed var(--line);
  border-radius: 999px;
  padding: .3rem .8rem;
  font-size: .85rem;
}
.chip.over { border-color: var(--accent); background: rgba(76, 154, 255, .12); }
.chip.busy { opacity: .5; }

.job .meta-right { display: flex; align-items: center; gap: .4rem; }
.job .date { color: var(--muted); font-size: .78rem; }

/* ── Layer A: the crew TV board ──────────────────────────────────────── */

body.tv {
  background: #0b0e13;
  padding: 1.5rem 2rem 1rem;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}

.tv-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 2rem; }
.tv-head h1 { font-size: 2rem; margin: 0; }
.tv-sub { color: var(--muted); margin: .2rem 0 0; font-size: 1rem; }

.tv-counts { display: flex; gap: 1.5rem; list-style: none; margin: 0; padding: 0; text-align: right; }
.tv-counts .n { display: block; font-size: 2.4rem; font-weight: 700; line-height: 1; }
.tv-counts .l { color: var(--muted); font-size: .95rem; }

.tv-band {
  border: 1px solid var(--line);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius);
  padding: .8rem 1.1rem;
  background: var(--panel);
}
.tv-band h2 { margin: 0 0 .5rem; font-size: 1.1rem; }
.tv-today { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 1.25rem; }
.tv-today li { display: flex; align-items: center; gap: .6rem; font-size: 1.15rem; }
.tv-today strong { font-weight: 600; }

.tv-columns { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; flex: 1; }

.tv-col h2 {
  font-size: 1.05rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
  border-bottom: 1px solid var(--line);
  padding-bottom: .4rem;
  margin: 0 0 .7rem;
}
.tv-n { color: var(--text); font-weight: 700; }

.tv-job {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .7rem .85rem;
  margin-bottom: .7rem;
}
.tv-job.active { border-left: 4px solid var(--ok); }
.tv-job.estimate { border-left: 4px solid var(--warn); }
.tv-job.completed { border-left: 4px solid var(--line); opacity: .72; }

.tv-job header { display: flex; justify-content: space-between; font-size: .8rem; color: var(--muted); }
.tv-job h3 { font-size: 1.3rem; margin: .25rem 0 .45rem; line-height: 1.25; }

.tv-crew { margin: 0; font-size: 1.05rem; color: var(--text); }
.tv-crew em { color: var(--muted); font-style: italic; }

.tv-chip {
  display: inline-block;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: .15rem .65rem;
  margin-right: .35rem;
  font-size: 1rem;
}

.tv-empty { color: var(--muted); font-style: italic; }

.tv-pool { border-top: 1px solid var(--line); padding-top: .8rem; }
.tv-pool h2 { font-size: 1rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 0 0 .5rem; }
.tv-pool ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .6rem; }
.tv-pool li {
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: .35rem 1rem;
  font-size: 1.15rem;
  background: var(--panel);
}

.tv-foot {
  display: flex;
  justify-content: space-between;
  color: var(--muted);
  font-size: .8rem;
  padding-top: .4rem;
}

@media (max-width: 900px) {
  .tv-columns { grid-template-columns: 1fr; }
  .tv-head { flex-direction: column; }
}

/* ── gallery (Phase 5) ──────────────────────────────────────────────── */

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  align-items: flex-end;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .9rem 1rem;
  margin: 1rem 0;
}
.filters > div { flex: 1 1 160px; }
.filters label { margin: 0 0 .3rem; }
.filters-go { flex: 0 0 auto; }
.filters-go button { margin-top: 0; width: auto; padding: .55rem 1rem; }

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: .9rem;
  margin-top: 1rem;
}

.photo {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .9rem 1rem;
  display: flex;
  flex-direction: column;
  gap: .5rem;
}
.photo header { display: flex; justify-content: space-between; align-items: center; gap: .5rem; }
.photo h2 { margin: 0; font-size: .98rem; }
.photo .key { color: var(--muted); font-size: .78rem; font-variant-numeric: tabular-nums; }
.photo .facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .15rem .6rem;
  margin: 0;
  font-size: .82rem;
}
.photo .facts dt { color: var(--muted); }
.photo .facts dd { margin: 0; font-variant-numeric: tabular-nums; }
/* The photograph itself, from the authorized download route in its inline
   mode. Fixed height so a portrait shot and a landscape one make an even
   grid; the bytes are never resized, only the box they are drawn in. */
.photo .shot {
  display: block;
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) - 2px);
  overflow: hidden;
  background: var(--line);
}
.photo .shot img { display: block; width: 100%; height: 180px; object-fit: cover; }
.photo .row { margin-top: auto; display: flex; gap: .8rem; align-items: center; }

/* Tags and the before/after badge: small, clickable, and the same shape the
   rail's chips use so the vocabulary reads as one thing. */
.tags { display: flex; flex-wrap: wrap; gap: .3rem; margin: 0; }
.tag {
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: .1rem .55rem;
  font-size: .78rem;
  color: var(--accent);
}
.tag:hover { background: var(--panel-2); text-decoration: none; }
.pair { margin: 0; font-size: .85rem; }

.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

.pager {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 1.25rem;
  padding-top: .9rem;
  border-top: 1px solid var(--line);
}

/* ── charts and tables (Phase 6) ────────────────────────────────────── */

.chart {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem;
  margin-top: 1rem;
}
.chart svg { width: 100%; height: auto; display: block; }
.chart .legend { display: flex; gap: 1.25rem; font-size: .85rem; color: var(--muted); margin: .6rem 0 0; }
.chart .legend .swatch {
  display: inline-block;
  width: .8rem; height: .8rem;
  border-radius: 2px;
  margin-right: .35rem;
  vertical-align: -1px;
}
/* The two chart colours, as classes: an inline style attribute would be
   blocked by `style-src 'self'`. Values match services.revenue. */
.swatch.revenue { background: #3fb950; }
.swatch.expense { background: #f85149; }

/* Tables: the revenue rollups. Borderless, tabular figures, right-aligned
   money so a column of amounts can be read down. */
table.grid { width: 100%; border-collapse: collapse; margin-top: .75rem; font-size: .9rem; }
table.grid th, table.grid td { text-align: left; padding: .45rem .6rem; border-bottom: 1px solid var(--line); }
table.grid th { color: var(--muted); font-weight: 600; font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; }
table.grid td.num, table.grid th.num { text-align: right; font-variant-numeric: tabular-nums; }
table.grid tfoot td { font-weight: 700; border-bottom: 0; }
.pos { color: var(--ok); }
.neg { color: var(--danger); }

/* A plain panel, used by the revenue and settings pages. */
.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.1rem 1.2rem;
  margin-top: 1rem;
}
.panel h2 { margin-top: 0; }
.panel button { width: auto; padding: .55rem 1.1rem; }
.hint { font-size: .82rem; color: var(--muted); margin: .3rem 0 .8rem; }

/* ── settings (Phase 7) ─────────────────────────────────────────────── */

.settings-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 1rem;
  align-items: start;
  margin-top: 1rem;
}
.settings-grid .panel { margin-top: 0; }

.error { color: var(--danger); }

/* ── the roster screen (Piece 2) ────────────────────────────────────── */

/* A row action inside a table cell: the form is the control, so it must not
   carry the block margins the standalone forms do. */
table.grid td form { margin: 0; display: inline; }
table.grid td .row { margin-top: 0; gap: .7rem; }
table.grid td .pill { font-size: .72rem; }

/* The two-panel roster: the list beside the person you selected. It is the
   settings grid with the list weighted, so the edit form keeps a readable
   width and the list does not become a sliver. Below the breakpoint the grid
   falls back to one column, the same way it does on every other
   `.settings-grid` page — never two unusable halves. */
.settings-grid.with-detail {
  grid-template-columns: minmax(0, 1.4fr) minmax(340px, 1fr);
}

/* Which row the other column is showing, so the two halves of the page agree
   on who you are looking at. */
table.grid tr.selected td { background: var(--panel-2); }

/* Name/value pairs, the same shape the photo card's facts list uses — generic
   here because the roster and the job jacket both read one. */
.facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .15rem .6rem;
  margin: 0 0 .6rem;
  font-size: .85rem;
}
.facts dt { color: var(--muted); }
.facts dd { margin: 0; font-variant-numeric: tabular-nums; }

@media (max-width: 900px) {
  .settings-grid.with-detail { grid-template-columns: 1fr; }
}

/* ── the jobs and receipts screens ──────────────────────────────────── */

/* Job scope: a label for the work, beside the key and never inside it. Same
   shape as the status pill, deliberately quieter. */
.pill.scope {
  color: var(--accent);
  border-color: rgba(76, 154, 255, .45);
  margin-left: .35rem;
}

/* The three review states Phase 4 keeps apart: "nobody has looked" is not
   "checked and right", and neither is "a human changed it". */
.pill.state-unreviewed { color: var(--warn); border-color: rgba(210, 153, 34, .45); }
.pill.state-verified { color: var(--ok); border-color: rgba(63, 185, 80, .45); }
.pill.state-corrected { color: var(--accent); border-color: rgba(76, 154, 255, .45); }

/* The jacket's add-forms use the filter row's layout, which already stacks and
   wraps; inside a panel it needs no top margin of its own. */
.panel .filters { margin: .5rem 0 0; }
.panel .filters label { margin-top: 0; }
table.grid td button.link { white-space: nowrap; }

/* The Support nav badge: a circled "!" with a faint yellow glow that says the
   office has reports it has not looked at. It sits on the right of the nav row,
   so it reads as attached to Support rather than to the page. */
/* The Support nav marker: a filled amber disc carrying a dark "!" — the
   unread-reports signal, and deliberately not a faint one.

   The selector is `.sidebar nav a .nav-badge`, NOT a bare `.nav-badge`, and
   that is load-bearing: the generic `.sidebar nav a` rule above sets `color`
   at specificity (0,1,1), which beat the bare class (0,1,0) and rendered the
   "!" as ordinary link text. Always out-specify that rule here. */
.sidebar nav a .nav-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  min-width: 1.3em;
  height: 1.3em;
  padding: 0 .3em;
  border-radius: 999px;
  background: var(--warn);
  color: #17130a;
  font-size: .78rem;
  font-weight: 800;
  line-height: 1;
  vertical-align: middle;
  box-shadow: 0 0 0 1px rgba(210, 153, 34, .35), 0 0 10px 2px rgba(210, 153, 34, .4);
}

/* The Support row is the one nav row that can carry a marker, so it was the one
   that became a flex row: label left, marker pinned to the right edge. Now that
   every row also carries its icon, `space-between` would strand the label
   between the icon and the marker, so the marker takes the auto margin instead —
   same right-edge result. Scoped to the Support row exactly as before. */
.sidebar nav a.nav-support {
  display: flex;
  align-items: center;
  gap: .5rem;
}
.sidebar nav a.nav-support .nav-badge { margin-left: auto; }

/* ── the support screen (the office half of the crew's issue report) ─── */

/* Tickets are CARDS THAT EXPAND (owner, 2026-10-02, from a reference shot):
   the collapsed card carries the few facts you scan by, and opening it grows
   the card downward into the message plus a dense label/value table. This
   replaced the flat table — the office wanted the scannable card shape back,
   but with the diagnostics still tabled rather than loose. */
.tickets {
  display: flex;
  flex-direction: column;
  gap: .6rem;
}

.ticket {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
  overflow: hidden;
}

.ticket-details > .ticket-summary {
  display: flex;
  flex-direction: column;
  gap: .4rem;
  padding: .75rem .9rem;
  cursor: pointer;
  list-style: none;
}
.ticket-summary::-webkit-details-marker { display: none; }
.ticket-summary:hover { background: var(--panel-2); }

.ticket-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .6rem;
}
.ticket-who { font-weight: 600; }

.ticket-facts {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem 1.1rem;
  font-size: .85rem;
}
.ticket-fact .ticket-label { color: var(--muted); }
.ticket-fact .ticket-label::after { content: ":"; }
.ticket-hint { margin-left: auto; }

.ticket-body {
  padding: 0 .9rem .9rem;
  border-top: 1px solid var(--line);
}

.ticket-section {
  margin: .85rem 0 .3rem;
  font-size: .72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--muted);
}

.ticket-message { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }

/* The tabled half: label left, value right, every row a share of the width. */
.ticket-table { width: 100%; border-collapse: collapse; }
.ticket-table th,
.ticket-table td {
  padding: .3rem .5rem;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line);
}
.ticket-table th {
  width: 1%;
  white-space: nowrap;
  font-weight: 500;
  color: var(--muted);
}
.ticket-table tr:last-child th,
.ticket-table tr:last-child td { border-bottom: none; }
.support-ua { font-size: .8rem; }

/* Ticket on the left, what the phone saw on the right (owner, 2026-10-02).
   Two columns while there is room; one column once there is not, because a
   squeezed diagnostics table is worse than a stacked one. */
.ticket-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .25rem 1.5rem;
  align-items: start;
}
@media (max-width: 760px) {
  .ticket-cols { grid-template-columns: 1fr; }
}

/* A log reads as lines, so each entry gets its own, and a long one wraps
   instead of widening the column. */
.ticket-logline {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* The Logs terminal (owner, 2026-10-03): one large box per ticket that every log
   prints into — the report's own crew log first, then any link-door walkthrough
   log from the same job, separated by a line naming which door it came from.

   It looks like a terminal because it is one: lines in the order they happened,
   monospace, on a near-black field. It SCROLLS rather than growing the card to
   the height of the afternoon, which is what keeps a 200-line log readable and
   the page navigable. */
.terminal {
  min-height: 18rem;
  max-height: 30rem;
  overflow: auto;
  padding: .7rem .85rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #05080b;
  color: #a8ecb6;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  font-size: .78rem;
  line-height: 1.55;
}
.terminal-line {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* Nothing to show is a fact, not an error — so it says so quietly and in the
   same box, rather than collapsing the column and shifting the layout. */
.terminal--empty {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-style: italic;
}

/* The screenshots a report rode in with, under its details. */
.ticket-shots {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  margin-top: .6rem;
}
.ticket-shot img {
  display: block;
  max-width: 96px;
  max-height: 96px;
  border-radius: 6px;
  border: 1px solid var(--line);
}

/* The action bar is anchored to the foot of the card in BOTH states — and it
   lives outside <details>, so pressing Complete cannot fold the card. */
.ticket-actions {
  display: flex;
  gap: .9rem;
  padding: .5rem .9rem;
  border-top: 1px solid var(--line);
  background: var(--panel-2);
}
.ticket-actions .link { font-size: .85rem; }

/* The link door's own logs, listed under the queue. Set apart from the queue
   itself, because these are not tickets and reading them as tickets would be a
   mistake the layout should not invite. */
.linklogs-head {
  margin: 2rem 0 .3rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
}

/* The page's own status line: where a refusal is reported in words instead of
   a JSON body in the browser window. */
[data-page-status] { min-height: 1.2em; }

/* ── the inbound SMS table ─────────────────────────────────────────── */

/* A column of timestamps, lined up so it can be read down — the same reason
   `.num` carries tabular figures for a column of amounts. */
.tnum { font-variant-numeric: tabular-nums; }

.nowrap { white-space: nowrap; }

/* The inbound table's one free-length cell. It takes the slack so the columns
   either side keep their width, and a long message wraps inside the panel
   instead of stretching the table past it — a phone screen has no horizontal
   scroll to fall back on here. */
table.grid td.msg { max-width: 34ch; overflow-wrap: anywhere; }

/* The three kinds of inbound message, kept apart by colour the way the job
   states are: a request needs answering, a stop must never be overlooked, and
   a start is a door reopening. */
.pill.request { color: var(--accent); border-color: rgba(76, 154, 255, .45); }
.pill.opt-out { color: var(--danger); border-color: rgba(248, 81, 73, .45); }
.pill.opt-in { color: var(--ok); border-color: rgba(63, 185, 80, .45); }

/* ── Crew Management (Phase 1) ─────────────────────────────────────── */

/* The grid is at most three across and steps down to one. `auto-fit` with a
   320px floor would fit a fourth card on a wide monitor, so the cap is a
   max-width of 1120px (3 × 320px + gaps) rather than a fixed column count:
   the cards stay large and the row never runs past three. */
.crew-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 1rem;
  margin-top: 1rem;
  max-width: 1120px;
}

/* The [hidden] guard. #crew-bulk-bar and #crew-modal below are display:flex,
   and an author display rule outranks the browser's own [hidden]; without this
   the bar and the modal would sit on screen from the first paint. The same trap
   once left the WalkThru PIN gate visible. */
#crew-bulk-bar[hidden],
#crew-modal[hidden] { display: none !important; }

.crew-card {
  display: flex;
  flex-direction: column;
  gap: .7rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem 1.1rem;
  cursor: pointer;
  transition: border-color .12s ease, background .12s ease;
}
.crew-card:hover { border-color: #3a4657; }
.crew-card.is-selected { border-color: var(--accent); background: var(--panel-2); }

.crew-card-head { display: flex; align-items: center; gap: .75rem; }

/* The initials disc. Fixed size so a name that wraps never squashes it. */
.crew-avatar {
  flex: 0 0 auto;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--panel-2);
  border: 1px solid var(--line);
  color: var(--accent);
  font-weight: 700;
  font-size: 1.15rem;
}
.crew-ident { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.crew-ident .crew-jacket { font-weight: 600; font-size: 1.05rem; }
.crew-name { overflow-wrap: anywhere; }

/* The generic control rule gives every input 100% width and a panel fill; a
   checkbox wants neither. */
.crew-select {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  padding: 0;
  background: transparent;
}
.crew-select:disabled { opacity: .3; cursor: default; }

.crew-chips { display: flex; flex-wrap: wrap; gap: .35rem; }

/* A login chip is a statement, not a dashed "free slot" the way the board's
   crew chips are, so it is solid. */
.crew-card .chip { border-style: solid; }
.chip.login { color: var(--ok); border-color: rgba(63, 185, 80, .45); }
.chip.invited { color: var(--warn); border-color: rgba(210, 153, 34, .45); }
.chip.none { color: var(--muted); }
.pill.role-admin { color: var(--accent); border-color: rgba(76, 154, 255, .45); }
.pill.role-crew { color: var(--ok); border-color: rgba(63, 185, 80, .45); }

.crew-contact {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .1rem .6rem;
  margin: 0;
  font-size: .85rem;
}
.crew-contact dt { color: var(--muted); }
.crew-contact dd { margin: 0; overflow-wrap: anywhere; }

/* The pencil sits at the card's foot, so every card's controls line up no
   matter how many chips or contact lines it carries. */
.crew-card-foot { display: flex; gap: .5rem; margin-top: auto; }
.crew-edit {
  width: auto;
  margin: 0;
  padding: .4rem .85rem;
  background: none;
  border: 1px solid var(--line);
  color: var(--text);
  font-weight: 500;
}
.crew-edit:hover { border-color: var(--accent); color: var(--accent); filter: none; }

/* The sticky bar: it appears under the page title once something is selected
   and rides the top of the viewport while the grid scrolls under it. */
#crew-bulk-bar {
  position: sticky;
  top: 0;
  z-index: 15;
  margin-top: 1rem;
  background: var(--panel-2);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: .6rem .9rem;
  display: flex;
  align-items: center;
  gap: 1rem;
}
#crew-bulk-bar form {
  display: flex;
  align-items: center;
  gap: 1rem;
  width: 100%;
  margin: 0;
}
#crew-bulk-bar button { width: auto; margin: 0; }

/* The edit modal, over the page rather than instead of it. `align-items`
   flex-start plus a scrollable overlay keeps a tall form (person + account)
   reachable on a short window instead of clipping its bottom. */
#crew-modal {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 2rem 1rem;
  background: rgba(8, 11, 15, .65);
  overflow-y: auto;
}
.crew-modal-box {
  width: 100%;
  max-width: 520px;
  margin: auto 0;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.2rem 1.3rem 1.4rem;
}
.crew-modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
}
.crew-modal-head h2 { margin: 0; }

/* Each record is a labelled block inside the one modal: the person first, then
   the account under a rule, so it reads as two records rather than one long
   form. */
#crew-person-form h3,
#crew-account-form h3 {
  margin: 1rem 0 .25rem;
  font-size: .8rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
}
#crew-account-form {
  margin-top: 1.2rem;
  padding-top: .6rem;
  border-top: 1px solid var(--line);
}

@media (max-width: 700px) {
  .crew-grid { grid-template-columns: 1fr; }
  .crew-modal-box { max-width: none; }
}

/* ── Crew Management (Phase 1.5): substance cards + the big modal ───── */

/* The card lost its pill/chip row; the facts grid below the head carries the
   wage, the contact lines and the last login instead. */
.crew-facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .15rem .6rem;
  margin: 0;
  font-size: .85rem;
}
.crew-facts dt { color: var(--muted); }
.crew-facts dd { margin: 0; overflow-wrap: anywhere; }

/* The role reads as plain text under the names, not a coloured tag. */
.crew-role {
  color: var(--muted);
  font-size: .8rem;
  text-transform: capitalize;
}

/* A much bigger modal: two masonry-ish columns of section cards, stepping to
   one on a narrow window. Column flow packs the cards without a fixed grid. */
.crew-modal-box { max-width: 1100px; }
.crew-modal-cols { column-count: 2; column-gap: 1rem; }
.crew-modal-cols > * { break-inside: avoid; }

/* A section (or the notes/upload form) that is display:block would outrank the
   browser's own [hidden]; the same trap the bulk bar already documents. */
.crew-modal-cols [hidden] { display: none !important; }

.crew-sec {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .9rem 1rem 1rem;
  margin: 0 0 1rem;
  break-inside: avoid;
}
.crew-sec h3 {
  margin: 0 0 .4rem;
  font-size: .8rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
}
.crew-sec label:first-of-type { margin-top: .35rem; }

/* The notes toolbar: compact buttons, not the full-width form button. */
.crew-notes-toolbar { display: flex; flex-wrap: wrap; gap: .3rem; margin-bottom: .4rem; }
.crew-notes-toolbar button {
  width: auto;
  margin: 0;
  padding: .3rem .55rem;
  background: none;
  border: 1px solid var(--line);
  color: var(--text);
  font-weight: 600;
}
.crew-notes-toolbar button:hover { border-color: var(--accent); color: var(--accent); filter: none; }

.crew-notes-editor {
  min-height: 120px;
  padding: .6rem .7rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow-wrap: anywhere;
}
.crew-notes-editor:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.crew-notes-editor ul, .crew-notes-editor ol { margin: .3rem 0; padding-left: 1.3rem; }

.crew-docs { list-style: none; margin: .6rem 0 0; padding: 0; font-size: .85rem; }
.crew-docs li { padding: .15rem 0; overflow-wrap: anywhere; }

.crew-sec-foot { display: flex; align-items: center; gap: .6rem; margin-top: .5rem; }
.crew-sec-foot button { width: auto; margin-top: 0; }

@media (max-width: 860px) {
  .crew-modal-cols { column-count: 1; }
}

/* ── Job Management (2026-10-04): cards + the big detail modal ──────── */

/* The grid caps at three across for the same reason the crew grid does:
   `auto-fit` with a 320px floor would fit a fourth card on a wide monitor, so
   the width is capped (3 × 320px + gaps) rather than a fixed column count. */
.job-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 1rem;
  margin-top: 1rem;
  max-width: 1120px;
}

/* The [hidden] guard: the bulk bar and the modal are display:flex, and an
   author display rule outranks the browser's own [hidden]; without this they
   would sit on screen from the first paint (the trap #crew-bulk-bar documents). */
#job-bulk-bar[hidden],
#job-modal[hidden] { display: none !important; }

.job-card {
  display: flex;
  flex-direction: column;
  gap: .7rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem 1.1rem;
  cursor: pointer;
  transition: border-color .12s ease, background .12s ease;
}
.job-card:hover { border-color: #3a4657; }
.job-card.is-selected { border-color: var(--accent); background: var(--panel-2); }

.job-card-head { display: flex; align-items: flex-start; gap: .75rem; }
.job-ident { display: flex; flex-direction: column; flex: 1; min-width: 0; gap: .1rem; }
.job-title { font-weight: 600; font-size: 1.05rem; overflow-wrap: anywhere; }
.job-key { color: var(--muted); font-size: .8rem; font-variant-numeric: tabular-nums; }
.job-meta { color: var(--muted); font-size: .82rem; text-transform: capitalize; }

/* The generic control rule gives every input 100% width and a panel fill; a
   checkbox wants neither. */
.job-select {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  padding: 0;
  background: transparent;
}

.job-facts {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .15rem .6rem;
  margin: 0;
  font-size: .85rem;
}
.job-facts dt { color: var(--muted); }
.job-facts dd { margin: 0; overflow-wrap: anywhere; }

.job-card-foot { display: flex; gap: .5rem; margin-top: auto; }
.job-edit {
  width: auto;
  margin: 0;
  padding: .4rem .85rem;
  background: none;
  border: 1px solid var(--line);
  color: var(--text);
  font-weight: 500;
}
.job-edit:hover { border-color: var(--accent); color: var(--accent); filter: none; }

/* The sticky bar under the title; hidden until something is selected. */
#job-bulk-bar {
  position: sticky;
  top: 0;
  z-index: 15;
  margin-top: 1rem;
  background: var(--panel-2);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: .6rem .9rem;
  display: flex;
  align-items: center;
  gap: 1rem;
}
#job-bulk-bar form {
  display: flex;
  align-items: center;
  gap: 1rem;
  width: 100%;
  margin: 0;
}
#job-bulk-bar button { width: auto; margin: 0; }

/* The edit modal, over the page rather than instead of it. `align-items:
   flex-start` plus a scrollable overlay keeps a tall form reachable on a short
   window instead of clipping its bottom. */
#job-modal {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 2rem 1rem;
  background: rgba(8, 11, 15, .65);
  overflow-y: auto;
}
.jm-modal-box {
  width: 100%;
  max-width: 1100px;
  margin: auto 0;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.2rem 1.3rem 1.4rem;
}
.jm-modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
}
.jm-modal-head h2 { margin: 0; }

/* Two columns of section cards (re-cut 2026-10-04). A grid, not the old
   multicol flow: the flow balanced itself and pushed every card but Details
   into the second column, so the layout could not be steered. A grid says
   exactly which card sits where — Specs beside Exclusions, Timeline and
   Payments across the full width — and the one `gap` is the whole spacing
   rule, so every card keeps the same padding on every side. */
.jm-modal-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  align-items: start;
}
.jm-modal-cols > * { min-width: 0; }
/* The Details, Notes and Uploads cards ride inside their own <form>; the form
   is layout-transparent so the card, not the form, is the grid item. */
.jm-modal-cols > form { display: contents; }

/* A section that is display:block would outrank the browser's own [hidden];
   the same trap the bulk bar documents. */
.jm-modal-cols [hidden] { display: none !important; }

.jm-sec {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem;
  margin: 0;
  min-width: 0;
}
/* Timeline reads across, the way Payments does: a wide list of milestones in
   a half-width column is a cramped scroll, not a timeline. */
.jm-sec-timeline, .jm-sec-payment { grid-column: 1 / -1; }
.jm-sec h3 {
  margin: 0 0 .4rem;
  font-size: .8rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
}
.jm-sec label:first-of-type { margin-top: .35rem; }
.jm-sec-foot { display: flex; align-items: center; gap: .6rem; margin-top: .5rem; }
.jm-sec-foot button { width: auto; margin-top: 0; }

/* One ordered list per modal section: a column of rows, each a draggable card
   carrying that list's fields. */
.jm-list {
  list-style: none;
  margin: .5rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: .4rem;
}
.jm-empty { padding: .15rem 0; }

.jm-row {
  display: flex;
  align-items: flex-start;
  gap: .4rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: .4rem .5rem;
}
.jm-row.jm-dragging { opacity: .5; }
.jm-drag {
  cursor: grab;
  color: var(--muted);
  user-select: none;
  line-height: 1.7;
  flex: 0 0 auto;
}
.jm-row-fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(90px, 1fr));
  gap: .35rem;
  flex: 1;
  min-width: 0;
}
.jm-row-fields input { padding: .35rem .5rem; font-size: .85rem; }
.jm-field[type="checkbox"] {
  width: 18px;
  height: 18px;
  padding: 0;
  margin-top: .4rem;
  background: transparent;
}
.jm-del {
  width: auto;
  margin: 0;
  padding: 0 .3rem;
  font-size: .9rem;
  flex: 0 0 auto;
}

/* The notes toolbar: compact buttons, not the full-width form button. */
.jm-notes-toolbar { display: flex; flex-wrap: wrap; gap: .3rem; margin-bottom: .4rem; }
.jm-notes-toolbar button {
  width: auto;
  margin: 0;
  padding: .3rem .55rem;
  background: none;
  border: 1px solid var(--line);
  color: var(--text);
  font-weight: 600;
}
.jm-notes-toolbar button:hover { border-color: var(--accent); color: var(--accent); filter: none; }

.jm-notes-editor {
  min-height: 120px;
  padding: .6rem .7rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow-wrap: anywhere;
}
.jm-notes-editor:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.jm-notes-editor ul, .jm-notes-editor ol { margin: .3rem 0; padding-left: 1.3rem; }

/* The job's files, grouped by category under their own expandable headings. */
.jm-filegroup { margin-top: .6rem; border-top: 1px solid var(--line); padding-top: .4rem; }
.jm-filegroup summary { cursor: pointer; color: var(--muted); font-size: .85rem; }
.jm-files { list-style: none; margin: .4rem 0 0; padding: 0; font-size: .85rem; }
.jm-files li { padding: .15rem 0; overflow-wrap: anywhere; }

@media (max-width: 860px) {
  .jm-modal-cols { grid-template-columns: 1fr; }
}
@media (max-width: 700px) {
  .job-grid { grid-template-columns: 1fr; }
  .jm-modal-box { max-width: none; }
}

/* Job Management, ledger slice (2026-10-04): the Records door on a card, and
   the modal's credit/debit ledger with its receipt-attach controls.

   `.job-records` mirrors `.job-edit` so the two doors on a card read as one
   pair (the pencil opens the edit modal, Records opens the jacket). */
.job-records {
  width: auto;
  margin: 0;
  padding: .4rem .85rem;
  background: none;
  border: 1px solid var(--line);
  color: var(--text);
  font-weight: 500;
  text-decoration: none;
  border-radius: var(--radius);
}
.job-records:hover { border-color: var(--accent); color: var(--accent); }

/* The ledger list: same row shell as the other collections, plus a receipt
   cell pinned after the fields, and a type/category select sized like its
   sibling inputs. */
.jm-ledger-list .jm-ledger-row { align-items: flex-start; }
.jm-ledger-row .jm-field-type,
.jm-ledger-row .jm-field-category { padding: .35rem .5rem; font-size: .85rem; }
.jm-receipt-cell {
  flex: 0 0 auto;
  max-width: 10rem;
  overflow-wrap: anywhere;
  font-size: .8rem;
  align-self: center;
}
.jm-receipt-cell a { color: var(--accent); }
.jm-detach { margin-left: .3rem; font-size: .75rem; }

/* Receipts not yet on an entry: one line each, with the entry picker and its
   attach button. */
.jm-receipt-attach { display: flex; flex-direction: column; gap: .35rem; }
.jm-receipt-attach-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .4rem;
  padding: .2rem 0;
}
.jm-receipt-name { overflow-wrap: anywhere; }
.jm-attach-entry { width: auto; padding: .25rem .4rem; font-size: .8rem; }
.jm-attach-go {
  width: auto;
  margin: 0;
  padding: .25rem .6rem;
  font-size: .8rem;
}

/* Photo lightbox (static/lightbox.js): the thumbnail is a button, and clicking
   it lifts the full-size file into an overlay instead of following a download
   link. The button reset is here because a <button> brings its own chrome where
   the old <a class="shot"> brought none. */
.shot[data-lightbox] { padding: 0; width: 100%; font: inherit; color: inherit; cursor: zoom-in; }
.shot[data-lightbox]:focus-visible { outline: 2px solid var(--accent, #6ea8fe); outline-offset: 2px; }
.lb {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  padding: 2rem; background: rgba(6, 8, 12, .88);
}
.lb[hidden] { display: none; }
.lb [hidden] { display: none; }
.lb-off { visibility: hidden; }
.lb-close {
  position: absolute; top: 1rem; right: 1.2rem;
  width: 2.4rem; height: 2.4rem; border-radius: 50%;
  border: 1px solid var(--line); background: rgba(0, 0, 0, .45);
  color: #fff; font-size: 1.4rem; line-height: 1; cursor: pointer;
}
.lb-stage {
  display: grid; grid-template-columns: auto 1fr auto;
  grid-template-rows: 1fr auto; align-items: center; gap: 1rem;
  max-width: min(96vw, 1400px); max-height: 92vh;
}
.lb-img {
  grid-column: 2; grid-row: 1;
  max-width: min(90vw, 1200px); max-height: 80vh;
  object-fit: contain; border-radius: var(--radius); background: #000;
}
.lb-caption { grid-column: 2; grid-row: 2; margin: 0; color: #e8e8ea; text-align: center; font-size: .86rem; }
.lb-prev, .lb-next {
  grid-row: 1; border: 1px solid var(--line); background: rgba(0, 0, 0, .45);
  color: #fff; font-size: 1.6rem; width: 2.6rem; height: 3.4rem;
  border-radius: var(--radius); cursor: pointer;
}
.lb-prev { grid-column: 1; }
.lb-next { grid-column: 3; }
body.lb-open { overflow: hidden; }

/* A row field with its name above it (static/jobs_manage.js `labelled`): an
   amount box and a note box side by side are two anonymous inputs without
   this. The label is visible, not a placeholder — a placeholder disappears
   exactly when a value arrives and the name is most needed. */
.jm-cell { display: flex; flex-direction: column; gap: .12rem; min-width: 0; }
.jm-cell-label { font-size: .64rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.jm-cell input, .jm-cell select { width: 100%; min-width: 0; }

/* ── Payments table (job modal, 2026-10-04) ─────────────────────────────
   One plain-text table where the scheduled-payment, deposit and ledger lists
   used to be. It spans both columns via `.jm-sec-payment` above (the grid's
   `grid-column: 1 / -1`, which replaced `column-span` when the modal moved off
   multicol). Everything is scoped to `.jm-pay-*` / `#jm-pay-table` /
   `#jm-pay-modal`, which nothing else uses (the skill's "scope new CSS to a
   hook only your element has"). */

.jm-pay-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  margin-bottom: .4rem;
}
.jm-pay-head h3 { margin: 0; }
.jm-pay-head button {
  width: auto;
  margin: 0;
  padding: .3rem .8rem;
}

.jm-pay-table {
  width: 100%;
  border-collapse: collapse;
  font-size: .85rem;
}
.jm-pay-table th,
.jm-pay-table td {
  text-align: left;
  padding: .3rem .4rem;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.jm-pay-table thead th {
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
  font-weight: 600;
}
.jm-pay-table td.num,
.jm-pay-table th.num { text-align: right; white-space: nowrap; }
.jm-pay-table tbody tr:last-child td { border-bottom: none; }
.jm-pay-details,
.jm-pay-receipt { overflow-wrap: anywhere; }
.jm-pay-receipt a { color: var(--accent); }
.jm-pay-actions { white-space: nowrap; text-align: right; }
.jm-pay-actions button {
  width: auto;
  margin: 0 0 0 .4rem;
  padding: 0;
}

/* The totals: a rule above the group, labels quiet and uppercase. */
.jm-pay-table tfoot th,
.jm-pay-table tfoot td {
  border-top: 1px solid var(--line);
  border-bottom: none;
  font-weight: 600;
}
.jm-pay-table tfoot th {
  text-transform: uppercase;
  font-size: .68rem;
  letter-spacing: .04em;
  color: var(--muted);
}

/* Money-gated payload: when no row and no estimate carries an amount (a crew
   caller), the amount cells are hidden rather than shown as a fake zero. */
.jm-pay-table.is-nomoney .jm-pay-amount { display: none; }

/* The Add/Edit payment overlay: a second, smaller modal over the big one. */
#jm-pay-modal {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: rgba(8, 11, 15, .72);
  overflow-y: auto;
}
/* A display rule outranks the browser's own [hidden]; the same trap the big
   modal and the bulk bar document. */
#jm-pay-modal[hidden],
.jm-pay-branch[hidden] { display: none !important; }
.jm-pay-box {
  width: 100%;
  max-width: 420px;
  margin: auto 0;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem 1.1rem 1.1rem;
}
.jm-pay-box label { display: block; margin-top: .5rem; }
.jm-pay-box input,
.jm-pay-box select { width: 100%; }
.jm-pay-branch { margin-top: .1rem; }

@media (max-width: 480px) {
  .jm-pay-table { font-size: .78rem; }
}

/* ── the office board: large cards, four across, flexing down ─────────── */

.board-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1rem;
  margin-top: 1rem;
  align-items: start;
}

.board-card {
  display: flex;
  flex-direction: column;
  gap: .6rem;
  min-height: 9rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-left: 4px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem 1.1rem;
}
.board-card[draggable="true"] { cursor: grab; }
.board-card.dragging { opacity: .5; }

.board-card-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .4rem;
}
.board-card-head .board-key {
  color: var(--muted);
  font-size: .8rem;
  font-variant-numeric: tabular-nums;
}
.board-title {
  margin: 0;
  font-size: 1.15rem;
  font-weight: 600;
  overflow-wrap: anywhere;
}
.board-card .board-status {
  display: flex;
  gap: .5rem;
  align-items: center;
  margin-top: auto;
}
.board-card .board-status select { flex: 1; }
.board-card .board-status button {
  width: auto;
  margin: 0;
  padding: .45rem .8rem;
}

/* One colour per status, on the card's own edge. The `status-<value>` class is
   set server-side and re-painted in place by board.js.

   Owner's colours (2026-10-05): estimate = yellow, active = green,
   wrap-up = blue, closed = amber. `wrap_up` and `closed` are the later stage
   names — the migration lands separately, so the rules are here now to be
   ready — and `completed` carries amber as the stand-in for `closed` until
   that rename. */
.board-card.status-estimate { border-left-color: #e3b341; }   /* yellow */
.board-card.status-active { border-left-color: var(--ok); }   /* green */
.board-card.status-wrap_up { border-left-color: var(--accent); }  /* blue */
.board-card.status-closed { border-left-color: var(--warn); }     /* amber */
.board-card.status-completed { border-left-color: var(--warn); }  /* amber — stand-in for `closed` */
.board-card.status-cancelled { border-left-color: var(--danger); } /* red — closed early */
.board-card.status-archived { border-left-color: var(--muted); }   /* grey — off the board */

@media (max-width: 1200px) {
  .board-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 900px) {
  .board-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  .board-grid { grid-template-columns: 1fr; }
}

/* The Archived Jobs list below the live grid (2026-10-05): a settled job leaves
   the board and the grid but must stay reachable, so it gets its own bordered
   section rather than a filter state nobody remembers to set. Scoped to the
   hook so it never touches the live grid's own forms. */
.archived-jobs {
  margin-top: 1.5rem;
  padding-top: 1rem;
  border-top: 1px solid var(--line);
}
.archived-jobs table.grid td form { margin: 0 .5rem 0 0; }
.archived-jobs table.grid input {
  width: 9rem;
  padding: .3rem .45rem;
  font-size: .85rem;
}

/* The closing checklist (2026-10-05): a fixed list on a completed job — as
   checkboxes on the job page, as plain lines on a completed board card. A done
   item is struck through. Colours come from the palette; nothing new is
   introduced. The checkbox keeps its natural width: the generic control rule
   gives every input 100%. */
.checklist {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: .9rem;
}
.checklist li { padding: .15rem 0; }
.checklist label {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin: 0;
}
.checklist input { width: auto; }
.checklist-done {
  color: var(--muted);
  text-decoration: line-through;
}

/* The file manager's row actions (2026-10-05). The generic control rule gives
   every input and button 100% width, which would blow the Actions cell apart;
   scope a compact size to this page's own hook, the way the archived-jobs
   controls do. */
.file-actions form { margin: 0 .4rem 0 0; display: inline; }
.file-actions input {
  width: 10rem;
  padding: .3rem .45rem;
  font-size: .85rem;
}
.file-actions button { width: auto; padding: .35rem .6rem; font-size: .85rem; }
