:root {
  --bg: #f4f6f9; --panel: #fff; --ink: #1f2937; --muted: #6b7280;
  --line: #e5e7eb; --primary: #1d4ed8; --primary-d: #1e40af;
  --ok: #16a34a; --bad: #c0392b; --warn: #d97706;
  --right: #c0392b; --left: #2563eb;
  --shadow: 0 1px 3px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.04);
}
* { box-sizing: border-box; }
body { margin: 0; font: 14px/1.5 -apple-system, "Segoe UI", Roboto, Arial, sans-serif; color: var(--ink); background: var(--bg); }
#app { display: flex; min-height: 100vh; }
a { color: var(--primary); text-decoration: none; }
a:hover { text-decoration: underline; }
h1 { font-size: 22px; margin: 0; }
h2 { font-size: 16px; margin: 0 0 12px; }
h3 { font-size: 13px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin: 18px 0 8px; }
.muted { color: var(--muted); }
.bad { color: var(--bad); } .ok { color: var(--ok); }
/* Not `.warn`: that is already taken by `.pill.warn`, which sets --warn as a
   BACKGROUND. A bare `.warn { color }` rule would reach those pills too and
   paint their text the same colour as the pill. */
.warn-text { color: var(--warn); }

/* A credential shown on screen for the length of one handover: an invitation
   link the administrator sends themselves when the email could not be
   delivered. Monospaced and full width because it is read aloud as often as it
   is copied, and a wrapped or ellipsised link is read out wrong. */
/* Centre status. Staff rows sit directly under the centre they belong to in the
   same table rather than in a nested one, so the columns stay aligned down the
   whole page; the centre rows carry a rule above them to keep the grouping
   readable without indentation tricks. */
.status-table .status-centre > td { border-top: 2px solid var(--line); padding-top: 12px; }
.status-table tbody tr:first-child > td { border-top: 0; }
.num { font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums; }

.link-row { display: flex; gap: 8px; align-items: center; }
.link-row input { flex: 1; min-width: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }

/* ---- sidebar ---- */
.sidebar { width: 230px; background: #0f172a; color: #cbd5e1; display: flex; flex-direction: column; padding: 18px 14px; }
.brand { font-weight: 800; font-size: 20px; color: #fff; letter-spacing: .02em; }
.brand span { display: block; font-size: 11px; font-weight: 500; color: #94a3b8; letter-spacing: .18em; text-transform: uppercase; }
.brand.big { font-size: 30px; }
.sidebar nav { margin-top: 26px; display: flex; flex-direction: column; gap: 4px; flex: 1; }
.sidebar nav a { color: #cbd5e1; padding: 9px 12px; border-radius: 8px; }
.sidebar nav a:hover { background: #1e293b; text-decoration: none; }
.sidebar nav a.active { background: var(--primary); color: #fff; }
.sidebar .user { border-top: 1px solid #1e293b; padding-top: 14px; }
.role-badge { display: inline-block; font-size: 10px; padding: 2px 8px; border-radius: 999px; background: #1e293b; color: #93c5fd; text-transform: uppercase; letter-spacing: .06em; }
.uname { font-size: 13px; color: #e2e8f0; margin: 6px 0; }

/* Which testing centre the person is acting in. A plain label when they belong
   to one, a picker when they belong to several -- a doctor contracted by two
   ATCs holds one account and two memberships. */
.atc-name { font-size: 12px; color: #cbd5e1; margin-bottom: 8px; font-weight: 600; }
.atc-switch { width: 100%; margin-bottom: 8px; font-size: 12px; padding: 5px 7px;
  background: #1e293b; color: #e2e8f0; border: 1px solid #334155; border-radius: 6px; }
.atc-switch:focus { outline: 2px solid #3b82f6; }
.link { background: none; border: none; color: #93c5fd; cursor: pointer; padding: 0; font-size: 12px; }

/* ---- content ---- */
.content { flex: 1; padding: 26px 32px; max-width: 1200px; }
.page-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
.card { background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 18px 20px; box-shadow: var(--shadow); margin-bottom: 18px; }
.card.disabled { opacity: .6; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; align-items: start; }
/* Report pages put a statutory document beside a narrow controls column. An
   even 1fr/1fr split squeezes the document to ~340px on a 1000px laptop, and
   because .report-preview scrolls, the grid is happy to shrink it that far —
   the content stays reachable but has to be scrolled sideways to be read.
   Give the document a floor instead, and drop to one column below it. */
.grid2.doc-grid { grid-template-columns: minmax(520px, 3fr) minmax(240px, 1fr); }
@media (max-width: 1180px) { .grid2.doc-grid { grid-template-columns: 1fr; } }

/* ---- stats ---- */
.stats { display: flex; gap: 16px; margin-bottom: 18px; flex-wrap: wrap; }
.stat { background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 16px 22px; box-shadow: var(--shadow); min-width: 120px; }
/* A stat card that is a link. It must not pick up link colouring -- the number
   already carries its own meaning, and a blue count reads as decoration. */
a.stat { display: block; color: inherit; text-decoration: none; }
a.stat:hover { border-color: var(--line-strong, var(--muted)); }
.stat .n { font-size: 28px; font-weight: 700; }
.stat .l { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }

/* ---- buttons ---- */
/* `.btn { display: inline-block }` beats the browser's own `[hidden]` rule, so
   a button hidden from JavaScript stays on screen. Restore it explicitly. */
[hidden] { display: none !important; }

.btn { display: inline-block; padding: 8px 14px; border-radius: 8px; border: 1px solid var(--line); background: #fff; cursor: pointer; font-size: 13px; color: var(--ink); }
.btn:hover { background: #f3f4f6; text-decoration: none; }
.btn.primary { background: var(--primary); color: #fff; border-color: var(--primary); }
.btn.primary:hover { background: var(--primary-d); }
.btn.sm { padding: 4px 10px; font-size: 12px; }
.btn.muted-btn { opacity: .5; }

/* ---- sidebar search ---- */
.search-wrap { position: relative; margin-top: 16px; }
#global-search { width: 100%; background: #1e293b; border: 1px solid #334155; color: #e2e8f0; padding: 8px 10px; border-radius: 8px; font-size: 13px; }
#global-search::placeholder { color: #64748b; }
.search-results { display: none; position: absolute; top: 40px; left: 0; right: 0; background: #fff; border: 1px solid var(--line); border-radius: 8px; box-shadow: var(--shadow); z-index: 20; max-height: 320px; overflow: auto; }
.search-results.show { display: block; }
.sr-h { font-size: 10px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); padding: 6px 10px 2px; }
.sr-item { display: block; padding: 7px 10px; color: var(--ink); font-size: 13px; border-bottom: 1px solid #f1f5f9; }
.sr-item:hover { background: #f3f4f6; text-decoration: none; }

/* ---- year checkboxes + comparison ---- */
.ycbs { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 12px; }
.ycb { flex-direction: row; align-items: center; gap: 6px; color: var(--ink); font-size: 13px; cursor: pointer; }
table.thr.cmp { margin: 14px 0; }
table.thr.cmp tr.sum td { background: #f8fafc; font-weight: 600; }
table.thr.cmp tr.grp td { background: #eef2f7; font-weight: 600; text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }

/* ---- sortable table headers ---- */
table.sortable thead th { cursor: pointer; user-select: none; position: relative; }
table.sortable thead th:hover { color: var(--primary); }
table.sortable thead th[data-dir="asc"]::after { content: " ▲"; font-size: 9px; }
table.sortable thead th[data-dir="desc"]::after { content: " ▼"; font-size: 9px; }
.warn-card { border-color: var(--warn); background: #fffbeb; }

/* ---- import card ---- */
.import-card { border-left: 4px solid var(--primary); }
.import-row { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-bottom: 8px; }
.import-row .inline-lbl { flex-direction: row; align-items: center; gap: 6px; color: var(--ink); }
.import-row .inline-lbl input { width: 64px; }
#import-status { margin: 6px 0; }
.import-img { max-width: 100%; border: 1px solid var(--line); border-radius: 8px; margin-top: 10px; max-height: 600px; }
.ai-panel { margin-top: 12px; border-top: 1px dashed var(--line); padding-top: 8px; }
.ai-panel summary { cursor: pointer; font-size: 13px; font-weight: 600; }
.ai-panel code { background: #f1f5f9; padding: 1px 4px; border-radius: 4px; }

/* ---- key/value table ---- */
table.kv td { padding: 6px 10px; border-bottom: 1px solid #f1f5f9; font-size: 13px; vertical-align: top; }
table.kv td:first-child { color: var(--muted); width: 38%; }

/* ---- chips (year toggle) ---- */
.chips { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.chip { padding: 6px 14px; border-radius: 999px; border: 1px solid var(--line); background: #fff; cursor: pointer; font-size: 13px; color: var(--ink); }
.chip:hover { background: #f3f4f6; }
.chip.active { background: var(--primary); color: #fff; border-color: var(--primary); }
td.actions { white-space: nowrap; display: flex; gap: 6px; }

/* ---- tables ---- */
table.data, table.thr, table.meta { width: 100%; border-collapse: collapse; font-size: 13px; }
table.data th, table.data td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--line); }
table.data th { color: var(--muted); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: .03em; }
.badge { font-size: 11px; padding: 2px 8px; border-radius: 999px; }
.b-draft { background: #f1f5f9; color: #475569; }
.b-submitted { background: #fef3c7; color: #92400e; }
.b-signed { background: #dcfce7; color: #166534; }
/* Deliberately not green. A historic record carries no clinical attestation,
   and colouring it like a signed one is exactly the confusion being removed. */
.b-historic { background: #ede9fe; color: #5b21b6; }
.b-elsewhere { background: #e0f2fe; color: #075985; }

/* ---- forms ---- */
.form-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px 18px; }
label { display: flex; flex-direction: column; font-size: 12px; color: var(--muted); gap: 4px; }
label.block { grid-column: 1 / -1; }
label.chk { flex-direction: row; align-items: center; gap: 8px; color: var(--ink); font-size: 13px; }
input, select, textarea { font: inherit; padding: 7px 9px; border: 1px solid var(--line); border-radius: 7px; background: #fff; color: var(--ink); }
input:focus, select:focus, textarea:focus { outline: 2px solid #bfdbfe; border-color: var(--primary); }
.form-actions { grid-column: 1 / -1; display: flex; gap: 10px; margin-top: 8px; }
.form-actions.sticky { position: sticky; bottom: 0; background: var(--bg); padding: 12px 0; border-top: 1px solid var(--line); }
.inline { display: flex; gap: 6px; }
.checks { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }

/* questionnaire */
.q { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px dashed var(--line); flex-wrap: wrap; }
.q .qt { flex: 1; min-width: 280px; }
.q.hard { background: #fef9c3; border-radius: 8px; padding: 8px 10px; }
.q .qd { flex: 1; min-width: 140px; }
.q .hint { font-size: 11px; color: var(--warn); }

/* ---- threshold grid ---- */
table.thr th, table.thr td { border: 1px solid var(--line); padding: 5px 6px; text-align: center; }
table.thr th { background: #f8fafc; font-size: 12px; }
table.thr .rl { text-align: left; font-weight: 600; white-space: nowrap; }
table.thr td.na { color: #cbd5e1; background: #fafafa; }
table.thr.input input { width: 52px; text-align: center; padding: 4px; }
/* A required threshold that has not been entered. Amber, not red: it is not an
   error while the technician is still in the booth typing -- it becomes one
   only at submit, which is what the state line under the table says. */
table.thr.input input[data-req].missing { border-color: var(--warn); background: #fffbeb; }

/* ---- audiogram / charts ---- */
svg.audiogram, svg.chart { max-width: 100%; height: auto; background: #fff; border: 1px solid var(--line); border-radius: 8px; }
.grid { stroke: #eef2f7; stroke-width: 1; }
.limit { stroke: #94a3b8; stroke-dasharray: 4 3; }
.ax-lbl { font-size: 10px; fill: var(--muted); }
.ax-title { font-size: 11px; fill: var(--muted); font-weight: 600; }
.ear-right.mark, .ear-right.line { stroke: var(--right); }
.ear-left.mark, .ear-left.line { stroke: var(--left); }
.mark { stroke-width: 2; fill: none; }
.line { fill: none; stroke-width: 1.5; }
.ear-right.bc { stroke: var(--right); fill: none; stroke-width: 1.5; }
.ear-left.bc { stroke: var(--left); fill: none; stroke-width: 1.5; }
.legend { display: flex; gap: 16px; font-size: 12px; color: var(--muted); margin-top: 6px; flex-wrap: wrap; align-items: center; }
.legend svg .mark, .legend svg .bc { stroke-width: 2; }

/* ---- login ---- */
.login { display: flex; align-items: center; justify-content: center; width: 100%; min-height: 100vh; }
.login-card { background: var(--panel); padding: 36px; border-radius: 16px; box-shadow: var(--shadow); width: 360px; text-align: center; }
.login-card .brand { color: var(--ink); }
.login-card .brand span { color: var(--muted); }
.role-pick { display: flex; flex-direction: column; align-items: flex-start; width: 100%; margin: 8px 0; padding: 12px 16px; border: 1px solid var(--line); border-radius: 10px; background: #fff; cursor: pointer; }
.role-pick:hover { border-color: var(--primary); background: #f8faff; }
.role-pick b { font-size: 15px; }
.role-pick span { font-size: 12px; color: var(--muted); }

/* ---- baseline audiometry picker ---- */
.baseline-picker { display: flex; flex-direction: column; gap: 6px; margin: 12px 0; max-height: 260px; overflow-y: auto; }
.baseline-pick { display: flex; gap: 10px; align-items: flex-start; padding: 8px 10px; border: 1px solid var(--line); border-radius: 8px; cursor: pointer; font-size: 13px; }
.baseline-pick:hover { border-color: var(--primary); }
.baseline-pick input { margin-top: 2px; }
.baseline-pick.is-current { border-color: var(--primary); background: #f5f8ff; }
.pill { display: inline-block; margin-left: 6px; padding: 1px 8px; border-radius: 999px; font-size: 11px; background: var(--primary); color: #fff; vertical-align: middle; }
.pill.warn { background: var(--warn); }
.pill.bad { background: var(--bad); }
.baseline-history { margin-top: 12px; font-size: 13px; }
.baseline-history summary { cursor: pointer; color: var(--muted); }
.baseline-history ul { margin: 8px 0 0; padding-left: 18px; color: var(--muted); }
.provisional { border: 1px solid var(--warn); background: #fffbeb; color: var(--warn); border-radius: 6px; padding: 8px 10px; font-size: 13px; margin: 10px 0; }

/* ---- review / docs ---- */
.report-preview { max-height: 80vh; overflow: auto; }
.ok-card { border-color: var(--ok); background: #f0fdf4; }
.cls { font-weight: 600; }
/* A classification that was not made. Neither red nor green: the record is not
   bad news and it is not good news, and colouring it either way is the mistake
   this state exists to prevent. */
.cls-none { color: var(--warn); }
.unknown { color: var(--muted); font-weight: 600; }
ul.flags { columns: 2; font-size: 13px; }

/* doc print */
.print-wrap { flex: 1; width: 100%; max-width: 840px; margin: 0 auto; padding: 20px; }
.print-toolbar { display: flex; gap: 10px; margin-bottom: 16px; }
.doc { background: #fff; padding: 40px; border: 1px solid var(--line); border-radius: 8px; }
.doc .lh { border-bottom: 2px solid var(--ink); padding-bottom: 8px; margin-bottom: 16px; }
.doc .lh h1 { font-size: 20px; }
/* The centre sits above the document title: the first question anybody filing a
   statutory report asks is which registered centre issued it. */
.doc .lh-top { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 10px; }
.doc .lh-logo { max-height: 56px; max-width: 180px; object-fit: contain; }
.doc .lh-centre { font-size: 13px; line-height: 1.4; }
.doc .lh-centre b { font-size: 15px; }
.doc .lh-reg { color: var(--muted); }
.doc .lh-missing { color: var(--warn); font-style: italic; max-width: 46em; }
.doc .lh-foot { margin-top: 28px; padding-top: 8px; border-top: 1px solid var(--line);
  font-size: 11px; color: var(--muted); }

/* A document produced by a demonstration centre. Deliberately loud, and
   deliberately NOT a faint diagonal watermark: a pale wash is the first thing a
   printer drops on a low-toner cartridge or a black-and-white setting, and the
   page it leaves behind is then a clean report on a real centre's letterhead.
   Solid ink in the reading order survives photocopying, faxing and greyscale. */
.doc .lh-specimen {
  margin: 0 0 14px; padding: 10px 14px; border: 2px solid #b91c1c; border-radius: 6px;
  background: #fef2f2; color: #7f1d1d; font-size: 12px; line-height: 1.45;
}
.doc .lh-specimen b { display: block; font-size: 14px; letter-spacing: .08em; margin-bottom: 3px; }
.doc h2 { font-size: 15px; margin-top: 18px; border-bottom: 1px solid var(--line); padding-bottom: 4px; }
.doc table.meta td { padding: 4px 8px; border-bottom: 1px solid #f1f5f9; font-size: 13px; }
.doc .sig { margin-top: 20px; padding: 10px; border: 1px solid var(--ok); border-radius: 6px; background: #f0fdf4; font-size: 13px; }
.doc .sig.draft { border-color: var(--warn); background: #fffbeb; color: var(--warn); }
.doc .date { color: var(--muted); }
.doc .sig-block { margin-top: 40px; }
.letter p { margin: 10px 0; }

/* ---- Appendix 6 document ---- */
.doc ul.a6 { margin: 6px 0; padding-left: 18px; font-size: 13px; }
.doc ul.a6 li { margin: 4px 0; }
.doc ul.a6.ticks { list-style: none; padding-left: 4px; }
.doc .ack { font-size: 13px; font-style: italic; color: var(--ink); }
.doc .sig-grid { display: flex; gap: 40px; flex-wrap: wrap; margin-top: 24px; }
.doc .sig-grid .sig-block { margin-top: 16px; flex: 1; min-width: 220px; }
.doc .sig-line { border-bottom: 1px solid var(--ink); min-height: 28px; margin-bottom: 4px; }

/* ---- in-list filter + employee acknowledgement ---- */
.list-filter { width: 100%; max-width: 420px; margin-bottom: 12px; padding: 8px 10px; border: 1px solid var(--line); border-radius: 6px; font-size: 14px; box-sizing: border-box; }
.a6-ack { margin-top: 16px; padding-top: 12px; border-top: 1px dashed var(--line); }
.a6-ack h3 { margin-top: 0; }

/* ---- presentation deck ---- */
.head-actions { display: flex; gap: 8px; }
.deck { width: 100%; }
.deck-bar { display: flex; gap: 10px; align-items: center; padding: 12px 16px; border-bottom: 1px solid var(--line); background: #fff; position: sticky; top: 0; z-index: 5; }
.deck-counter { margin-left: auto; color: var(--muted); font-size: 13px; }
.deck-stage { padding: 24px; display: flex; justify-content: center; }
.deck-slide-wrap { display: none; }
.deck-slide-wrap.show { display: block; }
.slide { width: 960px; max-width: 100%; aspect-ratio: 16 / 9; background: #fff; border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--shadow); padding: 48px 56px; box-sizing: border-box; overflow: hidden; }
.slide h1 { font-size: 40px; margin: 10px 0; }
.slide h2 { font-size: 26px; color: var(--primary); border: none; }
.slide h3 { font-size: 26px; color: var(--ink); text-transform: none; letter-spacing: 0; border-bottom: 3px solid var(--primary); padding-bottom: 8px; margin: 0 0 18px; }
.deck-chart { display: flex; justify-content: center; }
.deck-chart svg { max-height: 380px; }
.title-slide { background: #0f172a; color: #fff; display: flex; flex-direction: column; justify-content: center; }
.title-slide h1 { color: #fff; }
.title-slide h2 { color: #93c5fd; }
.title-slide .kicker { color: #94a3b8; text-transform: uppercase; letter-spacing: .15em; font-size: 13px; }
.title-slide .meta { color: #cbd5e1; margin-top: 20px; }
.kpis { display: flex; gap: 20px; margin: 30px 0; }
.kpi { flex: 1; background: #f8fafc; border: 1px solid var(--line); border-radius: 12px; padding: 24px; text-align: center; }
.kpi-n { font-size: 52px; font-weight: 800; }
.kpi-l { color: var(--muted); font-size: 15px; margin-top: 6px; }
.slide .lead { font-size: 20px; }
.deck-table { width: 100%; border-collapse: collapse; font-size: 16px; }
.deck-table th, .deck-table td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--line); }
.deck-table th { background: var(--primary); color: #fff; }
.deck-list { font-size: 19px; line-height: 1.9; }
.slide .foot { position: absolute; font-size: 11px; color: var(--muted); font-style: italic; margin-top: 24px; }

@media print {
  .no-print, .sidebar, .deck-bar { display: none !important; }
  /* print-color-adjust, or the band prints as an empty outline: browsers strip
     background colour by default and the one thing this element must not do is
     become subtle on paper. */
  .doc .lh-specimen { -webkit-print-color-adjust: exact; print-color-adjust: exact;
    border-color: #000; color: #000; }
  .print-wrap { max-width: none; padding: 0; }
  .doc { border: none; padding: 0; }
  body { background: #fff; }
  .deck-stage { padding: 0; display: block; }
  .deck-slide-wrap { display: block !important; page-break-after: always; }
  .slide { border: none; box-shadow: none; border-radius: 0; width: 100%; aspect-ratio: auto; min-height: 95vh; }
  @page { size: landscape; }
}

/* ---- narrow screens ----------------------------------------------------
 *
 * There was one breakpoint, at 900px, and all it did was collapse two grids to
 * a single column. Everything else stayed exactly as it is on a desktop --
 * including `#app { display: flex }` with a 230px sidebar, at every width. On a
 * 768px tablet that leaves 538px for the record; on a phone it leaves 145px,
 * and the tables then pushed the whole page sideways because nothing scrolls
 * inside its own box.
 *
 * Who is actually on a small screen decides how far this goes. Threshold entry
 * happens at a booth on a real machine and is not being optimised for a phone.
 * The employer tier is the opposite: an HR manager at a client company opening
 * a link to their employees' due dates, on whatever is in their hand. That tier
 * has existed since 0013 and could not use the product at all.
 */

/* Tablet and below: the sidebar stops being a column and becomes a bar. */
@media (max-width: 900px) {
  .grid2 { grid-template-columns: 1fr; }
  .form-grid { grid-template-columns: 1fr; }

  #app { flex-direction: column; min-height: 100vh; }
  .sidebar { width: auto; padding: 12px 16px; }
  .sidebar nav { margin-top: 12px; flex-direction: row; flex-wrap: wrap; gap: 4px 6px; flex: 0 0 auto; }
  .sidebar nav a { padding: 7px 11px; font-size: 13px; }
  .sidebar .user { margin-top: 12px; padding-top: 12px;
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; }
  .sidebar .user .uname { margin: 0; }
  .search-wrap { margin-top: 12px; }
  .atc-switch, .atc-name { margin-bottom: 0; }
  .atc-switch { width: auto; }
  .content { padding: 20px 16px; max-width: none; }
}

/* Phone: the tables are the problem, not the type. A statutory table has more
   columns than a phone has room for, and the choice is between scrolling it
   inside its own box and scrolling the entire page -- which hides the sidebar,
   the heading and any error message along with it. `display: block` on the
   table is what makes overflow-x apply to it; it is scoped here so nothing
   about the desktop layout changes. */
@media (max-width: 640px) {
  table.data, table.thr, table.meta {
    display: block; overflow-x: auto; -webkit-overflow-scrolling: touch;
  }
  .page-head { flex-direction: column; align-items: flex-start; gap: 8px; }
  .stat { padding: 12px 16px; min-width: 96px; }
  .stat .n { font-size: 22px; }
  .card { padding: 14px 14px; }
  .content { padding: 16px 12px; }
  .form-actions { flex-wrap: wrap; }
  /* A control that cannot be tapped accurately is not usable at all. */
  .btn { padding: 10px 14px; }
  .btn.sm { padding: 7px 11px; }
}

/* The step-up confirmation before an OHD signs. Set apart from the rest of the
   card because it asks for a credential, and a password field that looks like
   every other field on a form invites the habit of filling it without reading. */
.sign-confirm { border: 1px solid var(--warn); background: #fffbeb; border-radius: 10px;
  padding: 14px 16px; margin: 14px 0; }
.sign-confirm p { margin-top: 0; font-size: 13px; }
.sign-confirm input { width: 100%; }

/* A change that did not reach the server. Fixed to the bottom of the viewport
   and it does not fade: the failure being reported is precisely the one the old
   storage model used to hide, and a toast that disappears would hide it again. */
.write-failure { position: fixed; left: 0; right: 0; bottom: 0; z-index: 9999;
  display: flex; gap: 18px; align-items: center; justify-content: space-between;
  background: #7f1d1d; color: #fff; padding: 14px 22px; font-size: 13px;
  box-shadow: 0 -6px 24px rgba(0,0,0,.25); }
.write-failure p { margin: 4px 0 0; }
.write-failure .muted { color: #fecaca; }
.write-failure-actions { flex: 0 0 auto; }

/* About to be signed out for inactivity (item 25, finding F13).

   Amber rather than red, and above the two failure banners in the stack: this
   is the only one of the three that is not reporting a fault. Nothing has gone
   wrong and nothing is lost yet — it is a question, and it has a button that
   answers it.

   It does not fade, for the same reason the others do not, and for one more:
   the person it is addressed to is reading rather than looking at the edges of
   the screen, which is the whole reason they are about to be signed out. */
.idle-warning { position: fixed; left: 0; right: 0; bottom: 0; z-index: 10001;
  display: flex; gap: 18px; align-items: center; justify-content: space-between;
  background: #92400e; color: #fff; padding: 14px 22px; font-size: 13px;
  box-shadow: 0 -6px 24px rgba(0,0,0,.25); }
.idle-warning p { margin: 4px 0 0; }
.idle-warning .muted { color: #fde68a; }
.idle-warning .countdown { font-variant-numeric: tabular-nums; font-weight: 700; }
.idle-warning-actions { flex: 0 0 auto; }

/* An act that happened but was not written to the audit trail (finding G2).
   Same place, deliberately a different colour: nothing clinical has been lost
   here, and if this looked like the red one people would learn to dismiss both.
   It does not fade either — the remedy is to write the act down by hand, and a
   notice that vanishes gets no remedy at all. */
.audit-failure { position: fixed; left: 0; right: 0; bottom: 0; z-index: 9998;
  display: flex; gap: 18px; align-items: center; justify-content: space-between;
  background: #78350f; color: #fff; padding: 14px 22px; font-size: 13px;
  box-shadow: 0 -6px 24px rgba(0,0,0,.25); }
.audit-failure p { margin: 4px 0 0; }
.audit-failure .muted { color: #fde68a; }
.audit-failure code { background: rgba(255,255,255,.15); padding: 1px 4px; border-radius: 3px; }

/* ---- instruments & calibration ---- */
/* The calibration verdict under the test form's instrument picker. It sits
   between "helpful note" and "this save will be refused", so the three states
   are visually distinct rather than three shades of grey. */
.cal-state { margin-top: 10px; font-size: 13px; line-height: 1.45; }
.cal-state.ok { color: var(--ok); }
.cal-state.bad-note { border-left: 3px solid var(--bad); background: #fef2f2; color: var(--bad);
  padding: 8px 10px; border-radius: 0 6px 6px 0; }
.cal-state.warn-note { border-left: 3px solid var(--warn); background: #fffbeb; color: var(--warn);
  padding: 8px 10px; border-radius: 0 6px 6px 0; }
/* The manual booth/calibration pair, revealed when no registered instrument is
   chosen. `display: contents` so the two labels stay in the parent form-grid's
   columns instead of stacking inside a wrapper of their own. */
.manual-instrument { display: contents; }
.manual-instrument[hidden] { display: none; }
/* The inline editor that replaced prompt() for regulatory dates. It opens in a
   row of its own directly under the button that asked for it, so the record it
   is about stays on screen and in place -- which a modal cannot do, and which
   is the whole reason for it: a leaving date is checked against the start date
   in the row above. */
.row-editor > td { background: var(--bg); padding: 0; }
.row-editor-box { padding: 12px 14px; border-left: 3px solid var(--primary); }
.row-editor-box > b { display: block; margin-bottom: 2px; }
.row-editor-box p.muted { margin: 0 0 10px; max-width: 62ch; }
.row-editor-fields { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; }
.row-editor-fields label.block { flex: 1 1 220px; margin: 0; }
.row-editor-fields .block[data-group] { flex: 1 1 100%; }
/* The group's caption. A <label> wrapping several checkboxes would associate
   with the first of them, so the caption is a span and each checkbox keeps its
   own label. */
.row-editor-fields .field-label { display: block; font-size: 12px; color: var(--muted);
  margin-bottom: 6px; }
.row-editor-fields .chk-row { display: flex; flex-wrap: wrap; gap: 14px; }
.row-editor-live { margin-top: 10px; font-size: 13px; min-height: 1.2em; }
.row-editor-live code { background: var(--panel); padding: 1px 5px; border-radius: 4px; }
.row-editor-error { margin-top: 8px; font-size: 13px; }
.row-editor .form-actions { margin-top: 12px; }

.row-actions { white-space: nowrap; text-align: right; }
.row-actions .btn { margin-left: 6px; }
/* Four actions in one cell -- the platform's centre list -- run past the edge
   of a narrow screen, and `white-space: nowrap` above would keep them there.
   Wrapping is better than a row that scrolls away from its own centre's name. */
@media (max-width: 900px) {
  .row-actions { white-space: normal; }
  .row-actions .btn { margin: 2px 0 2px 6px; }
}

/* ---- consent ---- */
/* The language switch above the notice. A segmented pair rather than a select:
   the person choosing is often the worker reaching across the desk, and one tap
   on a visible word beats opening a dropdown of language codes. Each button is
   labelled in its OWN language -- "Bahasa Malaysia", not "Malay" -- because the
   reader who needs it is the one who cannot read the other option. */
.notice-langs { display: flex; gap: 6px; margin-bottom: 12px; flex-wrap: wrap; }
.notice-lang.active { background: var(--primary); border-color: var(--primary); color: #fff; }
.notice-body { white-space: pre-wrap; font-family: inherit; font-size: 13.5px; line-height: 1.6;
  margin: 0; max-height: 380px; overflow-y: auto; background: #f8fafc;
  border: 1px solid var(--line); border-radius: 6px; padding: 14px 16px; }
/* flex-direction is spelled out because the base `label` rule two hundred lines
   up sets `column`, and a bare `display: flex` here inherits it -- which puts
   the tick box on its own line above the words it applies to. */
.consent-choice { display: flex; flex-direction: row; gap: 10px; align-items: flex-start;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: 8px; margin-bottom: 8px;
  cursor: pointer; font-size: 13px; color: var(--ink); }
.consent-choice:hover { border-color: var(--primary); }
.consent-choice input { margin-top: 2px; flex: none; width: 16px; height: 16px; }
.consent-choice span { display: block; }
.consent-choice .muted { display: block; margin-top: 3px; font-size: 12.5px; line-height: 1.5; }
.consent-table td:first-child { width: 46%; }
/* touch-action: none is what stops a signing stroke scrolling the page on a
   booth tablet instead of drawing. Without it the canvas gets a pointerdown
   and then nothing, and the box stays empty however hard somebody signs. */
.sigpad canvas { width: 100%; max-width: 600px; height: auto; aspect-ratio: 600 / 180;
  border: 1px dashed var(--line); border-radius: 8px; background: #fff; touch-action: none;
  cursor: crosshair; display: block; }
.sigpad-actions { display: flex; gap: 12px; align-items: center; max-width: 600px;
  margin-top: 8px; justify-content: space-between; }
.sig-thumb { height: 42px; background: #fff; border: 1px solid var(--line); border-radius: 4px; }
ul.tight { margin: 8px 0; padding-left: 20px; font-size: 13px; }

/* ---- notifications ---- */
.notify-form { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); }
.notify-form .form-grid { margin-bottom: 4px; }
.notify-form .muted { font-size: 12px; margin: 6px 0 0; }

/* The platform operator inside a centre they are not staff of.
 *
 * Loud on purpose, and on every screen for the length of the visit. The whole
 * reason entering a centre is a door rather than a default is that it should
 * not be possible to forget you walked through it -- a subtle banner would
 * defeat the design within a week. */
.operator-banner {
  display: flex; gap: 16px; align-items: center; justify-content: space-between;
  margin: 0 0 18px; padding: 12px 16px; border-radius: 8px;
  background: var(--warn); color: #1f2937; font-size: 13px; line-height: 1.45;
}
.operator-banner .btn { flex: none; }

/* A centre whose records are simulated. Blue rather than the amber used for the
   operator banner and for warnings: this is not a hazard and not something to
   act on, it is a statement about what the reader is looking at, and giving it
   the warning colour would train people to ignore the warning colour. */
.demo-banner {
  margin: 0 0 18px; padding: 12px 16px; border-radius: 8px;
  border: 1px solid #1d4ed8; background: #eff6ff; color: #1e3a8a;
  font-size: 13px; line-height: 1.45;
}
.demo-banner b { display: block; font-size: 14px; margin-bottom: 2px; }

/* Confirmation of work that HAS been saved. Green, and visually quieter than
   .write-failure above -- that one reports work that is not saved and must not
   be dismissed by reflex; this one is a receipt. It carries a Dismiss button
   rather than fading on a timer, because a report number somebody is about to
   write down should not vanish while they are reaching for a pen. */
.flash {
  display: flex; gap: 16px; align-items: center; justify-content: space-between;
  margin: 0 0 18px; padding: 11px 16px; border-radius: 8px;
  border: 1px solid var(--ok); background: #f0fdf4; color: #14532d;
  font-size: 13px; line-height: 1.45;
}
.flash .btn { flex: none; }
.flash-warn { border-color: var(--warn); background: #fffbeb; color: #7c4a03; }

/* Import warnings, one per line. They used to be short codes joined with
   commas — "no IC, no threshold data found" — which fitted on a line and told
   a clinic administrator nothing they could act on. They are sentences now,
   and sentences run together are worse than a list. */
.import-warnings { margin: 4px 0 0; padding-left: 18px; color: var(--bad); font-size: 12px; }
.import-warnings li { margin: 2px 0; }
.roster-changes { margin: 0; padding-left: 18px; font-size: 13px; }
.roster-changes li { margin: 2px 0; }

/* The new-centre setup checklist (item 32). A list rather than a paragraph,
   because the point is that somebody can see what is left at a glance and go
   straight to the screen that fixes it. Done steps stay visible and go quiet:
   crossing them off is most of what makes a checklist feel finishable. */
.setup-list { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 10px; }
.setup-step { display: flex; gap: 10px; align-items: flex-start; }
.setup-step .setup-mark { flex: none; width: 18px; text-align: center; font-weight: 700; }
.setup-step.done { opacity: .55; }
.setup-step.done b { font-weight: 500; }
.setup-step .btn.sm { margin-top: 6px; }
.setup-step .pill { margin-left: 6px; }

/* An audit entry made outside the actor's own centre. */
.warn-row > td { background: color-mix(in srgb, var(--warn) 16%, transparent); }

/* In-page confirmation. See app/js/dialog.js for why ten of the twenty
 * confirm() calls became this and ten deliberately did not. */
.confirm-host { position: fixed; inset: 0; z-index: 10000; display: flex;
  align-items: center; justify-content: center; padding: 24px; }
.confirm-backdrop { position: absolute; inset: 0; background: rgba(15, 23, 42, .45); }
.confirm-panel { position: relative; max-width: 560px; width: 100%; max-height: 85vh;
  overflow: auto; background: #fff; border-radius: 10px; padding: 22px 24px;
  box-shadow: 0 20px 50px rgba(15, 23, 42, .3); }
.confirm-panel h2 { margin: 0 0 10px; font-size: 17px; }
.confirm-panel.danger { border-top: 4px solid var(--bad); }
.confirm-body { font-size: 13px; line-height: 1.55; color: var(--muted); }
.confirm-body b { color: inherit; }
.confirm-points { margin: 12px 0 4px; padding-left: 20px; font-size: 13px; line-height: 1.55; }
.confirm-points li { margin-bottom: 8px; }
.confirm-ack { display: block; margin: 14px 0 2px; font-size: 13px; }
.confirm-panel .form-actions { margin-top: 18px; }
.btn.danger { background: var(--bad); color: #fff; border-color: var(--bad); }
.btn.danger:disabled { opacity: .5; cursor: not-allowed; }

/* A checkbox group is a fieldset with a legend, not a label wrapped round the
 * lot: a <label> associates with the FIRST control inside it, so one around
 * three checkboxes named the first one twice and left the group unnamed.
 * Reset the browser's default fieldset chrome so it looks as it always did. */
.role-group { border: 0; margin: 0; padding: 0; min-inline-size: 0; }
.role-group > legend { padding: 0; font-size: 13px; color: var(--muted); }

/* The client summary report (Phase 2, app/js/summary.js). A document first:
   it is read on paper by an employer's safety officer, so the type is plain
   and the tables are dense but legible. Each section starts a new page in
   print, and so does every appended report. */
.sm-wrap { max-width: 1000px; }
.sm-doc { font-size: 13px; line-height: 1.5; }
.sm-doc h1 { font-size: 30px; margin: 4px 0 6px; }
.sm-doc h2 { font-size: 16px; }
.sm-doc h3 { font-size: 13px; margin: 14px 0 6px; }
.sm-page { padding-bottom: 28px; margin-bottom: 28px; border-bottom: 1px dashed var(--line); }
.sm-runhead { display: flex; justify-content: space-between; gap: 12px; font-size: 10px; color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em; border-bottom: 1px solid var(--line); padding-bottom: 4px; margin-bottom: 12px; }
.sm-cover-centre { display: flex; gap: 14px; align-items: flex-start; margin-bottom: 56px; }
.sm-cover-title { margin-bottom: 36px; }
.sm-kicker { font-size: 12px; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.sm-client { font-size: 20px; font-weight: 600; }
.sm-kv { border-collapse: collapse; width: 100%; max-width: 640px; }
.sm-kv td { padding: 5px 10px 5px 0; border-bottom: 1px solid #eef1f5; vertical-align: top; }
.sm-kv td:first-child { color: var(--muted); width: 210px; }
.sm-small { font-size: 11px; color: var(--muted); max-width: 72ch; }
.sm-band { padding: 8px 12px; border-radius: 6px; margin-bottom: 14px; font-size: 13px; }
.sm-band.sm-warn { background: #fffbeb; border: 1px solid var(--warn); }
.sm-band.sm-bad { background: #fef2f2; border: 1px solid var(--bad); }
.sm-t1, .sm-recs, .sm-abbr { border-collapse: collapse; width: 100%; max-width: 680px; }
.sm-t1 th, .sm-t1 td, .sm-recs th, .sm-recs td, .sm-abbr td { padding: 5px 8px; border-bottom: 1px solid #e5e9f0; text-align: left; vertical-align: top; }
.sm-t1 tr.sub td { color: var(--muted); }
.sm-t1 tr.sub td:nth-child(2) { padding-left: 24px; }
.sm-doc .num { text-align: right; font-variant-numeric: tabular-nums; }
.sm-defs dt { font-weight: 600; margin-top: 8px; }
.sm-defs dd { margin: 2px 0 0; max-width: 78ch; }
.sm-wide { overflow-x: auto; }
.sm-results { border-collapse: collapse; width: 100%; font-size: 11px; }
.sm-results th { text-align: left; background: #f1f4f8; padding: 5px 6px; vertical-align: bottom; }
.sm-results td { padding: 5px 6px; border-bottom: 1px solid #e5e9f0; vertical-align: top; }
.sm-sys { color: var(--warn); font-weight: 700; }
.sm-divider { padding-top: 120px; }
.sm-report .doc { border: none; padding: 0; }
@media print {
  .sm-page { border-bottom: none; margin-bottom: 0; break-after: page; page-break-after: always; }
  .sm-report { break-before: page; page-break-before: always; }
  .sm-results tr { break-inside: avoid; }
  .sm-results { font-size: 9px; }
}
.a4-table td { vertical-align: top; text-align: left; }
.a4-table td.a4-ans { width: 32%; }
/* The centre's accent (0039 summary_settings.accent) colours the headings and
   the rules; everything else stays the document's own ink. */
.sm-doc { --sm-accent: #1e3a5f; }
.sm-doc h1, .sm-doc h2 { color: var(--sm-accent); }
.sm-doc h2 { border-bottom-color: var(--sm-accent); }
.sm-doc .sm-kicker { color: var(--sm-accent); }
.sm-doc .sm-results th, .sm-doc .sm-t1 thead th, .sm-doc .sm-recs thead th { border-bottom: 2px solid var(--sm-accent); }
.sm-cover-photo { display: block; width: 100%; max-height: 360px; object-fit: cover; border-radius: 6px; margin: 0 0 28px; }
.sm-cert img { display: block; width: 100%; height: auto; border: 1px solid var(--line); }
@media print { .sm-cert img { max-height: 255mm; width: auto; max-width: 100%; margin: 0 auto; } }
.cert-pill { display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 12px; }
.cert-pill.ok { background: #ecfdf3; color: #166534; }
.cert-pill.warn { background: #fffbeb; color: #92400e; }
.cert-pill.bad { background: #fef2f2; color: #991b1b; }

/* Testing calendar (Phase 3) */
.day-progress { margin-top: 8px; font-size: 14px; }
.day-progress .bar { height: 8px; background: var(--line); border-radius: 999px; overflow: hidden; margin-top: 6px; }
.day-progress .bar span { display: block; height: 100%; background: var(--ok, #15803d); }
.day-row.day-tested td { opacity: .7; }
.day-row.day-absent td:nth-child(2) b { text-decoration: line-through; }
.session-emp { padding: 8px 0; border-top: 1px solid var(--line); }
.session-emp:first-of-type { border-top: 0; }
.session-emp p { margin: 4px 0; font-size: 14px; }
