  /* Read-only login: every control that would write is hidden rather than
     disabled, so the screen reads as a record to be inspected, not a form
     with everything greyed out. */
  body.read-only .primary,
  body.read-only .edit-cust-btn, body.read-only .del-cust-btn,
  body.read-only .edit-inst-btn, body.read-only .del-inst-btn,
  body.read-only .param-delete-btn, body.read-only #mdDeleteBtn,
  body.read-only .add-point-btn, body.read-only #addParamBtn,
  body.read-only button.act,
  body.read-only .obs-save-all-btn,
  body.read-only .complete-sheet-btn,
  body.read-only .obs-submit-btn,
  body.read-only .obs-approve-btn,
  body.read-only .obs-reject-btn,
  body.read-only .next-act { display: none !important; }
  body.read-only input:not([type="search"]),
  body.read-only select,
  body.read-only textarea { pointer-events: none; background: var(--sunk, #F5F8F9); }
  /* A CLOSED RECORD - an observation sheet the QM/TM has approved. Not a
     permission (that is body.read-only, which is about the role) but a state
     of the record itself: the work is done and reviewed, so the screen stops
     behaving like a form. Amending it is still possible and still allowed,
     but it has to be asked for - see the Amend button. */
  .sheet-closed input:not([type="search"]),
  .sheet-closed select,
  .sheet-closed textarea { pointer-events: none; background: var(--sunk, #F5F8F9); }
  .sheet-closed .obs-save-all-btn, .sheet-closed .obs-unc-btn,
  .sheet-closed .add-point-btn, .sheet-closed .edit-params-btn,
  .sheet-closed .param-delete-btn { display: none !important; }
  .ro-banner {
    display: none; align-items: center; gap: 8px; margin-bottom: 14px; padding: 9px 14px;
    border: 1px solid var(--line); border-left: 3px solid var(--brand); border-radius: 8px;
    font-size: var(--fs-body); color: var(--ink-soft); background: var(--brand-tint, #E3EDF1);
  }
  body.read-only .ro-banner { display: flex; }
  /* Search sits on the register it filters, not in a global bar - the count
     next to it is the point: "12 of 168" tells you the filter is working
     without you having to count rows. */
  .reg-search { display: flex; align-items: center; gap: 9px; padding: 10px 14px; margin-bottom: 12px;
      border: 1px solid var(--line); border-radius: 8px; background: var(--card, #fff); }
  .reg-search svg { width: 15px; height: 15px; color: var(--ink-faint); flex-shrink: 0; }
  .reg-search input { flex: 1; min-width: 0; border: 0; outline: 0; margin: 0; padding: 2px 0;
      font-size: var(--fs-body); background: transparent; color: var(--ink); }
  .reg-search input::-webkit-search-cancel-button { display: none; }
  .reg-search:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-tint, rgba(23,84,110,.12)); }
  .reg-count { font-size: var(--fs-label); color: var(--ink-faint); font-family: var(--mono); white-space: nowrap; }
  .reg-count.reg-none { color: var(--amber); }
  .reg-clear { appearance: none; border: 1px solid var(--line); background: transparent; color: var(--ink-soft);
      font-size: var(--fs-label); padding: 3px 10px; border-radius: 5px; cursor: pointer; }
  .reg-clear:hover { border-color: var(--brand); color: var(--brand); }
  .nav-item.nav-action { color: #9FD0E4; }
  .nav-item.nav-action:hover { background: rgba(255,255,255,0.10); color: #fff; }
  .sidebar.qs-mode .nav-item.nav-action { color: #C9B6F5; }
  .nav-group-label { font-size: var(--fs-label); letter-spacing: 0.08em; text-transform: uppercase; color: #7E9AA8; padding: 14px 10px 4px; }
  /* 8px rather than 10px, 23.09.2026: 26 nav items multiply 2px into 104px,
     which is what put the login block below the fold at 1366x768 once the
     logo had been taken as far as it sensibly goes. */
  .nav-item { padding: 5px 10px; border-radius: 8px; font-size: var(--fs-body); cursor: pointer; color: #C6D9E1; transition: background 0.12s; display: flex; align-items: center; gap: 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .nav-item svg { width: 16px; height: 16px; flex-shrink: 0; opacity: 0.85; }
  .qs-nav-card { background: linear-gradient(155deg, rgba(110,90,168,0.35), rgba(110,90,168,0.12)); border: 1px solid var(--violet); margin-top: 8px; padding-top: 12px; padding-bottom: 12px; }
  .qs-nav-card .qs-icon { width: 30px; height: 30px; border-radius: 8px; background: var(--violet); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
  .qs-nav-card .qs-icon svg { width: 15px; height: 15px; color: #fff; opacity: 1; margin: 0; }
  .qs-nav-card .qs-text .qs-title { font-size: var(--fs-body); font-weight: 600; color: #fff; }
  .qs-nav-card .qs-text .qs-sub { font-size: var(--fs-label); color: #9DA8C4; margin-top: 1px; }
  .nav-item:hover { background: rgba(255,255,255,0.06); }
  .nav-item.active { background: var(--brand); color: white; font-weight: 500; }
  .sidebar.qs-mode .nav-item.active { background: #7C3AED; }
  .sidebar.qs-mode { border-right: 3px solid #7C3AED; }
  .nav-spacer { flex: 1; }
  .nav-user { font-size: var(--fs-label); color: #7FA3B4; padding: 6px 10px; border-top: 1px solid rgba(255,255,255,0.1); }
  .nav-user b { color: #DCE8ED; display: block; font-size: var(--fs-body); margin-bottom: 2px; }
  .nav-logout { cursor: pointer; color: #E8B4B4; margin-top: 6px; font-size: var(--fs-body); }

  /* min-width:0 is what actually lets a flex child shrink below the
     intrinsic width of its content - without it `flex: 1` cannot. */
  /* 32px top and bottom was the last 29px of the 1366x768 overflow once the
     nav came out of flow, 23.09.2026. The horizontal padding is unchanged -
     it is what keeps a table off the edge of the screen. */
  main { flex: 1; min-width: 0; padding: 16px 40px; }

  h1.page-title { font-size: var(--fs-head); font-weight: 650; margin: 0 0 4px 0; letter-spacing: -0.01em; }
  p.page-sub { color: var(--ink-soft); margin: 0 0 28px 0; font-size: var(--fs-title); }

  /* ---------- Phase 1: split-screen landing ----------
     Identity on one side, the way in on the other. Stacks vertically below
     900px so a phone gets the sign-in first and the identity block beneath,
     rather than a squeezed two-column layout. */
  #loginScreen { width: 100%; min-height: 100vh; display: grid; grid-template-columns: 1fr 0.92fr;
      background: linear-gradient(160deg, var(--brand-dark) 0%, #123544 55%, #0B2833 100%); }

  .login-identity { display: flex; align-items: center; justify-content: center;
      padding: 56px 64px; color: #EAF2F5; }
  .li-inner { max-width: 600px; width: 100%; }
  .li-logo { display: block; width: 220px; max-width: 62%; height: auto; background: #fff;
      border-radius: 12px; padding: 16px 20px; margin-bottom: 30px; }
  .li-name { font-size: var(--fs-figure); line-height: 1.18; margin: 0 0 8px; font-weight: 600; letter-spacing: -0.015em; }
  .li-tagline { margin: 0 0 22px; font-size: var(--fs-body); letter-spacing: 0.10em; text-transform: uppercase;
      color: #9FC6D6; }
  .li-desc { margin: 0 0 30px; font-size: var(--fs-title); line-height: 1.45; letter-spacing: 0.09em;
      text-transform: uppercase; font-weight: 500; color: rgba(234,242,245,0.92); max-width: 30ch; }

  /* The accreditation block is deliberately quiet and factual - it is a
     statement about the laboratory, not a badge on the product. */
  .li-accred { border: 1px solid rgba(255,255,255,0.18); border-left: 3px solid #7FB9CE;
      border-radius: 8px; padding: 16px 18px; background: rgba(255,255,255,0.05); }
  .li-accred-head { font-size: var(--fs-label); letter-spacing: 0.12em; text-transform: uppercase;
      color: #9FC6D6; margin-bottom: 8px; }
  .li-accred-body { margin: 0 0 10px; font-size: var(--fs-body); line-height: 1.55; color: #EAF2F5; }
  .li-accred-facts { display: grid; grid-template-columns: auto 1fr; gap: 4px 14px; margin: 0 0 10px;
      font-size: var(--fs-body); }
  .li-accred-facts dt { color: #9FC6D6; }
  .li-accred-facts dd { margin: 0; color: #EAF2F5; }
  .li-accred-note { margin: 0; font-size: var(--fs-label); line-height: 1.5; color: rgba(234,242,245,0.62); }

  .login-access { display: flex; align-items: center; justify-content: center; padding: 48px 56px;
      background: var(--surface); }
  .la-inner { width: 100%; max-width: 420px; }
  .la-head { font-size: var(--fs-figure); font-weight: 600; color: var(--ink); margin-bottom: 6px; letter-spacing: -0.01em; }
  .la-sub { font-size: var(--fs-title); color: var(--ink-faint); margin: 0 0 30px; line-height: 1.55; }
  /* the sign-in controls carry this page, so they are sized to be reached
     comfortably rather than to match a dense form elsewhere in the app */
  .la-inner label { font-size: var(--fs-body); margin-bottom: 6px; }
  .la-inner input { font-size: var(--fs-title); padding: 13px 14px; }
  .la-inner button.primary { font-size: var(--fs-title); padding: 14px 18px; }
  .la-inner .login-hint { margin-top: 22px; }

  @media (max-width: 900px) {
    #loginScreen { grid-template-columns: 1fr; }
    .login-access { order: -1; padding: 40px 24px 32px; }
    .login-identity { padding: 32px 24px 44px; }
    .li-logo { width: 150px; margin-bottom: 20px; }
    .li-name { font-size: var(--fs-head); }
    .li-desc { margin-bottom: 22px; }
  }
  @media (max-width: 420px) {
    .li-accred-facts { grid-template-columns: 1fr; gap: 2px; }
    .li-accred-facts dt { margin-top: 6px; }
  }
  label { display: block; font-size: var(--fs-body); color: var(--ink-soft); margin: 14px 0 6px; font-weight: 500; }
  /* A TICK BOX IS NOT A TEXT FIELD, and this rule used to treat it as one.
     `input` bare gave every checkbox and radio width:100%, 10px 12px of
     padding and a border. Inside a flex row that is worse than it sounds:
     `flex: none` is `flex: 0 0 auto`, and an `auto` basis resolves from the
     WIDTH property - so the box claimed the whole row and pushed its own
     label off the right edge.

     Gurudeep found it on the F-08 masters list, 25.08.2026: four ticks each
     with a wide empty gap, every identifier clipped at "AQCAL/". It was not
     that list's fault, and it was never only that list - 17 checkbox and
     radio sites inherit this. Three had already been patched one at a time
     (`.checkbox-row input`, and two inline `style="width:auto"`), which is
     the shape of a defect being treated where it shows rather than where it
     is.

     Excluded here rather than reset afterwards, so a new checkbox anywhere in
     the app is born correct instead of waiting to be found. */
  input:not([type="checkbox"]):not([type="radio"]), select, textarea {
    width: 100%; padding: 10px 12px; border: 1px solid var(--line); border-radius: 8px;
    font-size: var(--fs-body); font-family: var(--sans); background: var(--surface); color: var(--ink);
  }
  input[type="checkbox"], input[type="radio"] {
    width: auto; flex: none; margin: 0; accent-color: var(--brand);
  }
  input:not([type="checkbox"]):not([type="radio"]):focus, select:focus, textarea:focus {
    outline: 2px solid var(--brand); outline-offset: 1px; border-color: var(--brand); }
  input[type="checkbox"]:focus-visible, input[type="radio"]:focus-visible {
    outline: 2px solid var(--brand); outline-offset: 1px; }
  button {
    font-family: var(--sans); cursor: pointer; border: none; border-radius: 8px;
    font-size: var(--fs-body); font-weight: 550; padding: 10px 16px; transition: opacity 0.12s;
  }
  button:hover { opacity: 0.88; }
  button:active { opacity: 0.75; }
  button.primary { background: var(--brand); color: white; }
  button.ghost { background: transparent; color: var(--brand); border: 1px solid var(--line); }
  /* ADD NEW SOURCE. Gurudeep, 23.09.2026: "add a term replaced by Add New
     Source keep in bold should be visible clearly highlight in diffrent
     colour". Violet rather than brand: brand is every other primary button on
     the screen, so being primary made it look like the Close beside it. Amber
     reads as a warning, danger as destructive and success as confirmation -
     violet is the only token carrying no meaning this would contradict.
     The small variant is the same colour on a tint, for the one that sits
     inline on a table heading and had been a bare ghost link. */
  button.add-source { background: var(--violet); color: #fff; font-weight: 700;
                      border: 1px solid var(--violet); letter-spacing: 0.01em; }
  button.add-source:hover { filter: brightness(1.10); }
  button.add-source.small { background: var(--violet-tint); color: var(--violet);
                            border: 1px solid rgba(110,90,168,0.35); font-weight: 700; }
  button.add-source.small:hover { background: var(--violet); color: #fff; }
  button:disabled { opacity: 0.4; cursor: not-allowed; }
  .la-inner button.primary { width: 100%; margin-top: 22px; }
  .login-error { color: var(--danger); font-size: var(--fs-body); margin-top: 12px; }
  .login-hint { font-size: var(--fs-label); color: var(--ink-faint); margin-top: 18px; line-height: 1.5; }

  .task-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 14px; margin-bottom: 32px; }
  .task-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 18px 20px; cursor: pointer; transition: border-color 0.12s, transform 0.12s; }
  .task-card:hover { border-color: var(--brand); transform: translateY(-1px); }
  .task-card .count { font-family: var(--mono); font-size: var(--fs-figure); font-weight: 600; line-height: 1; }
  .task-card .label { font-size: var(--fs-body); color: var(--ink-soft); margin-top: 6px; }
  .task-card.tone-amber .count { color: var(--amber); }
  .task-card.tone-danger .count { color: var(--danger); }
  .task-card.tone-brand .count { color: var(--brand); }
  .task-card.tone-success .count { color: var(--success); }

