:root {
  --bg: #f6f7f9;
  --panel: #ffffff;
  --ink: #1b1f24;
  --muted: #66707c;
  --line: #dfe3e8;
  --accent: #1f6feb;
  --accent-ink: #ffffff;
  --warn-bg: #fff4e0;
  --warn-line: #e8b96a;
  --warn-ink: #8a5a12;
  --warn-strong: #ffe9c6;
  --warn-tint: #fffdf8;
  --bad: #b42318;
  --bad-ink: #c00016;      /* the word "Danger" on a sheet's signal line */
  --bad-bg: #fdeceb;
  --bad-line: #f3bdb8;
  --bad-tint: #fdf7f7;
  --good: #1a7f4b;
  --good-bg: #eaf6ef;
  --good-line: #b6ddc5;
  --good-strong: #166b3f;
  --vbad: #7a1610;         /* the risk matrix's top band, darker than --bad */
  --vbad-bg: #f6d7d3;
  --vbad-line: #d99a92;
  --accent-strong: #1a5fd0;
  --info-bg: #eef4ff;
  --info-line: #cfe0ff;
  --info-ink: #24478f;
  --panel-2: #fbfcfd;      /* insets: filter bands, tiles, dropzones */
  --hover: #fafbfc;        /* a table row under the pointer */
  --control: #ffffff;      /* buttons, and anything you type into */
  --line-strong: #b9c0c9;  /* the border a button takes on hover */
  --chip-bg: #eef1f5;
  --chip-ink: #48525e;
  --chip-hover: #e2e7ee;
  --toast-bg: #1b1f24;
  --toast-ink: #ffffff;
  --unplaced-bg: #c8ced5;
  --unplaced-ink: #4a5560;
  --scrim: rgba(20, 24, 30, 0.45);
  --shadow: rgba(0, 0, 0, 0.2);
  --radius: 10px;
}

/* Dark. The tokens above are the whole surface, so this block is the only
   place that has to know what dark looks like.
   `data-theme` is always set — index.html resolves "match my Mac" to a real
   value before the first paint, so this needs one selector rather than a copy
   of itself inside a prefers-color-scheme query, which would be two lists of
   colours to keep in step.
   Pictograms, the C and P badges and venue colours are deliberately absent:
   those colours carry meaning and must not shift with the theme. */
[data-theme="dark"] {
  color-scheme: dark;
  --bg: #14171a;
  --panel: #1c2024;
  --ink: #e6e9ed;
  --muted: #98a2ad;
  --line: #333941;
  --accent: #4d8ef7;
  --accent-ink: #0d1117;
  --warn-bg: #3b2f16;
  --warn-line: #7a6128;
  --warn-ink: #e8bd6b;
  --warn-strong: #4a3a1a;
  --warn-tint: #2a2416;
  --bad: #ff8a80;
  --bad-ink: #ff8a80;
  --bad-bg: #3a1f1d;
  --bad-line: #6b3a35;
  --bad-tint: #2b1d1c;
  --good: #5cc98e;
  --good-bg: #16301f;
  --good-line: #2f6b48;
  --good-strong: #3fa571;
  --vbad: #ff6f61;
  --vbad-bg: #4a1c17;
  --vbad-line: #8a423a;
  --accent-strong: #6ba2fa;
  --info-bg: #1a2740;
  --info-line: #2e4a7a;
  --info-ink: #a5c6ff;
  --panel-2: #23272c;
  --hover: #23272c;
  --control: #24292f;
  --line-strong: #4e5762;
  --chip-bg: #2a2f36;
  --chip-ink: #c3cbd4;
  --chip-hover: #343b44;
  --toast-bg: #e6e9ed;
  --toast-ink: #14171a;
  --unplaced-bg: #454d57;
  --unplaced-ink: #d4dae1;
  --scrim: rgba(0, 0, 0, 0.6);
  --shadow: rgba(0, 0, 0, 0.6);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  padding: 0 0 4rem;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
}

header { padding: 1.75rem 2rem 0.75rem; }
/* The mark keeps its own blue in both themes, like the pictograms and the
   venue badges: it is an identity, not a piece of the interface. */
.brand { display: flex; align-items: center; gap: 0.85rem; }
.logo { width: 40px; height: 40px; flex: none; border-radius: 10px; }

/* The institution's logo, top right. Constrained by height and given
   object-fit so any shape of crest, wordmark or lockup lands sensibly —
   these arrive at every aspect ratio there is. `margin-left: auto` is what
   puts it in the corner. */
.inst-logo {
  margin-left: auto; flex: none;
  max-height: 46px; max-width: 240px; width: auto; height: auto;
  object-fit: contain;
}
/* Most institution logos are dark ink on nothing, and disappear against the
   dark theme. This is the answer a brand team would give: put the mark on
   white rather than recolour it. Only in dark mode — a logo boxed in white on
   an already-white page looks like a mistake. */
[data-theme="dark"] .inst-logo.on-panel {
  background: #ffffff; padding: 0.35rem 0.5rem; border-radius: 8px;
}
@media (max-width: 700px) {
  .inst-logo { max-height: 34px; max-width: 140px; }
}
/* An inventory preview: rows to tick, and quieter lists of what will not be
   touched. */
.inv-row {
  display: grid; grid-template-columns: auto 1fr auto; gap: 0.6rem;
  align-items: start; padding: 0.45rem 0; border-top: 1px solid var(--line);
}
.inv-row:first-of-type { border-top: none; }
.inv-line { white-space: nowrap; font-size: 0.8rem; }
.inv-quiet { max-height: 11rem; overflow-y: auto; font-size: 0.88rem; }
/* One line each. Several manufacturer fields in a real library hold a full
   postal address, which turns a list of forty names into three screens. The
   whole value is still on the row's tooltip. */
.inv-quiet > div {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  padding: 0.1rem 0;
}

/* The same logo in Settings, where it is being looked at rather than worn. */
.brand-preview { padding: 0.6rem 0; }
.inst-logo-preview {
  max-height: 56px; max-width: 260px; object-fit: contain;
}
[data-theme="dark"] .inst-logo-preview.on-panel {
  background: #ffffff; padding: 0.4rem 0.55rem; border-radius: 8px;
}
h1 { margin: 0; font-size: 1.5rem; letter-spacing: -0.01em; }
h2 { margin: 0 0 0.75rem; font-size: 1.05rem; }
h3 { margin: 1rem 0 0.35rem; font-size: 0.95rem; }
.sub { margin: 0.35rem 0 0; color: var(--muted); max-width: 70ch; }

.tabs { display: flex; gap: 0.5rem; padding: 1rem 2rem 0; border-bottom: 1px solid var(--line); }
.tab {
  background: none; border: 1px solid transparent; border-bottom: none;
  padding: 0.55rem 1rem; border-radius: 8px 8px 0 0; cursor: pointer;
  font-size: 0.95rem; color: var(--muted);
}
.tab.active { background: var(--panel); border-color: var(--line); color: var(--ink); font-weight: 600; }

.panel { padding: 1.5rem 2rem; display: grid; gap: 1.25rem; }
.card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.25rem;
}

/* Settings is fourteen cards; grouped under a small label they read as six
   subjects. The group takes the panel's gap between groups and a tighter one
   inside, so the boundary between subjects is visible without a rule. */
.settings-group { display: grid; gap: 0.85rem; }
/* The six sections sit behind a row of buttons under the intro; one shows at
   a time (showSettingsSection). Sixteen cards in one scroll was a list nobody
   could find anything in. The buttons are pills rather than a second row of
   folder tabs, so they do not compete with the tab bar above them. */
.subtabs { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-top: 0.9rem; }
.subtab {
  background: none; border: 1px solid var(--line); border-radius: 999px;
  padding: 0.35rem 0.85rem; cursor: pointer; font-size: 0.9rem; color: var(--muted);
}
.subtab:hover { color: var(--ink); }
.subtab.active { background: var(--accent); border-color: transparent; color: var(--accent-ink); font-weight: 600; }
.group-label {
  margin: 0 0 -0.2rem 0.25rem; font-size: 0.78rem; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted);
}

.row { display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap; }
.row.spread { justify-content: space-between; }
/* A rule above only. Boxing the band between two lines stacks up against the
   card's own border and the filter box below it, and a screen of horizontal
   rules is most of what makes a list page feel cluttered. */
.row.toolbar { margin: 0.85rem 0 0.7rem; padding: 0.7rem 0 0; border-top: 1px solid var(--line); }
.spacer { flex: 1; }
/* A flex child that takes the room left, unlike .grow which also restyles
   its text as a field label. The kinds-of-stores rows wrapped their
   description at intrinsic width without it. */
.fill { flex: 1; min-width: 0; }
/* The kinds-of-stores list in Settings. It used .log-entry once, whose
   9.5rem first column squeezed each kind's description into a sliver. */
.kind-row { padding: 0.6rem 0; border-top: 1px solid var(--line); }
.kind-row:first-child { border-top: none; }
.grow { flex: 1; display: grid; gap: 0.25rem; font-size: 0.85rem; color: var(--muted); }
.pad { padding: 1rem 0; }

button {
  font: inherit; padding: 0.45rem 0.9rem; border-radius: 8px;
  border: 1px solid var(--line); background: var(--control);
  color: var(--ink); cursor: pointer;
}
button:hover { border-color: var(--line-strong); }
button.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
button.primary:hover { filter: brightness(1.08); }
button.ghost { border-color: transparent; color: var(--muted); }
button.danger { color: var(--bad); }
button:disabled { opacity: 0.5; cursor: not-allowed; }
button.link { border: none; background: none; color: var(--accent); padding: 0.2rem; text-decoration: underline; }

input[type=text], input[type=search], input[type=password], select {
  font: inherit; padding: 0.42rem 0.6rem; border: 1px solid var(--line);
  border-radius: 8px; background: var(--control); color: var(--ink);
  min-width: 12rem;
}
input[type=search] { min-width: 20rem; }

/* Every other box you type into. Without this they keep the browser's own
   white in dark mode — white boxes on a dark card, with black text you can
   read but that nobody would call finished. Tick boxes, radios and the colour
   picker are left to the browser, which draws them properly from
   `color-scheme` and would only be spoiled by a background of ours. */
input:not([type=checkbox]):not([type=radio]):not([type=color]),
select, textarea {
  color: var(--ink); background-color: var(--control);
}

.dropzone {
  display: block; border: 2px dashed var(--line); border-radius: var(--radius);
  padding: 1.5rem; text-align: center; color: var(--muted); cursor: pointer;
  background: var(--panel-2);
}
.dropzone.hover { border-color: var(--accent); background: var(--info-bg); }

.log { margin-top: 0.75rem; display: grid; gap: 0.35rem; font-size: 0.9rem; }
.log .ok { color: var(--good); }
.log .err { color: var(--bad); }
.log .busy { color: var(--muted); }
/* The "was that a re-issue?" question an upload can raise, indented under
   its own log line so the answer buttons read as belonging to that sheet. */
.log .refile-ask { margin: 0.1rem 0 0.4rem 1.1rem; display: grid;
                   gap: 0.35rem; justify-items: start; }
.log .refile-ask p { margin: 0; }
/* One sheet can cover several products, so the candidates are ticked rather
   than pressed — a row of tick-boxes, then one button to file the lot. */
.log .refile-ask .refile-choice { display: flex; gap: 0.4rem;
                                  align-items: baseline; font-size: 0.85rem; }

table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
th, td { text-align: left; padding: 0.5rem 0.6rem; border-bottom: 1px solid var(--line); vertical-align: top; }
/* A long unbroken token — a pasted URL, a serial with no spaces — must wrap
   rather than push the whole table sideways off the page. */
td, .log-entry, .banner, .muted { overflow-wrap: anywhere; }
td .tag, td button, td .button-link { overflow-wrap: normal; white-space: nowrap; }
.check-photo { display: block; max-width: 240px; border-radius: 8px; margin: 0.4rem 0; }
.matrix-pair { display: flex; gap: 2rem; flex-wrap: wrap; align-items: flex-start; }
.matrix-pair > div { flex: 1 1 18rem; }
/* A heading never wraps: a two-line "STOCK QUANTITY" next to one-line
   neighbours put the header row at two different heights and read as text
   sitting loose at the bottom of the row. The cells below still wrap. */
th { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); white-space: nowrap; }
tbody tr:hover { background: var(--hover); }
.w-check { width: 2rem; }
/* Sortable headings. The arrow hangs off aria-sort, so what a screen reader
   is told and what the eye sees cannot drift apart. The slot is always
   there — invisible until hovered or sorted — so a column keeps exactly the
   same width whether it is sorted or not, instead of every heading shuffling
   sideways the moment one is pressed. */
th[data-sort] { cursor: pointer; user-select: none; }
th[data-sort]:hover { color: var(--ink); }
th[data-sort]::after { content: " ↕"; opacity: 0; }
th[data-sort]:hover::after { opacity: 0.5; }
th[aria-sort="ascending"]::after { content: " ↑"; opacity: 1; }
th[aria-sort="descending"]::after { content: " ↓"; opacity: 1; }
td.cell-edit input { width: 100%; min-width: 6rem; border-color: transparent; background: transparent; }
td.cell-edit input:focus { border-color: var(--line); background: var(--control); }

.pill {
  display: inline-block; background: var(--chip-bg); color: var(--muted);
  border-radius: 999px; padding: 0.05rem 0.55rem; font-size: 0.8rem; font-weight: 600;
}
.tag {
  display: inline-block; background: var(--info-bg); color: var(--info-ink);
  border: 1px solid var(--info-line);
  border-radius: 6px; padding: 0.02rem 0.35rem; margin: 0 0.2rem 0.2rem 0; font-size: 0.78rem;
}
.tag.risk-High { background: var(--bad-bg); border-color: var(--bad-line); color: var(--bad); }
.tag.risk-Medium { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn-ink); }
.tag.risk-Low { background: var(--good-bg); border-color: var(--good-line); color: var(--good); }
.tag.risk-VeryHigh { background: var(--vbad-bg); border-color: var(--vbad-line); color: var(--vbad); font-weight: 700; }
/* A value typed by a person rather than read from the sheet. Amber, not
   red: a correction is legitimate, but it must never pass as sheet data. */
/* Quiet but present: the marker follows its value everywhere it is read,
   and a boxed amber pill repeated down a card drowned the data it flags.
   Plain small amber text keeps the warning without the noise. */
.tag.hand-tag {
  background: none; border: none; padding: 0;
  color: var(--warn-ink); font-size: 0.78rem; font-weight: 600;
}

/* The 5x5 risk matrix picker. Severity runs up the side, likelihood along
   the bottom; each cell wears its band's colour so the shape of the scale
   is visible before anything is clicked. */
.risk-matrix { display: inline-block; margin: 0.5rem 0; }
.matrix-row { display: flex; align-items: stretch; gap: 3px; margin-bottom: 3px; }
.matrix-axis {
  flex: none; width: 9.5rem; font-size: 0.78rem; color: var(--muted);
  display: flex; align-items: center; justify-content: flex-end; padding-right: 0.5rem;
}
.matrix-axis-col {
  flex: none; width: 3rem; font-size: 0.7rem; color: var(--muted);
  text-align: center; line-height: 1.2;
}
.matrix-foot { align-items: flex-start; }
.matrix-cell {
  flex: none; width: 3rem; height: 2.4rem; border-radius: 6px;
  border: 1px solid var(--line); font-weight: 600; cursor: pointer;
}
.matrix-cell.band-Low { background: var(--good-bg); border-color: var(--good-line); color: var(--good); }
.matrix-cell.band-Medium { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn-ink); }
.matrix-cell.band-High { background: var(--bad-bg); border-color: var(--bad-line); color: var(--bad); }
.matrix-cell.band-Very-high { background: var(--vbad-bg); border-color: var(--vbad-line); color: var(--vbad); }
.matrix-cell.chosen { outline: 2px solid var(--accent); outline-offset: 1px; }
.matrix-legend { font-size: 0.82rem; }
/* The "Why this score?" box. A bare textarea inside a label lays out inline
   beside the question at the browser's default size — a cramped little
   window for the sentence someone signs against. Question above, full-width
   box below. */
#modalBody label > textarea {
  display: block; width: 100%; box-sizing: border-box; margin-top: 0.3rem;
  resize: vertical; line-height: 1.45; font: inherit;
  padding: 0.42rem 0.6rem; border: 1px solid var(--line); border-radius: 8px;
}

.muted { color: var(--muted); }
.hidden { display: none !important; }
.check { display: inline-flex; gap: 0.4rem; align-items: center; }

.banner {
  margin-top: 0.9rem; padding: 0.6rem 0.85rem; border-radius: 8px;
  background: var(--warn-bg); border: 1px solid var(--warn-line); font-size: 0.9rem;
}

.modal {
  position: fixed; inset: 0; background: var(--scrim);
  display: flex; align-items: flex-start; justify-content: center; padding: 3rem 1rem; z-index: 50;
  overflow: auto;
}
.modal-inner {
  background: var(--panel); border-radius: var(--radius); padding: 1.25rem;
  width: min(860px, 100%); box-shadow: 0 20px 50px var(--shadow);
}
.modal dl { display: grid; grid-template-columns: 13rem 1fr; gap: 0.3rem 1rem; margin: 0.5rem 0 0; }
/* The in-page SDS viewer. White on purpose, whatever the theme: PDF pages
   are white, and a dark frame would flash while the sheet loads. */
.sds-frame {
  width: 100%; height: 75vh; border: 1px solid var(--line);
  border-radius: 8px; background: #fff;
}
.modal dt { color: var(--muted); font-size: 0.85rem; }
.modal dd { margin: 0; white-space: pre-wrap; }

.toast {
  position: fixed; bottom: 1.25rem; left: 50%; transform: translateX(-50%);
  background: var(--toast-bg); color: var(--toast-ink);
  padding: 0.6rem 1rem; border-radius: 8px;
  font-size: 0.9rem; z-index: 60; box-shadow: 0 8px 24px rgba(0,0,0,0.25);
}
.toast.err { background: var(--bad); color: var(--bg); }

.mapping-row { display: grid; grid-template-columns: 1fr 14rem 5rem; gap: 0.5rem; align-items: center; padding: 0.3rem 0; border-bottom: 1px solid var(--line); }
.mapping-row .label { font-size: 0.9rem; }
.mapping-row select { min-width: 0; width: 100%; }
/* The sample fill sits under all three columns — what the box would
   actually say, which is how a wrong mapping gets noticed. */
.mapping-row .mapping-preview { grid-column: 1 / -1; padding-left: 0.5rem; }
.mapping-row .mapping-preview:empty { display: none; }
.conf { font-size: 0.78rem; color: var(--muted); text-align: right; }
.conf.low { color: var(--warn-ink); }
.mapping-row.doubt { background: rgba(217, 119, 6, 0.06); }
.banner.quiet { background: transparent; border-style: dashed; }
.template-item { border: 1px solid var(--line); border-radius: 8px; padding: 0.75rem; margin-bottom: 0.6rem; }
.sign-off-row { margin-top: 0.5rem; gap: 0.5rem; align-items: center; font-size: 0.9rem; }
.sign-off-row select { width: auto; }

/* --- chemical page --- */
a.link, .link { color: var(--accent); text-decoration: underline; cursor: pointer; }
.button-link {
  display: inline-block; padding: 0.4rem 0.8rem; border: 1px solid var(--line);
  border-radius: 8px; text-decoration: none; color: var(--ink);
  background: var(--control);
}
.button-link:hover { border-color: var(--line-strong); }

.field-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 0.75rem; margin: 1rem 0 0.85rem;
}
.field-grid label { display: grid; gap: 0.2rem; font-size: 0.82rem; color: var(--muted); }
.field-grid input { min-width: 0; }

/* A pickable list of options — the Fisher product grades in the CAS dialog.
   Block labels so each choice reads as one line, radio and text together. */
.radio-row { display: block; margin: 0.35rem 0; font-size: 0.9rem; }
.radio-row input { margin-right: 0.4rem; }

.tiles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  gap: 0.6rem; margin: 0.25rem 0 0.5rem;
}
.tile {
  border: 1px solid var(--line); border-radius: 8px; padding: 0.6rem 0.7rem;
  background: var(--panel-2); display: grid; gap: 0.15rem;
}
.tile-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.tile-value { font-size: 1.05rem; font-weight: 600; }
.tile-sub { display: block; font-size: 0.75rem; font-weight: 400; color: var(--muted); }

table.compact { font-size: 0.86rem; margin-bottom: 0.5rem; }
table.compact th, table.compact td { padding: 0.35rem 0.5rem; }

.order-form {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.6rem; align-items: end; margin-bottom: 0.5rem;
}
.order-form label { display: grid; gap: 0.2rem; font-size: 0.8rem; color: var(--muted); }
.order-form input { min-width: 0; font: inherit; padding: 0.4rem 0.5rem; border: 1px solid var(--line); border-radius: 8px; }

.dropzone.small { padding: 0.85rem; font-size: 0.9rem; }
.card.err { color: var(--bad); }
#chemicalPage { display: grid; gap: 1.25rem; }

/* --- SDS revisions --- */
.log .dup { color: var(--warn-ink); }
.tag.muted-tag { background: var(--chip-bg); border-color: var(--line); color: var(--muted); }

.revision {
  border: 1px solid var(--line); border-left: 3px solid var(--line);
  border-radius: 8px; padding: 0.7rem 0.85rem; margin-bottom: 0.6rem;
}
.revision.current { border-left-color: var(--accent); background: var(--panel-2); }
.revision details { margin-top: 0.5rem; }
.revision summary { cursor: pointer; font-size: 0.88rem; color: var(--muted); }
.rev-note { font-size: 0.88rem; margin-top: 0.3rem; }

.change {
  border-top: 1px solid var(--line); padding: 0.45rem 0 0.45rem 0.6rem;
  margin-top: 0.4rem; font-size: 0.88rem;
}
.change-summary { display: flex; gap: 0.35rem; flex-wrap: wrap; margin: 0.4rem 0; }
.change.tier-classification { border-left: 3px solid var(--bad); background: var(--bad-tint); }
.change.tier-check { border-left: 3px solid var(--warn-line); background: var(--warn-tint); }
.change.tier-wording { border-left: 3px solid var(--line); }
.change-head { display: flex; gap: 0.45rem; align-items: center; flex-wrap: wrap; margin-bottom: 0.2rem; }
.change-kind {
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em;
  border-radius: 4px; padding: 0.05rem 0.35rem; border: 1px solid var(--line);
  color: var(--muted);
}
.kind-added { background: var(--good-bg); border-color: var(--good-line); color: var(--good); }
.kind-removed { background: var(--bad-bg); border-color: var(--bad-line); color: var(--bad); }
.change-before, .change-after { padding-left: 1rem; position: relative; }
.change-before { color: var(--muted); text-decoration: line-through; }
.change-before::before { content: "−"; position: absolute; left: 0; }
.change-after::before { content: "+"; position: absolute; left: 0; color: var(--good); }

.picto-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 0.4rem; margin: 0.75rem 0;
}
.picto-grid .check { padding: 0.25rem 0; }

/* Filter chips above the library table. Kept visually quiet: this is a
   secondary control that sits directly under the primary toolbar. */
.row.filters {
  gap: 0.3rem; padding: 0.55rem 0.7rem; margin-bottom: 0.9rem;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 8px;
  align-items: center;
  /* Stays put while the table scrolls, so the filters are reachable from
     anywhere in a long library rather than only from the top. */
  position: sticky; top: 0.5rem; z-index: 5;
}
.row.filters:empty { display: none; }

/* Two chip groups that narrow each other — state and type on the equipment
   list, venue and category on the tools one — are one block, not two.
   They used to be two sticky rows both pinned to the same offset, so
   scrolling drew the second one on top of the first. Sticking the pair
   instead fixes that, and reads as the one filter bar it always was. */
.filter-stack {
  position: sticky; top: 0.5rem; z-index: 5; margin-bottom: 0.9rem;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 8px;
  /* The groups sit side by side and only drop onto a second line when the
     window is too narrow for them. Two groups of four chips do not need two
     rows of a wide screen. */
  display: flex; flex-wrap: wrap; align-items: center;
}
.filter-stack > .row.filters {
  position: static; margin-bottom: 0; background: none; border: none;
  border-radius: 0; flex: 0 1 auto;
}

/* The "what needs doing" headline, inside the filter bar rather than in a
   band above it. It was repeating what the chips underneath already said, and
   in here it stays on screen while a long register scrolls past. */
.bar-status {
  display: flex; align-items: center; gap: 0.5rem; flex: 1 1 100%;
  padding: 0.5rem 0.7rem 0; font-size: 0.9rem;
}
.bar-status:empty { display: none; }
.status-mark { font-weight: 650; }
.status-mark.bad { color: var(--bad); }
.status-mark.ok { color: var(--good); }
.status-note { color: var(--muted); font-size: 0.85rem; }

.filter-label {
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted); margin-right: 0.15rem;
}

.chip {
  display: inline-flex; align-items: center; gap: 0.35rem;
  font-size: 0.8rem; line-height: 1.5; padding: 0.15rem 0.5rem;
  border-radius: 6px; border: 1px solid transparent; background: var(--chip-bg);
  color: var(--chip-ink); cursor: pointer; transition: background 0.12s, color 0.12s;
}
.chip:hover { background: var(--chip-hover); border-color: transparent; }
.chip.on { background: var(--accent); color: var(--accent-ink); }
.chip.on:hover { background: var(--accent-strong); }

.chip-count {
  font-size: 0.72rem; font-variant-numeric: tabular-nums; opacity: 0.6;
}
.chip.on .chip-count { opacity: 0.75; }

.chip-plain.on { background: var(--good); }
.chip-plain.on:hover { background: var(--good-strong); }
.chip-warn { background: var(--warn-bg); color: var(--warn-ink); }
.chip-warn:hover { background: var(--warn-strong); }
.chip-warn.on, .chip-warn.on:hover { background: var(--warn-ink); color: var(--panel); }

.chip-divider {
  width: 1px; align-self: stretch; margin: 0.1rem 0.35rem;
  background: var(--line);
}

.chip-clear {
  margin-left: auto; font-size: 0.78rem; padding: 0.15rem 0.4rem;
  border: none; background: none; color: var(--muted); text-decoration: underline;
  cursor: pointer;
}
.chip-clear:hover { color: var(--ink); }

.check.no-hazard {
  display: flex; align-items: flex-start; gap: 0.5rem; margin-top: 0.5rem;
  padding: 0.6rem 0.7rem; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel-2); font-size: 0.9rem;
}
.check.no-hazard input { margin-top: 0.25rem; }

/* Definition lists on the chemical page. Two columns, one row per field,
   and a readable measure — the values include long prose from the sheet. */
#chemicalPage dl {
  display: grid; grid-template-columns: 13rem minmax(0, 1fr);
  gap: 0; margin: 0.25rem 0 0;
}
#chemicalPage dt, #chemicalPage dd {
  padding: 0.5rem 0; border-top: 1px solid var(--line); margin: 0;
}
#chemicalPage dt {
  color: var(--muted); font-size: 0.85rem; padding-right: 1rem;
}
#chemicalPage dd { white-space: pre-wrap; max-width: 78ch; }
#chemicalPage dt:first-of-type, #chemicalPage dt:first-of-type + dd {
  border-top: none;
}

@media (max-width: 720px) {
  #chemicalPage dl { grid-template-columns: 1fr; }
  #chemicalPage dt { padding-bottom: 0; border-top: 1px solid var(--line); }
  #chemicalPage dt + dd { border-top: none; padding-top: 0.15rem; }
}

/* --- editing the hazard record --- */
.hf-form {
  display: grid; gap: 0.1rem; margin: 0.75rem 0;
  max-height: 55vh; overflow-y: auto; padding-right: 0.5rem;
}
.hf-row {
  display: grid; grid-template-columns: 13rem minmax(0, 1fr);
  gap: 0 1rem; padding: 0.45rem 0; border-top: 1px solid var(--line);
  align-items: start;
}
.hf-row:first-child { border-top: none; }
/* Section headings mirror the cards on the page behind the dialog. */
.hf-section { margin: 1.1rem 0 0.2rem; font-size: 0.95rem; }
.hf-section + .hf-row { border-top: none; }
/* The per-field reset, under the control it restores. Quiet — it is an
   escape hatch, not the main event. */
.hf-reset { margin-top: 0.3rem; font-size: 0.78rem; padding: 0.15rem 0.5rem; }
.hf-label { font-size: 0.85rem; color: var(--muted); padding-top: 0.3rem; }
.hf-help {
  display: block; font-size: 0.76rem; color: var(--muted);
  opacity: 0.85; margin-top: 0.15rem;
}
.hf-control input[type=text], .hf-control select, .hf-control textarea {
  width: 100%; min-width: 0; font: inherit;
  padding: 0.35rem 0.5rem; border: 1px solid var(--line); border-radius: 6px;
}
.hf-control textarea { resize: vertical; line-height: 1.45; }
.hf-routes { display: flex; flex-wrap: wrap; gap: 0.15rem 1rem; padding-top: 0.3rem; }

.edit-log { margin-top: 1rem; border-top: 1px solid var(--line); padding-top: 0.6rem; }
.edit-log summary { cursor: pointer; font-size: 0.88rem; color: var(--muted); }

@media (max-width: 720px) {
  .hf-row { grid-template-columns: 1fr; }
}

/* --- the change log --- */
.log-filters {
  display: flex; flex-wrap: wrap; gap: 0.4rem 0.6rem; align-items: center;
  margin: 0.75rem 0; padding: 0.6rem 0.7rem;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 8px;
}
.log-filters input[type=search] { flex: 1 1 16rem; min-width: 0; }
.log-filters select, .log-filters input[type=date] {
  font: inherit; padding: 0.3rem 0.4rem;
  border: 1px solid var(--line); border-radius: 6px; background: var(--control);
}
.log-filters .dates { font-size: 0.82rem; color: var(--muted); }
.log-filters .hidden { display: none; }

.log-entry {
  display: grid; grid-template-columns: 9.5rem minmax(0, 1fr) auto;
  gap: 0 1rem; padding: 0.6rem 0 0.6rem 0.7rem;
  border-top: 1px solid var(--line);
  border-left: 3px solid transparent;
}
.log-entry:first-child { border-top: none; }
/* One colour per area, so a run of the same kind of change reads as a block
   rather than as a wall of identical rows. */
.log-entry.cat-library     { border-left-color: #4c8bf5; }
.log-entry.cat-purchasing  { border-left-color: #6bb26b; }
.log-entry.cat-templates   { border-left-color: #b98adf; }
.log-entry.cat-learning    { border-left-color: #e0a34a; }
.log-entry.cat-documents   { border-left-color: #52b4b0; }
.log-entry.cat-log         { border-left-color: #9aa4ad; }

.log-when {
  font-size: 0.8rem; color: var(--muted); display: flex;
  flex-direction: column; padding-top: 0.1rem;
}
.log-id { opacity: 0.6; font-variant-numeric: tabular-nums; }
.log-head {
  display: flex; flex-wrap: wrap; gap: 0.35rem 0.6rem; align-items: baseline;
}
.log-cat { font-weight: 600; font-size: 0.88rem; }
/* The area in words, beside the action. The coloured border says it too,
   but only to someone who can see it and has learned which colour is which. */
.log-area {
  font-size: 0.72rem; color: var(--muted); text-transform: uppercase;
  letter-spacing: 0.04em; border: 1px solid var(--line); border-radius: 4px;
  padding: 0 0.3rem;
}
.log-who {
  font-size: 0.78rem; color: var(--muted);
  background: var(--chip-bg); border-radius: 10px; padding: 0.05rem 0.45rem;
}
.log-summary { margin-top: 0.15rem; line-height: 1.45; max-width: 82ch; }
.log-actions { display: flex; gap: 0.3rem; align-items: start; }

.log-entry.withdrawn { opacity: 0.65; }
.log-entry.withdrawn .log-summary { text-decoration: line-through; }
.log-flag {
  margin-top: 0.35rem; font-size: 0.82rem; padding: 0.35rem 0.5rem;
  border-radius: 6px; max-width: 82ch;
}
.withdrawn-flag { background: var(--bad-bg); border: 1px solid var(--bad-line); }
.amended-flag { background: var(--warn-bg); border: 1px solid var(--warn-line); }

.log-detail { margin-top: 0.3rem; }
.log-detail summary {
  cursor: pointer; font-size: 0.8rem; color: var(--muted);
}
.log-detail dl {
  display: grid; grid-template-columns: 12rem minmax(0, 1fr);
  gap: 0.15rem 0.75rem; margin: 0.4rem 0 0.2rem; font-size: 0.86rem;
}
.log-detail dt { color: var(--muted); }
.log-detail dd { margin: 0; white-space: pre-wrap; }
.log-detail-list { margin: 0; padding-left: 1.1rem; }
.log-detail-nested { grid-template-columns: 10rem minmax(0, 1fr) !important; }

.log-quote {
  margin: 0.6rem 0; padding: 0.5rem 0.7rem; border-left: 3px solid var(--line);
  background: var(--panel-2); font-size: 0.9rem;
}
.ok-banner { border-color: var(--good-line); background: var(--good-bg); }
.log-list-compact .log-entry { grid-template-columns: 9.5rem minmax(0, 1fr); }

@media (max-width: 720px) {
  .log-entry, .log-list-compact .log-entry { grid-template-columns: 1fr; }
  .log-when { flex-direction: row; gap: 0.5rem; }
  .log-detail dl { grid-template-columns: 1fr; }
}

/* --- chemicals and pyrotechnics --- */
/* The letter carries the meaning; the colour only reinforces it, so the badge
   still reads for anyone who cannot tell blue from red. */
.kind-badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.15rem; height: 1.15rem; border-radius: 50%;
  font-size: 0.68rem; font-weight: 700; line-height: 1;
  color: #fff; flex: 0 0 auto;
}
.kind-chemical { background: #2f6fd0; }
.kind-pyro { background: #c0392b; }

.cell-pick { white-space: nowrap; }
.kind-badges { display: flex; gap: 0.15rem; margin-top: 0.3rem; }
/* In a list with no tick box the badge is the whole cell, so it does not
   need pushing down to sit under one. */
td.cell-pick > .kind-badges:first-child,
td.cell-pick > .kind-badge:first-child { margin-top: 0; }
.store-card .row.spread { align-items: flex-start; }

.chip.chip-chemical.on { background: #2f6fd0; border-color: #2f6fd0; color: #fff; }
.chip.chip-pyro.on { background: #c0392b; border-color: #c0392b; color: #fff; }

/* A dialog that is a list of jobs to pick from — each its own name and a
   line saying what it is for. Same card as .kind-picker's rows, but a button
   rather than a radio: choosing one goes straight there. */
.menu-list { display: grid; gap: 0.5rem; margin: 0.75rem 0; }
.menu-item {
  display: block; width: 100%; text-align: left;
  padding: 0.6rem 0.7rem; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel-2); font: inherit; font-size: 0.9rem;
  color: var(--ink); cursor: pointer;
}
.menu-item:hover { background: var(--hover); }
.menu-item .hf-help { margin-top: 0.2rem; }

.kind-picker {
  display: grid; gap: 0.5rem; margin: 0.75rem 0;
}
.kind-picker .check {
  display: flex; align-items: flex-start; gap: 0.5rem;
  padding: 0.6rem 0.7rem; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel-2); font-size: 0.9rem;
}
.kind-picker .check input { margin-top: 0.2rem; }
.kind-picker .kind-badge { margin-top: 0.05rem; }

/* The badge sits beside the name in the page heading, which is larger than
   the library row, so it needs to grow with it rather than stay tiny. */
#chemicalPage h2 .kind-badges {
  display: inline-flex; vertical-align: middle; margin: 0 0.35rem 0 0;
}
#chemicalPage h2 .kind-badge { width: 1.4rem; height: 1.4rem; font-size: 0.8rem; }

/* --- generated risk assessments --- */
.ra-current, .ra-row {
  display: flex; justify-content: space-between; align-items: center;
  gap: 1rem; padding: 0.65rem 0; border-top: 1px solid var(--line);
}
.ra-current { border-top: none; }
.ra-list .ra-row:first-child { border-top: none; }
.ra-current .muted, .ra-row .muted { font-size: 0.84rem; }

@media (max-width: 720px) {
  .ra-current, .ra-row { flex-direction: column; align-items: flex-start; gap: 0.4rem; }
}

/* --- productions --- */
.pyro-row {
  display: grid; grid-template-columns: minmax(10rem, 2fr) 8rem 10rem auto;
  gap: 0.5rem; align-items: end; margin-bottom: 0.45rem;
}
.pyro-row label { display: grid; gap: 0.2rem; font-size: 0.8rem; color: var(--muted); }
.pyro-row input, .pyro-row select {
  font: inherit; padding: 0.4rem 0.5rem; min-width: 0;
  border: 1px solid var(--line); border-radius: 8px; background: var(--control);
}

/* The per-performance editor can be twenty rows by half a dozen columns, so
   it scrolls inside the dialog rather than pushing the buttons off-screen. */
.show-editor { max-height: 52vh; overflow: auto; margin: 0.75rem 0; }
.show-editor table { font-size: 0.85rem; }
.show-editor th { position: sticky; top: 0; background: var(--panel); z-index: 1; }
.show-editor input {
  font: inherit; font-size: 0.85rem; padding: 0.25rem 0.35rem; width: 100%;
  min-width: 6rem; border: 1px solid var(--line); border-radius: 6px;
}
.show-editor td { padding: 0.25rem 0.3rem; }

.order-form.optional-block {
  padding-top: 0.5rem; border-top: 1px dashed var(--line);
}
.batch-cell { font-size: 0.84rem; }
.out-of-date { color: var(--bad); font-weight: 600; }
.banner.subtle { background: var(--panel-2); border-color: var(--line); }

/* --- types, sizes and what is held --- */
.type-workings td { border-bottom: 1px solid var(--line); font-size: 0.8rem;
  padding-top: 0; padding-bottom: 0.45rem; }
.type-workings + tr td { border-top: none; }

.show-editor td.pair { display: table-cell; }
.show-editor td.pair input[type=number] { width: 4.5rem; min-width: 0; }
.show-editor td.pair input[type=text] { width: 7rem; min-width: 0; margin-top: 0.2rem; }
.show-past { background: var(--hover); }
.show-past td:first-child { white-space: nowrap; }

details.new-production { margin-top: 1rem; border-top: 1px solid var(--line);
  padding-top: 0.75rem; }
details.new-production summary { cursor: pointer; font-weight: 600; }

/* The productions dialog is a page in its own right, so give it the room. */
#modal .modal-inner { width: min(1100px, 100%); }

.order-form.device-block {
  padding: 0.5rem 0.7rem; margin-bottom: 0.5rem;
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 8px;
}

/* A qualifier on a field label — "inc. VAT" — that must not compete with it. */
.order-form label .hint {
  font-weight: 400; font-size: 0.72rem; opacity: 0.8;
}

.row-actions { white-space: nowrap; }
.to-fill { font-style: italic; opacity: 0.75; }
.order-form .block-note {
  grid-column: 1 / -1; margin: 0 0 0.2rem; font-size: 0.82rem;
  color: var(--muted);
}

/* --- access equipment --- */
/* The state of a piece of kit is the thing you scan for, so overdue rows get
   a bar down the side rather than only a chip you have to read. */
.eq-failed td:first-child, .eq-overdue td:first-child,
.eq-never td:first-child { border-left: 3px solid var(--bad); padding-left: 0.4rem; }
.eq-due_soon td:first-child { border-left: 3px solid var(--warn-line); padding-left: 0.4rem; }
.eq-not_in_use { opacity: 0.65; }

.inspection {
  border-top: 1px solid var(--line); padding: 0.7rem 0 0.7rem 0.7rem;
  border-left: 3px solid transparent;
}
.inspection:first-of-type { border-top: none; }
.inspection.out-fail { border-left-color: var(--bad); background: var(--bad-tint); }
.inspection.out-advisory { border-left-color: var(--warn-line); }
.inspection.out-pass { border-left-color: var(--good-line); }
.inspection .advisory { color: var(--warn-ink); }
/* A pre-use check that could not be read gets the same amber as an advisory:
   it is not a pass and must not look like one. */
.inspection.out-unclear { border-left-color: var(--warn-line); }

/* A pre-use check folds away — there can be one a day, and the questions
   underneath are only wanted when somebody is looking into one of them. */
details.inspection > summary {
  cursor: pointer; list-style: none; display: flex;
  justify-content: space-between; gap: 0.5rem;
}
details.inspection > summary::-webkit-details-marker { display: none; }
details.inspection > summary::after {
  content: "▸"; color: var(--muted); transition: transform 0.15s;
}
details.inspection[open] > summary::after { transform: rotate(90deg); }
.pre-use-answers { margin: 0.5rem 0; }
.pre-use-answers td:first-child { color: var(--muted); width: 60%; }
.pre-use-answers tr.answer-bad td { color: var(--bad); font-weight: 600; }

/* The code Microsoft asks you to type on their page. Big and spaced, because
   it is read off one screen and typed into another, often on a phone. */
.device-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1.6rem; font-weight: 700; letter-spacing: 0.18em;
  margin: 0.4rem 0; user-select: all;
}

.dropzone.warn { border-color: var(--bad); background: var(--bad-tint); }
#equipmentPage { display: grid; gap: 1.25rem; }
#eqSearch { min-width: 16rem; }

/* --- people and stores --- */
#personPage, #storePage { display: grid; gap: 1.25rem; }
#peopleSearch, #storesSearch { min-width: 15rem; }
.banner ul { margin: 0.4rem 0 0; padding-left: 1.2rem; }
.banner li { margin-bottom: 0.15rem; }

/* --- venues and tool badges --- */
.venue-badge { font-size: 0.66rem; }
.venue-badge.unplaced { background: var(--unplaced-bg); color: var(--unplaced-ink); }
.place-row {
  display: grid; grid-template-columns: minmax(9rem, 1fr) minmax(9rem, 1fr) 7rem auto;
  gap: 0.5rem; align-items: end; margin-bottom: 0.45rem;
}
.place-row label { display: grid; gap: 0.2rem; font-size: 0.8rem; color: var(--muted); }
.place-row input, .place-row select {
  font: inherit; padding: 0.4rem 0.5rem; min-width: 0;
  border: 1px solid var(--line); border-radius: 8px; background: var(--control);
}
.chip .chip-dot {
  display: inline-block; width: 0.6rem; height: 0.6rem; border-radius: 50%;
  margin-right: 0.35rem; vertical-align: baseline;
}
.chip.venue-chip.on .chip-dot { background: currentColor !important; opacity: 0.6; }

/* Tape: the colour of the roll beside its name, so the list reads like the
   shelf. White and clear need an edge or they vanish on a light page. */
.tape-swatch {
  display: inline-block; width: 1rem; height: 1rem; border-radius: 50%;
  border: 1px solid rgba(128, 128, 128, 0.45); vertical-align: middle;
}
.tape-marks { white-space: nowrap; }
.tape-marks .tape-swatch { margin-right: 0.35rem; }
.tape-symbol { vertical-align: middle; color: var(--muted); }
.chip .tape-symbol { width: 0.9rem; height: 0.9rem; margin-right: 0.3rem; }
.tape-swatch.clear {
  background: transparent; border-style: dashed;
}
.tape-swatch.earth {
  background: repeating-linear-gradient(135deg, #2f9a4e 0 3px, #f4d21a 3px 6px);
}
/* The tape form's pickers: a grid of marks and a grid of swatches. */
.mark-field { margin-top: 0.75rem; }
.mark-label { display: block; font-size: 0.9rem; margin-bottom: 0.3rem; }
.mark-grid {
  display: flex; flex-wrap: wrap; gap: 0.3rem;
}
.mark-tile {
  width: 2.1rem; height: 2.1rem; padding: 0; display: inline-flex;
  align-items: center; justify-content: center;
  border: 1px solid var(--line); border-radius: 8px; background: var(--control);
  color: var(--ink); cursor: pointer;
}
.mark-tile:hover { border-color: var(--muted); }
.mark-tile.on { border: 2px solid var(--accent); }
.mark-tile.auto { border-style: dashed; }
.mark-tile .tape-symbol { color: var(--ink); width: 18px; height: 18px; }
.mark-tile .tape-swatch { width: 1.2rem; height: 1.2rem; }
.mark-none { color: var(--muted); font-weight: 600; }
.mark-caption { display: block; font-size: 0.85rem; margin-top: 0.3rem; }
/* The Minimum column on the tape tab: a quiet button until pressed. */
.min-edit {
  font: inherit; padding: 0.15rem 0.45rem; border-radius: 6px;
  border: 1px dashed var(--line); background: transparent; color: var(--ink);
  cursor: pointer;
}
.min-edit.unset { color: var(--muted); }
.min-edit:hover { border-style: solid; background: var(--control); }
.min-input {
  font: inherit; width: 5.5rem; padding: 0.15rem 0.4rem;
  border: 1px solid var(--accent); border-radius: 6px; background: var(--control);
}
input[type=color] {
  padding: 0.15rem; height: 2.1rem; min-width: 3.5rem;
  border: 1px solid var(--line); border-radius: 8px; background: var(--control);
}

/* A destructive action sits apart from the ones you came to press, below a
   rule and with its consequence written next to it. */
.danger-footer {
  margin-top: 1rem; padding-top: 0.7rem; border-top: 1px solid var(--line);
  font-size: 0.85rem;
}
.danger-footer .muted { flex: 1 1 20rem; }

/* The sub-menu band: a way into something else, not part of this list's own
   controls, so it is separated and says what it is for. */
/* The library band's captioned groups: "Add" once, then short buttons.
   A group stays whole when the row wraps, and a hidden group takes its
   separator with it. */
.tb-group { display: inline-flex; gap: 0.4rem; align-items: center; flex-wrap: wrap; padding-right: 0.9rem; border-right: 1px solid var(--line); }
.tb-group:last-child, .tb-group.hidden { border-right: 0; padding-right: 0; }
.tb-group-name { font-size: 0.8rem; color: var(--muted); }

/* The two things this page exists for sit together on their own line, with
   the tick count opposite, rather than at the end of a queue of buttons that
   open something else. */
.selection-bar { margin: 0.85rem 0 0; }

/* One control on its own line should be its own width, not the width of the
   card. */
.landing-pick { flex: 0 1 18rem; }

/* --- reference photographs --- */
/* A square beside the heading, like a profile picture: the same shape on
   every kind of record, whatever shape the photograph was. */
.heading-with-photo { flex-wrap: nowrap; align-items: flex-start; }

.photo-badge {
  width: 4.75rem; height: 4.75rem; flex: 0 0 auto; padding: 0;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel-2); overflow: hidden; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.photo-badge img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-badge.empty {
  border-style: dashed; color: var(--muted); font-size: 0.7rem;
  line-height: 1.25; text-align: center;
}
.photo-badge:hover { border-color: var(--line-strong); }

.photo-dialog {
  display: grid; grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
  gap: 1rem; align-items: start; margin-top: 0.5rem;
}
.photo-large {
  aspect-ratio: 1 / 1; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--panel-2); overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  color: var(--muted); font-size: 0.9rem;
}
/* object-fit does the cropping, so a photo shows square even when sips could
   not make a square copy of it. */
.photo-large img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-side { display: grid; gap: 0.6rem; align-content: start; }
.photo-side p { margin: 0; }

@media (max-width: 720px) {
  .photo-dialog { grid-template-columns: 1fr; }
  .heading-with-photo { flex-wrap: wrap; }
}

/* --- the settings tab --- */
/* Pushed to the far right, away from the six tabs that are the work itself.
   A cog rather than a word: it is the only tab that is not a place things
   are kept, and it should not read as a seventh one. */
.tab-cog {
  margin-left: auto; display: inline-flex; align-items: center;
  padding: 0.5rem 0.7rem;
}
.tab-cog svg { display: block; }
.tab-cog.active svg { color: var(--accent); }

#tab-settings .card p.muted { max-width: 74ch; }

/* Who this browser is, top right after the institution's logo. */
/* Pushed into the corner itself, because the logo before it — which used
   to do the pushing — is display:none until one is uploaded, and a hidden
   element's auto margin pushes nothing. With a logo showing, it follows
   the logo at a normal gap instead. */
.session { display: flex; align-items: center; gap: 0.4rem; margin-left: auto; align-self: flex-start; font-size: 0.9rem; white-space: nowrap; }
.inst-logo:not(.hidden) ~ .session { margin-left: 1rem; }
.session .who { display: grid; line-height: 1.2; margin-right: 0.4rem; }
.session .who .muted { font-size: 0.8rem; }
@media (max-width: 700px) { .session { margin-left: 0.5rem; } .session .who .muted { display: none; } }
/* A phone. Side by side, the mark, the title block and the account box
   squeezed the title and tagline into a column three words wide and pushed
   Account and Users off the right-hand edge. So the header becomes a grid:
   the mark and the title share the first row, and everything else — the
   department line, the tagline, the logo, the account buttons — takes a
   full row of its own. The title block's <div> is dissolved (display:
   contents) so its children can be placed on the grid; above this width it
   lays out exactly as before. Lives here rather than with the header
   rules because it has to come after `.session` to win over it. */
@media (max-width: 480px) {
  header { padding: 1rem 1rem 0.5rem; }
  .brand {
    display: grid; grid-template-columns: auto minmax(0, 1fr);
    gap: 0.35rem 0.75rem; align-items: center;
  }
  .brand > div:not(.session) { display: contents; }
  .brand .inst-line, .brand .sub, .brand .inst-logo, .brand .session {
    grid-column: 1 / -1;
  }
  .brand .sub { margin-top: 0; }
  .brand .inst-logo { margin-left: 0; }
  .session, .inst-logo:not(.hidden) ~ .session {
    margin-left: 0; white-space: normal; flex-wrap: wrap; align-self: auto;
  }
}
.setup-user-form { margin-top: 0.75rem; }
.err { color: var(--bad); }

/* The title is a link back to the start page, but looks like a title. */
h1 a { color: inherit; text-decoration: none; }
h1 a:hover { text-decoration: underline; text-decoration-thickness: 1px; }
.inst-line { margin: 0.2rem 0 0; font-weight: 600; color: var(--ink); }

/* The start page: whose register this is, then a door into each register.
   Cards rather than a list of links because the blurb under each is what
   tells a colleague who has never opened the tool where to go. */
/* One grid child of the panel, so it needs the panel's gap inside it too —
   without this the hero sits flush on the cards. */
#homePage { display: grid; gap: 1.25rem; }
.home-hero { text-align: center; padding: 2.5rem 1.5rem 2rem; }
.home-hero h2 { font-size: 1.5rem; margin: 0.75rem 0 0.5rem; }
.home-hero p { margin: 0 auto; max-width: 60ch; }
.home-logo { max-height: 80px; max-width: 320px; object-fit: contain; }
[data-theme="dark"] .home-logo.on-panel {
  background: #ffffff; padding: 0.5rem 0.75rem; border-radius: 8px;
}
.home-grid {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}
.home-card {
  display: grid; gap: 0.35rem; align-content: start;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.1rem 1.25rem;
  color: inherit; text-decoration: none; font-size: 0.92rem;
}
.home-card:hover { border-color: var(--accent); }
.home-card strong { font-size: 1.02rem; }
.home-card-quiet { background: var(--panel-2); }

/* The first-time set-up: one centred column, a row of step names, then one
   card per step. The column is a grid so the steps and the card share a
   width and the panel's gap sits between them. */
#setupPage { display: grid; gap: 1.25rem; max-width: 72ch; margin: 0 auto; width: 100%; }
.setup-steps {
  display: flex; flex-wrap: wrap; gap: 0.5rem 1.25rem; list-style: none;
  margin: 0; padding: 0 0.25rem; counter-reset: step; font-size: 0.88rem;
  color: var(--muted);
}
.setup-steps li { counter-increment: step; }
.setup-steps li::before {
  content: counter(step); display: inline-grid; place-items: center;
  width: 1.4rem; height: 1.4rem; margin-right: 0.45rem; border-radius: 50%;
  border: 1px solid var(--line); font-size: 0.75rem;
}
.setup-steps li.now { color: var(--ink); font-weight: 600; }
.setup-steps li.now::before { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.setup-steps li.done::before { content: "✓"; background: var(--good-bg); border-color: var(--good-line); color: var(--good); }
.setup-card p.muted { max-width: 66ch; }
.setup-nav { margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--line); }

/* GHS pictograms on the item page. The diamond's red and the white ground are
   part of the symbol's meaning, so they are literal colours on purpose and do
   not follow the theme — same rule as the venue colours and the blue C. */
.ghs-row { display: flex; flex-wrap: wrap; gap: 1rem; margin: .5rem 0 .75rem; }
.ghs-fig { margin: 0; text-align: center; width: 92px; }
.ghs-fig .ghs { width: 84px; height: 84px; display: block; margin: 0 auto; }
.ghs-fig figcaption { font-size: .78rem; color: var(--muted); margin-top: .25rem; }
.h-statements { margin: .25rem 0 .5rem; padding-left: 1.2rem; }
.h-statements li { margin: .2rem 0; }
.signal-Danger { color: var(--bad-ink); }
.signal-Warning { color: var(--warn-ink); }

/* The whole name cell opens the item, not just the link text — the hand
   should say so. Interactive children keep their own cursors. */
td.cell-name { cursor: pointer; }

/* The count on the Access equipment tab: failed, overdue or never inspected.
   A literal red like the pictograms — it means the same thing lit or dark. */
.tab-badge {
  display: inline-block; min-width: 1.1rem; margin-left: 0.4rem;
  padding: 0.05rem 0.3rem; border-radius: 999px; text-align: center;
  font-size: 0.72rem; font-weight: 600; line-height: 1.2;
  background: #b3261e; color: #ffffff;
}

/* --- narrow screens --- */
/* The register is worked at a desk, but it does get opened on a phone in a
   workshop. No redesign: the wide tables scroll sideways inside the page
   rather than pushing the whole layout off the right edge, and the tab row
   wraps rather than clips. */
@media (max-width: 720px) {
  .tabs { flex-wrap: wrap; padding: 1rem 1rem 0; }
  .panel { padding: 1rem; }
  header { padding: 1.25rem 1rem; }
  /* A grid item is min-width:auto by default, so a wide table stretches the
     card, the panel and the whole page with it — the scroll below only bites
     once the card is allowed to be narrower than its contents. */
  .panel > *, .card { min-width: 0; }
  table { display: block; overflow-x: auto; }
  /* Without a floor the columns crush instead of scrolling: a supplier
     address renders one word per line and each row is a screen tall. Hold
     the text columns open and let the sideways scroll above do its job.
     The tick-box column is the one genuinely narrow one. */
  td, th { min-width: 7rem; }
  td.cell-pick, th.w-check { min-width: 0; }
  .cell-name { min-width: 15rem; }
  /* On a phone the library shows what a cupboard walk needs — name, amount,
     where, risk — and leaves CAS, routes, gloves and last-ordered to the
     item page. Four fewer columns is the difference between a sideways
     scroll and a list that fits. */
  #libraryTable th:nth-child(3), #libraryTable td:nth-child(3),
  #libraryTable th:nth-child(7), #libraryTable td:nth-child(7),
  #libraryTable th:nth-child(8), #libraryTable td:nth-child(8),
  #libraryTable th:nth-child(10), #libraryTable td:nth-child(10) { display: none; }
  #libraryTable .cell-name { min-width: 11rem; }
  /* Pinned filters are worth a strip of a wide screen; on a phone the
     wrapped chip rows cover half the register while it scrolls. */
  .row.filters, .filter-stack { position: static; }
}

/* Snippets from the search-inside-the-sheets dialog: the matched words are
   highlighted, and the highlight keeps its colour in both themes because it
   marks a fact about the text, not a piece of chrome. */
mark { background: #ffe08a; color: #1b1f24; padding: 0 2px; border-radius: 2px; }

/* Thumbnails in list rows: the record's own photograph, small and square,
   sat beside the name. None drawn when there is no photo. */
.thumb { width: 28px; height: 28px; object-fit: cover; border-radius: 6px;
  vertical-align: middle; margin-right: 0.45rem; }
.sub-block details { margin-top: 0.4rem; }
.sub-block summary { cursor: pointer; }

/* --- the document editors and signatures --- */
.back-link { display: inline-block; margin-bottom: 0.6rem; font-size: 0.9rem; }

/* The cover sheet as a grid of ticks. Column headings stand on end so
   sixteen of them fit; the cells are buttons because pressing one is the
   whole interaction. */
.cover-grid-wrap { overflow-x: auto; margin: 0.5rem 0; }
/* Centred, or the label sits at the cell's left edge and reads as belonging
   to the column before it — the base th rule is text-align: left. */
.cover-grid th.rot { height: 7.5rem; vertical-align: bottom; text-align: center; padding: 0 0.15rem; white-space: nowrap; }
.cover-grid th.rot span { writing-mode: vertical-rl; transform: rotate(180deg); display: inline-block; font-weight: 600; font-size: 0.78rem; }
.cover-grid td { text-align: center; }
.cover-grid td:first-child { text-align: left; min-width: 12rem; }
.cover-grid td:first-child .small { font-size: 0.75rem; }
.tick-cell {
  width: 2rem; height: 2rem; padding: 0; font-size: 1rem; line-height: 1;
  border-radius: 6px; color: var(--ink);
}
.tick-cell.unknown { color: var(--warn-ink); background: var(--warn-bg); border-color: var(--warn-line); }
.tick-cell.changed { outline: 2px solid var(--accent); outline-offset: -2px; }

/* The risk assessment as a column of boxes. */
.ra-doc { border: 1px solid var(--line); border-radius: 8px; padding: 0.5rem 0.85rem; margin: 0.6rem 0; }
.ra-doc summary { cursor: pointer; display: flex; gap: 0.75rem; align-items: baseline; }
.ra-entries { display: grid; gap: 0.6rem; margin-top: 0.6rem; }
.ra-entry { display: grid; gap: 0.2rem; }
.ra-entry-label { font-size: 0.82rem; color: var(--muted); }
.ra-entry textarea {
  width: 100%; box-sizing: border-box; resize: vertical; line-height: 1.45; font: inherit;
  padding: 0.42rem 0.6rem; border: 1px solid var(--line); border-radius: 8px;
}
.ra-entry.unmapped textarea { border-style: dashed; }
.ra-readback { display: grid; grid-template-columns: 13rem 1fr; gap: 0.35rem 1rem; margin: 0.5rem 0; }
.ra-readback dt { color: var(--muted); font-size: 0.85rem; }
.ra-readback dd { margin: 0; white-space: pre-wrap; }
.ra-links { display: flex; flex-direction: column; align-items: flex-end; gap: 0.2rem; }

/* Signatures: the pad is white whatever the theme, because the picture it
   makes goes onto white paper and should be drawn on it. */
.sig-pad { background: #ffffff; border: 1px solid var(--line-strong); border-radius: 8px; touch-action: none; max-width: 100%; display: block; }
.sig-image { height: 3rem; background: #ffffff; border: 1px solid var(--line); border-radius: 6px; padding: 0.15rem 0.4rem; }
.sig-panel { border: 1px solid var(--line); border-radius: 8px; padding: 0.7rem 0.85rem; display: grid; gap: 0.6rem; background: var(--panel-2); }
.sig-panel .field-grid { margin: 0; }
.sig-panel > label { display: grid; gap: 0.2rem; font-size: 0.82rem; color: var(--muted); }
.pin-inline { display: inline-flex; gap: 0.4rem; align-items: center; font-size: 0.85rem; color: var(--muted); }
.pin-inline input { width: 5rem; min-width: 0; }
.home-card-nudge { border-color: var(--accent); background: var(--info-bg); }
@media (max-width: 720px) {
  .ra-readback { grid-template-columns: 1fr; }
}

/* --- the Page view of a risk assessment --- */
.view-toggle { margin: 0.4rem 0 0.8rem; }
.view-toggle button[aria-selected] { min-width: 5rem; }
.pg-heading { margin: 1rem 0 0.4rem; }
/* Docked at the top of the dialog while the page scrolls under it: opaque,
   on the panel colour, with a shadow so it reads as a bar rather than a
   slab lying on the paper (which is how the first version looked). */
/* The dialog scrolls inside .modal, whose 3rem top padding is part of the
   scrollport: pinned at top: 0 the bar sat 3rem down with the page
   scrolling past in the gap above it. -3rem pins it flush with the top. */
.pg-toolbar {
  position: sticky; top: -3rem; z-index: 3; display: flex; gap: 0.25rem; align-items: center;
  flex-wrap: wrap; padding: 0.5rem 0.6rem; background: var(--panel); border: 1px solid var(--line);
  border-radius: 8px; margin-bottom: 0.6rem; box-shadow: 0 6px 18px var(--shadow);
}
.pg-toolbar button { min-width: 1.8rem; padding: 0.2rem 0.4rem; font-size: 0.82rem; }
.pg-toolbar button.b { font-weight: 700; } .pg-toolbar button.i { font-style: italic; }
.pg-toolbar button.u { text-decoration: underline; }
.pg-toolbar .muted { font-size: 0.78rem; }
.pg-colour { display: inline-flex; align-items: center; gap: 0.2rem; font-size: 0.85rem; }
.pg-colour input { width: 1.6rem; height: 1.6rem; padding: 0; border: 1px solid var(--line); border-radius: 6px; }
/* The page is paper: white and dark ink whatever the theme, since that is
   what will come out of the printer. */
.ra-page {
  background: #ffffff; color: #111; box-sizing: border-box; margin: 0 auto 1rem;
  box-shadow: 0 2px 12px var(--shadow); border: 1px solid #cfd3d8;
  font-family: Calibri, "Helvetica Neue", Arial, sans-serif; font-size: 13px; line-height: 1.3;
  /* Visible, not auto: the compulsory tabs hang off the paper's left
     edge like a sign-here flag, and an overflow clip cut them to their
     last letters on a form with a narrow margin. */
  max-width: 100%; overflow: visible;
}
.pg-p { margin: 0 0 0.35em; min-height: 1em; } .pg-p.b { font-weight: 700; }
.pg-table-wrap { margin: 0.3em 0 0.6em -28px; }
/* The signature block generation appends: a plain read-only table under the page. */
/* The approver's rows drawn in line with the form's own assessor row —
   generation adds them there; read-only on the page. */
.pg-inline-sig td { height: 1.8em; }
.pg-inline-sig td.b { font-weight: 600; }
.pg-appended { margin-top: 1em; }
.pg-appended .pg-static { width: 100%; margin: 0.3em 0; }
.pg-appended .pg-static td { height: 1.8em; }
.pg-appended .pg-static td.b { font-weight: 600; }
.pg-table { border-collapse: collapse; table-layout: fixed; }
/* The page is paper: the list's row-hover shade (dark in dark mode) has
   no business on it, and the page's own colours are fixed, not themed. */
.pg-table tbody tr:hover { background: transparent; }
.ra-page a { color: #1a4fb4; }
.pg-table td { border: 1px solid #555; padding: 2px 4px; vertical-align: top; position: relative; overflow-wrap: anywhere; }
.pg-table td.pg-gutter { border: none; width: 28px; padding: 0 2px 0 0; vertical-align: middle; text-align: right; white-space: nowrap; }
/* The gutters' + and ×: faintly there always (hidden until hover, nobody
   found them), clear when the row or table is under the pointer. */
.pg-gutter button, .pg-col-gutter button {
  display: inline-block; width: 12px; height: 12px; padding: 0; line-height: 10px; font-size: 10px;
  border-radius: 3px; border: 1px solid #999; background: #f4f4f4; color: #333; margin: 0 1px; opacity: 0.3;
}
.pg-table tr:hover .pg-gutter button, .pg-table:hover .pg-col-gutter button { opacity: 1; }
.pg-gutter .pg-row-remove:hover, .pg-col-gutter .pg-col-remove:hover { color: #b42318; border-color: #b42318; }
/* The "sign here" tab of a compulsory row: a red pointer in the page's
   margin, ending at the row gutter so it never covers the row's words,
   gone once a box is ticked. The template editor's is a quiet grey one,
   saying the row is compulsory. */
.pg-flag {
  /* z-index 2: over the resize handles, under the docked toolbar (3),
     which the tab once scrolled through. Width from the words: a fixed
     72px cut "Compulsory" short. */
  position: absolute; right: 100%; top: 50%; transform: translateY(-50%); z-index: 2;
  display: block; width: max-content; padding: 4px 16px 4px 7px; box-sizing: border-box;
  background: #d92d20; color: #fff; font: 700 9px/1.2 "Helvetica Neue", Arial, sans-serif;
  letter-spacing: 0.08em; text-transform: uppercase; text-align: left; white-space: nowrap;
  clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 50%, calc(100% - 10px) 100%, 0 100%);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25); pointer-events: none;
}
.pg-flag.answered { display: none; }
/* The approver chooser under the editor's buttons, and the card's
   pending-approval tag. */
.approver-list { display: grid; gap: 0.3rem; margin: 0.4rem 0 0.6rem; }
.approver-row { align-items: center; }
.pending-tag { background: #fff3cd; color: #7a5200; border-color: #e6c866; }
/* The approver's review: the generated document drawn as a page, to be
   read and nothing else — no handles, gutters, grips or hover outlines. */
.sign-page { margin: 0.4rem 0 0.8rem; }
.sign-page .ra-page { pointer-events: none; }
/* The gutter cell stays (the fixed table's first column is its 28px —
   hiding the cell handed that width to the first real column and drew
   it one letter wide); only its buttons go. */
.sign-page .pg-gutter button, .sign-page .pg-col-gutter, .sign-page .pg-col-resize, .sign-page .pg-row-resize,
.sign-page .pg-drag, .sign-page .pg-item-tools, .sign-page .pg-block-tools, .sign-page .pg-flag { display: none !important; }
.sign-page .pg-text, .sign-page .pg-edit { box-shadow: none; background: transparent; }
.sign-list { margin: 0.4rem 0 0.8rem; }
.pg-flag.quiet { background: #667085; box-shadow: none; }
.pg-table tr.pg-col-gutter:hover { background: transparent; }
.pg-table td.pg-col-gutter, .pg-col-gutter td { border: none; padding: 0 0 2px; text-align: center; white-space: nowrap; height: auto; vertical-align: bottom; }
.pg-wordbox-remove { margin-left: 2px; vertical-align: middle; opacity: 0.4; width: 14px; height: 14px; padding: 0; line-height: 11px; font-size: 10px; border-radius: 3px; border: 1px solid #bbb; background: #f4f4f4; color: #333; }
.pg-table td:hover .pg-wordbox-remove { opacity: 1; }
.pg-static { min-height: 1em; white-space: pre-wrap; }
.pg-words { margin-bottom: 2px; }
.pg-tick { color: #333; letter-spacing: 0.15em; }
.pg-xtick { cursor: pointer; min-height: 1.2em; border-radius: 2px; box-shadow: inset 0 0 0 1px rgba(31, 111, 235, 0.25); }
.pg-xtick:hover { background: #fbfdff; box-shadow: inset 0 0 0 2px rgba(31, 111, 235, 0.5); }
.pg-xtick.by-hand { color: #1a4fb4; font-weight: 600; }
/* A Word tick-box cell of a form's strip: the glyph sits where Word puts
   it, at the top of its narrow cell, in the box's own highlight. */
.pg-wordbox { text-align: center; letter-spacing: 0; }
.pg-wordbox .pg-glyph { font-size: 1.05em; padding: 0 1px; }
.pg-wordbox.by-hand .pg-glyph { color: #1a4fb4; }
/* A legacy form-field box: drawn where Word prints it, never clickable. */
.pg-formbox { cursor: default; box-shadow: none; }
/* The grip a blue box is dragged by: always faintly there (hidden until
   hover, nobody found it), clear when the cell is under the pointer. A
   cell that will take the drop is outlined. */
.pg-drag { float: right; cursor: grab; font-size: 0.85em; line-height: 1; padding: 0 2px; color: #1f6feb; opacity: 0.4; user-select: none; }
.pg-table td:hover .pg-drag { opacity: 0.85; }
.pg-drag:hover { opacity: 1 !important; }
.pg-drag:active { cursor: grabbing; }
.pg-table td.pg-drop { box-shadow: inset 0 0 0 2px #1f6feb; background: #eef4ff; }
/* Pictures at the size Word draws them (set inline); no cap, or a
   pictogram strip cannot be made the right size. */
/* A picture keeps its size; the column is what stops shrinking. */
.pg-pic { max-width: none; display: inline-block; vertical-align: middle; margin-right: 2px; }
.ra-page.template .pg-pic { cursor: pointer; }
.pg-edit {
  min-height: 1.3em; outline: none; white-space: pre-wrap; border-radius: 2px;
  box-shadow: inset 0 0 0 1px rgba(31, 111, 235, 0.25); padding: 1px 2px;
}
.pg-edit:focus { box-shadow: inset 0 0 0 2px rgba(31, 111, 235, 0.6); background: #fbfdff; }
.pg-edit ul, .pg-edit ol { margin: 0.1em 0 0.1em 1.3em; padding: 0; }
.pg-col-resize { position: absolute; top: 0; right: -3px; width: 6px; height: 100%; cursor: col-resize; z-index: 1; }
.pg-col-resize.edge { cursor: e-resize; }
.pg-pic-wrap { position: relative; display: inline-block; vertical-align: middle; }
.pg-pic-resize {
  position: absolute; right: -4px; bottom: -4px; width: 10px; height: 10px; cursor: nwse-resize;
  background: #1f6feb; border: 1px solid #fff; border-radius: 2px; opacity: 0.6;
}
.pg-pic-wrap:hover .pg-pic-resize { opacity: 1; }
.ra-page img.pg-pic { user-select: none; -webkit-user-drag: none; }
.pg-row-resize { position: absolute; left: 0; bottom: -3px; height: 6px; width: 100%; cursor: row-resize; z-index: 1; }
.pg-col-resize:hover, .pg-row-resize:hover { background: rgba(31, 111, 235, 0.35); }
.pg-reset { margin-left: 28px; font-size: 0.8rem; }

/* Page view: any text editable, tick boxes, the template editor's chips */
.pg-text { min-height: 1em; outline: none; white-space: pre-wrap; border-radius: 2px; padding: 1px 2px; }
.pg-text:hover { box-shadow: inset 0 0 0 1px rgba(31, 111, 235, 0.2); }
.pg-text:focus { box-shadow: inset 0 0 0 2px rgba(31, 111, 235, 0.5); background: #fbfdff; }
p.pg-text { display: block; }
.pg-tickbox { display: block; cursor: pointer; user-select: none; margin: 1px 0; }
.pg-tickbox .pg-glyph { font-size: 1.05em; }
.pg-tickbox.by-hand .pg-glyph { color: #1a4fb4; }
.pg-tickbox.pending { color: #1a4fb4; }
.pg-tick-remove { margin-left: 0.3em; width: 14px; height: 14px; padding: 0; line-height: 11px; font-size: 10px; border-radius: 3px; border: 1px solid #bbb; background: #f4f4f4; color: #333; }
/* Fill chips stay out of the way until the cell is under the pointer,
   or they crowd a strip of narrow cells. A set fill stays visible. */
.pg-chip { display: inline-block; font-size: 0.72em; line-height: 1.2; padding: 1px 6px; margin-top: 2px; border-radius: 10px; border: 1px dashed #9aa; background: #f6f7f9; color: #556; cursor: pointer; opacity: 0.25; }
.pg-table td:hover .pg-chip, .pg-chip.on { opacity: 1; }

.pg-chip.on { border-style: solid; border-color: #1f6feb; background: #eef4ff; color: #1a4fb4; display: inline-flex; align-items: center; gap: 3px; max-width: 100%; box-sizing: border-box; }
/* A long field name stays inside its cell rather than poking out of the table. */
.pg-chip-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.pg-chip-clear { flex: none; width: 13px; height: 13px; padding: 0; line-height: 10px; font-size: 10px; border-radius: 3px; border: 1px solid #9ab; background: #fff; color: #1a4fb4; }
.pg-chip-clear:hover { color: #b42318; border-color: #b42318; }
.pg-chip.quiet { cursor: default; border-style: dotted; }
.pg-block-tools { font-size: 0.78em; color: #667; margin: -0.2em 0 0.5em; opacity: 0.35; }
.pg-block-tools:hover, .pg-block-tools.top { opacity: 1; }
.pg-block-tools button.link { color: #1a4fb4; font-size: inherit; padding: 0 0.3em; }
.pg-block-tools button.link.danger { color: #b42318; }
/* The template editor's cell tools share the formatting bar: one bar. */
.pg-tool-group { display: inline-flex; gap: 0.25rem; align-items: center; flex-wrap: wrap; }
/* The cell line sits under the formatting buttons on a row of its own,
   its controls in groups (cell, fills, width, add, cell ops) with a rule
   between them. A group never breaks across lines; the row wraps between
   groups. Empty until a cell is clicked, when it shows only the hint. */
.pg-inspector { display: flex; gap: 0.35rem 0.9rem; align-items: center; flex-wrap: wrap; font-size: 0.85rem; flex-basis: 100%; }
.pg-group { display: inline-flex; gap: 0.35rem; align-items: center; white-space: nowrap; padding-right: 0.9rem; border-right: 1px solid var(--line); }
.pg-group:last-of-type { border-right: 0; padding-right: 0; }
.pg-group-name { font-size: 0.78rem; color: var(--muted); }
.pg-inspector label { display: inline-flex; gap: 0.3rem; align-items: center; color: var(--muted); }
.pg-inspector select, .pg-inspector input[type=text] { min-width: 7rem; max-width: 13rem; padding: 0.2rem 0.4rem; font-size: 0.82rem; }
.pg-inspector button { padding: 0.2rem 0.5rem; font-size: 0.82rem; }
.pg-toolbar { gap: 0.2rem; }
.pg-sep { display: inline-block; width: 1px; height: 1.4rem; background: var(--line); margin: 0 0.2rem; }
.pg-toolbar .pg-hint { flex-basis: 100%; font-size: 0.75rem; }
.pg-toolbar .spacer { display: none; }
/* One line each: the add form and the change form for a selected box.
   Wrapping onto two lines read as two things, so the labels are short
   and the controls shrink before the row breaks. */
#tplTickForm, #tplTickEdit { display: flex; gap: 0.5rem; align-items: center; flex-wrap: nowrap; width: 100%; white-space: nowrap; }
#tplTickForm select, #tplTickEdit select { max-width: 12rem; min-width: 5rem; flex: 0 1 auto; }
#tplTickForm input[type=text], #tplTickEdit input[type=text] { min-width: 5rem; max-width: 10rem; flex: 0 1 auto; }
#tplTickForm.hidden { display: none; }

/* Inserts: pictures and locked text, each its own item with its own tools */
.pg-insert { display: block; margin: 1px 0; position: relative; }
.pg-insert.txt .pg-insert-text { white-space: pre-wrap; }
.pg-insert.pending, .pg-tickbox.pending { outline: 1px dashed #1f6feb; outline-offset: 1px; }
/* One strip, one line, floating just above the item so a narrow cell
   cannot wrap it; shown while the pointer is over the item and kept up
   once the item is clicked (selected), so the × can always be reached. */
.pg-tickbox, .pg-insert { position: relative; }
.pg-item-tools {
  display: none; position: absolute; left: 0; top: -19px; z-index: 4; padding: 1px 2px;
  background: #fff; border: 1px solid #bbb; border-radius: 4px; box-shadow: 0 1px 4px rgba(0,0,0,0.15);
  white-space: nowrap; gap: 2px; align-items: center;
}
.pg-tickbox:hover .pg-item-tools, .pg-insert:hover .pg-item-tools,
.pg-tickbox.selected .pg-item-tools, .pg-insert.selected .pg-item-tools { display: inline-flex; }
.pg-tickbox.selected, .pg-insert.selected { outline: 1px solid #1f6feb; outline-offset: 1px; }
/* The cell the toolbar is about: outlined on the page, named in the toolbar */
.ra-page td.pg-cell-selected { outline: 2px solid #1f6feb; outline-offset: -2px; background: rgba(31, 111, 235, 0.07); }
/* On its own line above the controls: a heading for the row, not a
   group in it, so the row starts with the controls. */
.pg-cell-name { flex-basis: 100%; font-size: 0.8rem; color: #1f6feb; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Once a cell is selected the how-to line steps aside for the cell's own
   controls; it is back the moment nothing is selected. */
.pg-toolbar.has-cell .pg-hint { display: none; }
.pg-item-line { font-size: 0.8rem; }
.pg-item-line:empty { display: none; }
.pg-item-tools button {
  width: 16px; height: 16px; padding: 0; line-height: 13px; font-size: 10px; border-radius: 3px;
  border: 1px solid #bbb; background: #f4f4f4; color: #333;
}
.pg-item-tools input[type=color] { width: 16px; height: 16px; padding: 0; border: 1px solid #bbb; border-radius: 3px; }
#tplInsertForm { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; width: 100%; }
#tplInsertForm.hidden { display: none; }

/* Dragging items: a grip on the strip, a ghost under the pointer, a
   line where it will land. Text alignment of an item is the item's own. */
.pg-grip { cursor: grab; color: #667; padding: 0 3px; font-size: 12px; user-select: none; }
.pg-tickbox, .pg-insert { cursor: grab; }
.pg-insert.txt .pg-insert-text { cursor: grab; }
.pg-drag-ghost {
  position: fixed; z-index: 60; pointer-events: none; opacity: 0.75; background: #fff;
  border: 1px solid #1f6feb; border-radius: 4px; padding: 2px 6px; box-shadow: 0 4px 14px rgba(0,0,0,0.25);
  max-width: 260px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: #111; font-size: 13px;
}
.pg-drop-marker { height: 0; border-top: 2px solid #1f6feb; margin: 1px 0; }

/* Write controls a viewer (or a removed record's page) cannot use. Kept
   visible so the page reads the same as an editor's, with the reason as
   the tooltip. */
button.locked, button.locked:hover { opacity: 0.45; cursor: not-allowed; }

/* The Cupboards card and the walk dialog: what a storage rule objects to. */
.cupboard-table select, .cupboard-table input { width: 100%; }
.cupboard-warnings { margin: 0; padding-left: 1.2em; }
.cupboard-warnings li.bad { color: var(--bad); }
.cupboard-warnings li.warn { color: var(--warn-ink); }
.cupboard-warning-row td { padding-top: 0; border-top: 0; }
