/* ============================================================
   Monitech Operations Dashboard — Global Styles
   Move this <link> into App.razor or MainLayout.razor:
   <link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@300;400;500;600;700&family=Barlow:wght@300;400;500&display=swap" rel="stylesheet" />
   ============================================================ */

/* ── Dark theme (default) ── */
:root,
[data-theme="dark"] {
  --bg: #0d1117;
  --surface: #161b22;
  --surface2: #1c2330;
  --border: #21262d;
  --accent: #ffcc00;
  --accent2: #ffe566;
  --text: #e6edf3;
  --muted: #7d8590;
  --danger: #c0584a;
  --success: #3fb950;
  --info: #58a6ff;      /* DEV-SW-113: ticket status colour (e.g. To Do) */
  --warning: #d29922;   /* DEV-SW-113: ticket status colour (e.g. Waiting) */
  --purple: #a371f7;    /* DEV-SW-113: extra ticket status colours */
  --pink: #f778ba;
  --teal: #39c5bb;
  --orange: #f0883e;
  --sidebar-width: 220px;
}

/* ── Light theme ── */
[data-theme="light"] {
  --bg: #f6f8fa;
  --surface: #ffffff;
  --surface2: #eaeef2;
  --border: #d0d7de;
  --accent: #d49200;
  --accent2: #e6a800;
  --text: #1f2328;
  --muted: #656d76;
  --danger: #c0584a;
  --success: #1a7f37;
  --info: #0969da;      /* DEV-SW-113: ticket status colour (e.g. To Do) */
  --warning: #9a6700;   /* DEV-SW-113: ticket status colour (e.g. Waiting) */
  --purple: #8250df;    /* DEV-SW-113: extra ticket status colours */
  --pink: #bf3989;
  --teal: #1b7c83;
  --orange: #bc4c00;
  --sidebar-width: 220px;
}

*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html, body {
  height: 100%;
  background: url('/Main Background.jpg') center / cover no-repeat fixed;
  color: var(--text);
  font-family: 'Barlow', sans-serif;
  font-size: 14px;
}

  /* ── Overlay over background image ── */
  /* z-index:-1 keeps this darkening layer above the fixed background image but BEHIND all app
     content, so .layout-main no longer needs a positive z-index to sit above it. That in turn lets
     page-level modals (which are position:fixed) escape to the root stacking context and paint above
     the sidebar/topbar instead of being trapped beneath them. */
  body::before {
    content: '';
    position: fixed;
    inset: 0;
    background: rgba(32, 32, 32, 0.65);
    pointer-events: none;
    z-index: -1;
  }

[data-theme="light"] body::before {
  background: rgba(255, 255, 255, 0.35);
}

/* ── Font scale (device-level UI size) ────────────────────────────────────────
   Scales the whole interface proportionally via `zoom`, keyed by data-font-scale
   on <html> (set from localStorage by app.js). 'small' is the current/default
   size and applies no scaling; 'medium' and 'large' step it up. Persisted on the
   device, not per user. */
html[data-font-scale="medium"] body {
  zoom: 1.12;
}

html[data-font-scale="large"] body {
  zoom: 1.25;
}

/* ── Keyframes ── */
@keyframes fadeUp {
  from {
    opacity: 0;
    transform: translateY(16px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* ── Links ── */
a, .btn-link {
  color: var(--accent);
}

/* ── Blazor form validation ── */
h1:focus {
  outline: none;
}

.valid.modified:not([type=checkbox]) {
  outline: 1px solid var(--success);
}

.invalid {
  outline: 1px solid var(--danger);
}

.validation-message {
  color: var(--danger);
}

/* ── Blazor error boundary ── */
.blazor-error-boundary {
  background: rgba(192, 88, 74, 0.12);
  border-top: 1px solid rgba(192, 88, 74, 0.3);
  padding: 1rem 1rem 1rem 3.7rem;
  color: var(--danger);
  font-family: 'Barlow', sans-serif;
  font-size: 13px;
}

  .blazor-error-boundary::after {
    content: "An error has occurred."
  }

.form-floating > .form-control-plaintext::placeholder,
.form-floating > .form-control::placeholder {
  color: var(--muted);
  text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder,
.form-floating > .form-control:focus::placeholder {
  text-align: start;
}

/* A hint in an empty box must not read as something typed into it. The browser default is too close to
   real text on the dark theme, so every placeholder is muted; a page's own ::placeholder rule still wins. */
input::placeholder,
textarea::placeholder {
  color: var(--muted);
  opacity: .7;
}

/* ── Dashboard widget base ── */
/* Shared by all widget components. Widget-specific styles live in each widget's .razor.css */

.widget {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.widget-header {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 20px 24px 16px;
  border-bottom: 1px solid var(--border);
}

.widget-eyebrow {
  font-size: 9px;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--muted);
}

.widget-title {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 18px;
  font-weight: 500;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--text);
}

.widget-sub {
  font-size: 10px;
  color: var(--muted);
  letter-spacing: .1em;
  text-transform: uppercase;
  margin-left: auto;
  align-self: flex-end;
}

.widget-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
}

.widget-error {
  padding: 24px;
  font-size: 13px;
  color: var(--danger, #e55);
}

.widget-footer-link {
  display: block;
  padding: 12px 24px;
  font-size: 12px;
  color: var(--accent);
  text-decoration: none;
  border-top: 1px solid var(--border);
  transition: opacity .15s;
  margin-top: auto;
}

  .widget-footer-link:hover {
    opacity: .7;
  }

.widget-stats {
  display: flex;
  align-items: stretch;
  padding: 24px;
  gap: 0;
}

.stat-card {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  text-align: center;
}

.stat-value {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 48px;
  font-weight: 600;
  line-height: 1;
  color: var(--accent);
  letter-spacing: .02em;
}

.stat-label {
  font-size: 12px;
  font-weight: 500;
  color: var(--text);
}

.stat-sub {
  font-size: 11px;
  color: var(--muted);
}

.stat-divider {
  width: 1px;
  background: var(--border);
  margin: 0 16px;
  align-self: stretch;
}

.widget-all-clear {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 32px;
  gap: 8px;
}

.all-clear-icon {
  font-size: 32px;
  color: var(--success);
  line-height: 1;
}

.all-clear-text {
  font-size: 12px;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--success);
}

/* ── Quill rich text editor — theme overrides ── */
.rte-host {
  border: 1px solid var(--muted);
  border-radius: 4px;
  overflow: hidden;
}

  .rte-host:focus-within {
    border-color: var(--accent);
  }

/* Rich-text comment/reply composers in the PDF-markup viewer: the reply box is a flex row, so let the
   editor fill it; keep a comfortable typing area for the (toolbar-less) comment popovers. */
.reply-box .rte-comment { flex: 1; min-width: 0; }
.rte-comment .rz-html-editor-content { font-size: 13px; }

.ql-editor.rte-drag-over {
  outline: 2px dashed var(--accent);
  outline-offset: -2px;
  background: color-mix(in srgb, var(--accent) 6%, var(--surface));
}

/* Variant: use on pages where the host background is --surface so the editor recedes */
.rte-host.rte-sunken .ql-toolbar.ql-snow {
  background: var(--surface2) !important;
}

.rte-host.rte-sunken .ql-editor {
  background: var(--bg) !important;
}

/* Compact editor: starts short and grows with content (height stays auto), so a brief or empty entry
   doesn't render as a tall, mostly-empty box — used for the reports' immediate/preventative action fields. */
.rte-host.rte-compact .ql-editor {
  min-height: 44px;
}

/* A read-only rich editor can't be edited - show the default arrow cursor, not the text I-beam, so it
   doesn't look editable on hover. */
.rte-host.rte-readonly .ql-editor,
.rte-host.rte-readonly .ql-editor * {
  cursor: default;
}

.ql-toolbar.ql-snow {
  border: none !important;
  border-bottom: 1px solid var(--border) !important;
  background: var(--surface2);
  padding: 6px 8px;
  font-family: 'Barlow', sans-serif;
}

.ql-container.ql-snow {
  border: none !important;
  font-family: 'Barlow', sans-serif;
  font-size: 13px;
  height: auto;
}

.ql-editor {
  min-height: 160px;
  height: auto;
  color: var(--text);
  background: var(--surface);
}

  .ql-editor img {
    max-width: 100%;
    height: auto;
  }

/* Quill's own quill.snow.css loads after app.css and sets this placeholder to a hard-coded dark
   rgba(0,0,0,0.6) at equal specificity, so it wins by source order (dark text in dark theme).
   Prefix with .ql-container (always the editor's parent) to out-specify it and keep it theme-aware. */
.ql-container .ql-editor.ql-blank::before {
  color: var(--muted);
  font-style: normal;
}

.ql-snow .ql-stroke {
  stroke: var(--muted);
}

.ql-snow .ql-fill {
  fill: var(--muted);
}

.ql-snow .ql-picker {
  color: var(--muted);
}

.ql-snow.ql-toolbar button:hover .ql-stroke,
.ql-snow.ql-toolbar button.ql-active .ql-stroke {
  stroke: var(--accent);
}

/* ── Page layout utilities ── */

/* Body area within a page shell — fills remaining height, scrolls vertically.
   Padding matches the NCR page shell (.ncr-shell) so report headings line up. */
.page-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 24px;
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}

  .page-body::-webkit-scrollbar {
    width: 4px;
  }

  .page-body::-webkit-scrollbar-track {
    background: transparent;
  }

  .page-body::-webkit-scrollbar-thumb {
    background: var(--border);
  }

/* Styled thin scrollbar — for any element that already handles its own overflow */
.thin-scroll {
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}

  .thin-scroll::-webkit-scrollbar {
    width: 4px;
  }

  .thin-scroll::-webkit-scrollbar-track {
    background: transparent;
  }

  .thin-scroll::-webkit-scrollbar-thumb {
    background: var(--border);
  }

/* ── Global button utilities ── */
.btn-back {
  padding: 8px 18px;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
  font-family: 'Barlow', sans-serif;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color .15s, color .15s;
  white-space: nowrap;
}

  .btn-back:hover {
    border-color: var(--accent);
    color: var(--accent);
  }

/* ── Table headers — dark sticky bar (global, applies to all table views) ── */
thead th {
  background: var(--surface);
  position: sticky;
  top: 0;
  z-index: 1;
}

/* ── Sortable table headers ── */
th.sortable {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}

  th.sortable:hover {
    color: var(--accent);
  }

th.sort-active {
  color: var(--accent);
}

.sort-arrow {
  margin-left: 3px;
  font-size: 10px;
}

/* ── Shared button utilities ── */

/* Consistent corner radius for all buttons. Targets <button> elements plus any element
   carrying a btn-* class (e.g. <a class="btn-secondary">). Status badges/pills are NOT
   buttons and are intentionally excluded so they stay visually distinct. */
button, [class^="btn-"], [class*=" btn-"] {
  border-radius: 6px;
}

/* ── Reusable tab strip ───────────────────────────────────────────────────────
   A horizontal row of tabs with an accent underline on the active tab. Aligns to
   the standard 48px page gutter. Usage:
     <div class="tab-strip">
       <button class="tab-btn active">One</button>
       <button class="tab-btn">Two</button>
     </div>                                                                      */
.tab-strip {
  flex-shrink: 0;
  display: flex;
  gap: 4px;
  padding: 0 48px;
  border-bottom: 1px solid var(--border);
}

.tab-btn {
  padding: 12px 18px;
  border: none;
  background: none;
  color: var(--muted);
  font-family: 'Barlow', sans-serif;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color .15s, border-color .15s;
}

  .tab-btn:hover {
    color: var(--text);
  }

  .tab-btn.active {
    color: var(--accent);
    border-bottom-color: var(--accent);
  }

.btn-primary {
  padding: 8px 18px;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
  font-family: 'Barlow', sans-serif;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color .15s, color .15s;
}

  .btn-primary:hover,
  .btn-primary.active {
    border-color: var(--accent);
    color: var(--accent);
  }

/* Standard "+ New X" create button — accent outline that fills on hover. Matches the
   "New Announcement" button so all create actions look the same across pages. */
.btn-new {
  padding: 7px 16px;
  background: transparent;
  border: 1px solid var(--accent);
  color: var(--accent);
  font-family: 'Barlow', sans-serif;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background .15s, color .15s;
  white-space: nowrap;
}

  .btn-new:hover:not(:disabled),
  .btn-new.active {
    background: var(--accent);
    color: var(--bg);
  }

  .btn-new:disabled {
    opacity: .5;
    cursor: default;
  }

  /* Light mode: the accent-outline (amber text on a light background) is too faint, so make every
     "+ New X" create button a solid amber fill with dark text — a clear call-to-action across pages. */
  [data-theme="light"] .btn-new {
    background: var(--accent);
    color: #1a1a1a;
  }

    [data-theme="light"] .btn-new:hover:not(:disabled),
    [data-theme="light"] .btn-new.active {
      background: var(--accent2);
      color: #1a1a1a;
    }

.btn-accent {
  padding: 8px 18px;
  background: var(--accent);
  color: var(--bg);
  border: none;
  font-family: 'Barlow', sans-serif;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  cursor: pointer;
  border-radius: 6px;
  transition: filter .15s;
}

  .btn-accent:hover:not(:disabled) {
    filter: brightness(1.1);
  }

  .btn-accent:disabled {
    opacity: .5;
    cursor: default;
  }

/* Outlined accent (yellow border + text, transparent fill) */
.btn-accent-outline {
  padding: 8px 18px;
  background: transparent;
  color: var(--accent);
  border: 1px solid var(--accent);
  font-family: 'Barlow', sans-serif;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  cursor: pointer;
  border-radius: 6px;
  transition: background .15s, color .15s;
}

  .btn-accent-outline:hover:not(:disabled) {
    background: var(--accent);
    color: var(--bg);
  }

  .btn-accent-outline:disabled {
    opacity: .5;
    cursor: default;
  }

.btn-secondary {
  padding: 8px 18px;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
  font-family: 'Barlow', sans-serif;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color .15s, color .15s;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
}

  .btn-secondary:hover {
    border-color: var(--accent);
    color: var(--accent);
  }

/* Comment "Edit" action — the single global format for every comment thread (tickets, reports,
   NCRs, documents, announcements). Right-aligned within the (flex) comment header. */
.btn-comment-action {
  margin-left: auto;
  padding: 2px 8px;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
  font-family: 'Barlow', sans-serif;
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  cursor: pointer;
  border-radius: 6px;
  transition: border-color .15s, color .15s;
}

  .btn-comment-action:hover { border-color: var(--accent); color: var(--accent); }

.btn-danger {
  padding: 8px 18px;
  background: transparent;
  border: 1px solid var(--danger);
  color: var(--danger);
  font-family: 'Barlow', sans-serif;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background .15s, color .15s;
}

  .btn-danger:hover:not(:disabled) {
    background: var(--danger);
    color: #fff;
  }

  .btn-danger:disabled {
    opacity: .5;
    cursor: default;
  }

.btn-upload {
  padding: 8px 18px;
  background: transparent;
  border: 1px solid var(--text);
  color: var(--text);
  font-family: 'Barlow', sans-serif;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color .15s, color .15s, background .15s;
}

  .btn-upload:hover:not(:disabled) {
    border-color: var(--accent);
    color: var(--accent);
  }

  .btn-upload:disabled {
    opacity: .5;
    cursor: default;
  }

.btn-sm {
  padding: 5px 12px;
  font-size: 10px;
}

.ql-snow.ql-toolbar button:hover .ql-fill,
.ql-snow.ql-toolbar button.ql-active .ql-fill {
  fill: var(--accent);
}

.ql-snow.ql-toolbar button:hover,
.ql-snow.ql-toolbar button.ql-active,
.ql-snow.ql-toolbar .ql-picker-label:hover {
  color: var(--accent);
}

.ql-snow .ql-picker-options {
  background: var(--surface) !important;
  border-color: var(--border) !important;
  color: var(--text);
}

  .ql-snow .ql-picker-options .ql-picker-item:hover {
    color: var(--accent);
  }

/* ── Quill inline format classes — must work outside .ql-editor too ── */
.ql-size-small {
  font-size: 0.75em;
}

.ql-size-large {
  font-size: 1.5em;
}

.ql-size-huge {
  font-size: 2.5em;
}

.ql-font-serif {
  font-family: Georgia, serif;
}

.ql-font-monospace {
  font-family: 'Courier New', monospace;
}

/* ── @mention chips ─────────────────────────────────────────────── */
.mention {
  display: inline-block;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
  border-radius: 3px;
  padding: 0 4px;
  font-weight: 500;
  white-space: nowrap;
  cursor: default;
  user-select: text;
}

.mention-dropdown {
  position: fixed;
  z-index: 10000;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  box-shadow: 0 4px 16px rgba(0,0,0,.35);
  min-width: 200px;
  max-width: 300px;
  overflow: hidden;
}

.mention-dropdown-item {
  padding: 8px 12px;
  cursor: pointer;
  font-size: 13px;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

  .mention-dropdown-item.active,
  .mention-dropdown-item:hover {
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    color: var(--accent);
  }

.mention-dropdown-empty {
  padding: 8px 12px;
  font-size: 12px;
  color: var(--muted);
}

/* ── Rich-text content rendering ── */

.rte-content {
  line-height: 1.65;
}

  .rte-content p {
    margin: 0 0 .6em;
  }

  .rte-content h1 {
    font-size: 1.35em;
    font-weight: 600;
    margin: .8em 0 .4em;
  }

  .rte-content h2 {
    font-size: 1.15em;
    font-weight: 600;
    margin: .8em 0 .4em;
  }

  .rte-content h3 {
    font-size: 1em;
    font-weight: 600;
    margin: .8em 0 .4em;
  }

  .rte-content ul,
  .rte-content ol {
    padding-left: 1.5em;
    margin: 0 0 .6em;
  }

  .rte-content li {
    margin-bottom: .25em;
  }

  .rte-content blockquote {
    border-left: 3px solid var(--accent);
    margin: .6em 0;
    padding: .3em 1em;
    color: var(--muted);
  }

  .rte-content pre {
    background: var(--surface2);
    border: 1px solid var(--border);
    border-radius: 3px;
    padding: .75em 1em;
    font-size: 12px;
    overflow-x: auto;
    margin: .6em 0;
  }

  .rte-content code {
    background: var(--surface2);
    border-radius: 2px;
    padding: .1em .35em;
    font-size: 12px;
  }

  .rte-content a {
    color: var(--accent);
  }

  .rte-content img {
    max-width: 100%;
    height: auto;
  }

  /* A shown image opens in the file preview (RichTextImagePreview); the editor's own images keep the
     resize handles' cursor and open on a double-click instead. */
  .rte-content:not(.rz-html-editor-content) img:not(a img) {
    cursor: zoom-in;
  }

  /* ── Legacy QuillJS markup shim ──
     Old Quill content stored non-standard markup that relied on Quill's own stylesheet (now removed):
     lists as <ol><li data-list="bullet|ordered">, indent as .ql-indent-N, alignment as .ql-align-*, and
     div-based code blocks. New Radzen content uses standard tags and doesn't need these. PROD sampling
     (DEV-SW: quill→radzen) found only a handful of rows using data-list/ql-indent and zero align/code,
     so a CSS shim (no data migration) is enough — and it renders that old content correctly for the
     first time (the previous read path showed bullet lists as numbered). */
  .rte-content ol li[data-list="bullet"] {
    list-style-type: disc;
  }
  .rte-content ol li[data-list="ordered"] {
    list-style-type: decimal;
  }
  /* Quill's checklist items — render as a plain bullet (no interactive checkbox in read-only view). */
  .rte-content ol li[data-list="checked"],
  .rte-content ol li[data-list="unchecked"] {
    list-style-type: none;
  }
  .rte-content .ql-indent-1 { margin-left: 3em; }
  .rte-content .ql-indent-2 { margin-left: 6em; }
  .rte-content .ql-indent-3 { margin-left: 9em; }
  .rte-content .ql-indent-4 { margin-left: 12em; }
  .rte-content .ql-align-center  { text-align: center; }
  .rte-content .ql-align-right   { text-align: right; }
  .rte-content .ql-align-justify { text-align: justify; }
  .rte-content .ql-code-block-container,
  .rte-content .ql-code-block {
    background: var(--surface2);
    border: 1px solid var(--border);
    border-radius: 3px;
    padding: .5em 1em;
    font-family: 'Consolas', monospace;
    font-size: 12px;
    overflow-x: auto;
  }

/* ── Radzen editor content (WYSIWYG) ──
   The app's global reset (* { margin:0; padding:0 }) strips list padding/markers and block spacing inside
   the RadzenHtmlEditor's contenteditable, so bullets/numbers/headings don't show while editing. Restore
   the same typography the read view (.rte-content) uses. Scoped to the editor content element. */
.rte-radzen [contenteditable] p {
  margin: 0 0 .6em;
}
/* !important + Radzen's own content class, because default-base.css resets list markers/padding. */
.rte-radzen [contenteditable] ul,
.rte-radzen [contenteditable] ol,
.rte-radzen .rz-html-editor-content ul,
.rte-radzen .rz-html-editor-content ol {
  padding-left: 1.5em !important;
  margin: 0 0 .6em;
}
.rte-radzen [contenteditable] ul,
.rte-radzen .rz-html-editor-content ul { list-style: disc !important; }
.rte-radzen [contenteditable] ol,
.rte-radzen .rz-html-editor-content ol { list-style: decimal !important; }
.rte-radzen [contenteditable] li,
.rte-radzen .rz-html-editor-content li { display: list-item !important; margin-bottom: .25em; }
.rte-radzen [contenteditable] h1 { font-size: 1.35em; font-weight: 600; margin: .8em 0 .4em; }
.rte-radzen [contenteditable] h2 { font-size: 1.15em; font-weight: 600; margin: .8em 0 .4em; }
.rte-radzen [contenteditable] h3 { font-size: 1em;    font-weight: 600; margin: .8em 0 .4em; }
.rte-radzen [contenteditable] small,
.rte-content small { font-size: .8em; }
.rte-radzen [contenteditable] blockquote {
  border-left: 3px solid var(--accent);
  margin: .6em 0;
  padding: .3em 1em;
  color: var(--muted);
}

/* ── Rich-text image resize overlay (Radzen editor) ──
   Appended to <html> by radzenRte.js and positioned over a clicked image. The overlay itself passes clicks
   through (so the image stays selectable); only the handles + delete button are interactive. */
.rte-img-overlay {
  position: fixed;
  z-index: 9000;
  border: 1px solid var(--accent);
  pointer-events: none;
}
.rte-img-handle {
  position: absolute;
  width: 10px;
  height: 10px;
  background: var(--accent);
  border: 1px solid var(--surface);
  pointer-events: auto;
}
.rte-img-handle-nw { top: -5px;    left: -5px;  cursor: nwse-resize; }
.rte-img-handle-ne { top: -5px;    right: -5px; cursor: nesw-resize; }
.rte-img-handle-sw { bottom: -5px; left: -5px;  cursor: nesw-resize; }
.rte-img-handle-se { bottom: -5px; right: -5px; cursor: nwse-resize; }
.rte-img-delete {
  position: absolute;
  top: -10px;
  right: -10px;
  width: 20px;
  height: 20px;
  background: var(--danger);
  color: #fff;
  border-radius: 50%;
  text-align: center;
  line-height: 18px;
  font-size: 14px;
  cursor: pointer;
  pointer-events: auto;
}

/* ── Radzen editor theming ──
   Radzen's default-base.css is light-biased, so the editor doesn't follow the app's dark theme. Restyle it
   with the app's theme-aware tokens (correct in BOTH themes) — scoped to our .rte-radzen wrapper so no other
   Radzen usage is affected. NOTE: toolbar dropdown popups (Format/Colour) render in a body-level Radzen
   layer outside this scope and may still show light — themed separately if needed. */
.rte-radzen .rz-html-editor {
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--surface);
}
.rte-radzen .rz-html-editor-toolbar {
  background: var(--surface2);
  border-color: var(--border);
}
.rte-radzen .rz-html-editor-content {
  background: var(--surface);
  color: var(--text);
}
.rte-radzen .rz-html-editor-source {
  background: var(--surface);
  color: var(--text);
  border-color: var(--border);
}
/* Toolbar-less mode (ShowToolbar=false) — used on compact surfaces like comment boxes. Hides the whole
   toolbar; the Ctrl/Cmd+B/I/U and markdown list shortcuts still work (wired to the content element). */
.rte-radzen.rte-no-toolbar .rz-html-editor-toolbar { display: none !important; }

/* Normalise the built-in toolbar buttons to match the custom ones: transparent, no fixed box, hover
   highlight, vertically centred. The html-editor tools use .rz-html-editor-button. !important overrides
   default-base.css's fixed button sizing. */
.rte-radzen .rz-html-editor-toolbar { align-items: center; gap: 1px; }
.rte-radzen .rz-html-editor-toolbar .rz-html-editor-button {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  color: var(--text) !important;
  min-width: 0 !important;
  width: auto !important;
  height: auto !important;
  min-height: 0 !important;
  margin: 0 !important;
  padding: 5px 7px !important;
  border-radius: 3px !important;
  cursor: pointer;
}
.rte-radzen .rz-html-editor-toolbar .rz-html-editor-button:hover {
  background: color-mix(in srgb, var(--accent) 15%, transparent) !important;
}
.rte-radzen .rz-html-editor-toolbar .rz-html-editor-button.rz-state-active,
.rte-radzen .rz-html-editor-toolbar .rz-html-editor-button.rz-selected {
  background: color-mix(in srgb, var(--accent) 22%, transparent) !important;
  color: var(--accent) !important;
}
.rte-radzen .rz-html-editor-toolbar .rz-html-editor-button .rzi,
.rte-radzen .rz-html-editor-toolbar .rz-html-editor-button i { color: inherit !important; }

/* ── JIRA-style toolbar dropdowns (custom tools) ── */
.rte-radzen .rz-html-editor-toolbar { overflow: visible; flex-wrap: wrap; }
.rte-dd { position: relative; display: inline-flex; align-items: center; }
.rte-dd-toggle {
  background: transparent;
  border: none;
  color: var(--text);
  cursor: pointer;
  padding: 5px 7px;
  border-radius: 3px;
  font-size: 13px;
  line-height: 1.2;
}
.rte-dd-toggle:hover { background: color-mix(in srgb, var(--accent) 15%, transparent); }
/* Split buttons (default action + caret for the menu), grouped on hover like JIRA. */
.rte-split { position: relative; display: inline-flex; align-items: center; border-radius: 3px; }
.rte-split:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.rte-split-main,
.rte-split-caret {
  background: transparent;
  border: none;
  color: var(--text);
  cursor: pointer;
  padding: 5px 6px;
  border-radius: 3px;
  font-size: 13px;
  line-height: 1.2;
}
.rte-split-caret { padding-left: 2px; padding-right: 6px; font-size: 10px; }
.rte-split-main:hover,
.rte-split-caret:hover { background: color-mix(in srgb, var(--accent) 22%, transparent); }
/* Text-style icons (T, Tₛ, H₁…H₆). */
.rte-ico { font-weight: 700; }
.rte-ico sub { font-size: .7em; font-weight: 700; }
.rte-dd-menu {
  position: fixed;            /* escapes the editor box overflow; positioned by radzenRte.positionMenu */
  z-index: 9100;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .35);
  min-width: 180px;
  padding: 4px;
}
/* Active-format indicator (JIRA-style: the current format shows selected). */
.rte-dd-item.rte-active,
.rte-split-main.rte-active,
.rte-dd-toggle.rte-active { background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent); }
.rte-dd-item {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  color: var(--text);
  cursor: pointer;
  padding: 6px 10px;
  border-radius: 3px;
  font-size: 13px;
}
.rte-dd-item:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--accent); }
.rte-dd-sep { height: 1px; background: var(--border); margin: 4px 0; }
.rte-dd-backdrop { position: fixed; inset: 0; z-index: 9000; background: transparent; }
.rte-a-swatch { border-bottom: 2px solid var(--text); font-weight: 700; }
.rte-color-menu { display: grid; grid-template-columns: repeat(5, 22px); gap: 4px; min-width: 0; }
.rte-swatch { width: 22px; height: 22px; border: 1px solid var(--border); border-radius: 3px; cursor: pointer; padding: 0; }
.rte-link-menu { display: flex; flex-direction: column; gap: 6px; align-items: stretch; }
.rte-link-input {
  width: 100%;
  min-width: 220px;
  background: var(--surface2);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: 3px;
  padding: 5px 8px;
  font-size: 13px;
}
.rte-link-apply {
  background: var(--accent);
  color: #1f2328;
  border: none;
  border-radius: 3px;
  padding: 5px 12px;
  cursor: pointer;
  font-size: 13px;
}
/* Code block + inline code in the editor + read view. */
.rte-radzen [contenteditable] pre.rte-code,
.rte-content pre.rte-code,
.rte-radzen [contenteditable] pre,
.rte-content pre {
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: .6em 1em;
  font-family: 'Consolas', monospace;
  font-size: 12px;
  overflow-x: auto;
  white-space: pre-wrap;
}
.rte-radzen [contenteditable] code {
  background: var(--surface2);
  border-radius: 2px;
  padding: .1em .35em;
  font-size: 12px;
}

/* ── Global select styling for dark mode ──────────────────────────────────── */
select {
  color: var(--text) !important;
  background-color: var(--bg) !important;
}

  select option {
    background: var(--bg);
    color: var(--text);
  }

/* DEV-SW-113: a status dropdown (ticket list, ticket page, quick-view popup) shows its status in the status's
   colour, like the status badges on the board. The rule above forces every select's text to --text with
   !important, so these have to be !important too (and win on specificity). The options keep --text. */
select.status-neutral { color: var(--muted) !important; }
select.status-muted { color: var(--muted) !important; }
select.status-info { color: var(--info) !important; }
select.status-teal { color: var(--teal) !important; }
select.status-success { color: var(--success) !important; }
select.status-accent { color: var(--accent) !important; }
select.status-warning { color: var(--warning) !important; }
select.status-orange { color: var(--orange) !important; }
select.status-danger { color: var(--danger) !important; }
select.status-pink { color: var(--pink) !important; }
select.status-purple { color: var(--purple) !important; }

/* Consistent corner radius for all text inputs, textareas and selects — mirrors the
   buttons rule so every form control shares one subtle radius. Checkboxes/radios are
   excluded so toggles and checkboxes keep their own shape. */
input:not([type="checkbox"]):not([type="radio"]), textarea, select {
  border-radius: 6px;
}

/* ── Global date input styling ────────────────────────────────────────────── */
input[type="date"] {
  background-color: var(--bg) !important;
  color: var(--text) !important;
  border: 1px solid var(--border) !important;
  padding: 8px 10px !important;
  font-size: 13px !important;
  font-family: inherit !important;
  box-sizing: border-box !important;
  position: relative;
  cursor: pointer;
}

  input[type="date"]:focus {
    outline: none !important;
    border-color: var(--accent) !important;
  }

  input[type="date"]::-webkit-calendar-picker-indicator {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    cursor: pointer;
    opacity: 0;
  }

/* ── Shared date field styling ────────────────────────────────────────────── */
.date-field-wrapper {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

  .date-field-wrapper label {
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    transition: color .15s;
  }

    .date-field-wrapper label:hover {
      color: var(--accent);
    }

  .date-field-wrapper input[type="date"] {
    cursor: pointer;
  }

    .date-field-wrapper input[type="date"]::-webkit-calendar-picker-indicator {
      cursor: pointer;
    }

/* ── Global error — kept for Login page inline use ───────────────────────────── */
.global-error {
  background: rgba(192, 88, 74, .1);
  border: 1px solid rgba(192, 88, 74, .3);
  color: var(--danger);
  padding: 10px 13px;
  font-size: 12px;
  letter-spacing: .04em;
  margin-bottom: 20px;
  border-radius: 4px;
}

/* ── Disabled input styling ───────────────────────────────────────────────────── */
input:disabled,
select:disabled,
textarea:disabled {
  color: var(--text-muted, #888);
  background: var(--surface-raised, rgba(255,255,255,0.04));
  border-color: var(--border);
  cursor: not-allowed;
}

/* ── Issue Reports pages (Product Fault / Process / Incident) ───────────────── */
.rep-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--border);
  position: relative;
  flex-wrap: wrap;
}

.rep-eyebrow {
  font-size: 10px;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 6px;
}

.rep-title {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 32px;
  font-weight: 500;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text);
  margin: 0;
}

.rep-form {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 16px;
  margin-bottom: 16px;
}

.rep-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}

.rep-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.rep-field--full {
  grid-column: 1 / -1;
}

.rep-field label {
  font-size: 12px;
  color: var(--muted);
}

.rep-field input, .rep-field select, .rep-field textarea {
  width: 100%;
  padding: 7px 10px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  font-size: 14px;
}

/* Highlight a required form field whose requirement isn't met yet (replaces the inline error text). */
.rep-field input.rep-invalid,
.rep-field select.rep-invalid,
.rep-field textarea.rep-invalid {
  border-color: var(--danger);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--danger) 25%, transparent);
  outline: none;
}

.rep-req {
  color: var(--danger);
}

.rep-check {
  flex-direction: row;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--text);
  cursor: pointer;
}

  .rep-check input {
    width: auto;
  }

.rep-files {
  list-style: none;
  margin: 6px 0 0;
  padding: 0;
  font-size: 12px;
  color: var(--muted);
}

  .rep-files li {
    display: flex;
    align-items: center;
    gap: 6px;
  }

.rep-file-x {
  background: none;
  border: none;
  color: var(--danger);
  cursor: pointer;
}

.rep-meta {
  font-size: 12px;
  color: var(--muted);
  margin-top: 10px;
}

.rep-form-actions {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}

/* Consistent rounded frame for every data-table wrapper (any class containing "-table-wrap",
   including the shared .data-table-wrap used to wrap otherwise-bare tables). */
[class*="-table-wrap"] {
  overflow: auto;
  border: 1px solid var(--border);
  border-radius: 8px;
  /* When the wrapper is a flex item (e.g. inside a display:flex column like the project register's
     .pr-body), min-width defaults to min-content, so the table grows past the container and gets
     clipped instead of scrolling. min-width:0 lets it shrink to the container and scroll horizontally —
     the behaviour block-flow wrappers (e.g. tickets) already have. No-op for non-flex wrappers. */
  min-width: 0;
}

/* ── Responsive column fit (all data tables) ──────────────────────────────────
   As the viewport narrows, every data-table's inter-column gaps tighten step-by-step to keep all
   columns visible for as long as possible, only falling back to the wrapper's horizontal scroll once
   even the tightest gaps can't fit. Applies to every `*-table-wrap`; opt a table OUT with the
   `no-table-fit` class (used for the Documents list + attachments tables, which have their own
   preview-split layout and tighten themselves).
   Deliberately viewport @media, NOT container queries: `container-type` collapses flex-item table
   wrappers in Chromium (it broke the Documents list), and table width tracks the viewport closely
   enough for this. The !important beats each table's own cell padding; scoped to *-table-wrap so it
   never touches unrelated cells. */
[class*="-table-wrap"]:not(.no-table-fit) { overflow-x: auto; }
@media (max-width: 1280px) {
  [class*="-table-wrap"]:not(.no-table-fit) th,
  [class*="-table-wrap"]:not(.no-table-fit) td { padding-left: 8px !important; padding-right: 8px !important; }
}
@media (max-width: 1024px) {
  [class*="-table-wrap"]:not(.no-table-fit) th,
  [class*="-table-wrap"]:not(.no-table-fit) td { padding-left: 5px !important; padding-right: 5px !important; }
}
@media (max-width: 820px) {
  [class*="-table-wrap"]:not(.no-table-fit) th,
  [class*="-table-wrap"]:not(.no-table-fit) td { padding-left: 3px !important; padding-right: 3px !important; }
}

/* When a table wrapper is the sole content of a page-body, bound it to the body's
   height so it scrolls internally — this keeps the sticky `thead th` header pinned
   while scrolling. Pages with a toolbar/other content above the table (wrapper is
   not :only-child) are left to scroll normally. */
.page-body > [class*="-table-wrap"]:only-child {
  max-height: 100%;
}

/* Short viewports (e.g. a phone turned landscape): the fixed 52px top bar plus a page's header/filter
   rows leave the internal-scrolling .page-body almost no height, so its scroll box — and a height-bounded
   table wrapper inside it — collapses to an unusable sliver. On short viewports switch these pages to
   natural scrolling: the shell grows to fit its content and .layout-main (already overflow-y:auto) scrolls
   the whole page beneath the fixed top bar, so the entire table is reachable. Horizontal scrolling of a
   too-wide table still happens inside its own wrapper. */
@media (max-height: 600px) {
  .fs-pg-shell { height: auto; min-height: 100%; }
  .page-body { flex: 0 0 auto; overflow-y: visible; }
  .page-body > [class*="-table-wrap"]:only-child { max-height: none; }
}

/* ── Table-list pages scroll as a whole page (global table setting) ────────────
   By default a .page-body traps a data table in a short internal scroll box while
   the page header + search/filter stay pinned. For any list/table page we instead
   let the ENTIRE page scroll: the fixed app top bar stays put, but the page header,
   search/filter and table all scroll together and .layout-main (already
   overflow-y:auto) does the scrolling. This uses the available height far better and
   is the same mechanism the short-viewport rule above uses, promoted to always-on.

   Scoped with :has() to pages whose .page-body actually contains a data table, so
   master/detail views (which manage their own internal scroll) are left untouched.
   .tkt-shell is Tickets' own shell class; it gets the same treatment. */
.fs-pg-shell:has(.page-body [class*="-table-wrap"]),
.tkt-shell:has(.page-body [class*="-table-wrap"]) {
  height: auto;
  min-height: 100%;
}
.page-body:has([class*="-table-wrap"]) {
  flex: 0 0 auto;
  overflow-y: visible;
}
.page-body:has([class*="-table-wrap"]) [class*="-table-wrap"] {
  max-height: none;
}
/* The page itself scrolls now, so a sticky column-header row would tuck behind the
   sticky page header. Let it scroll with the rows instead. */
.page-body:has([class*="-table-wrap"]) thead th {
  position: static;
}

.rep-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}

  .rep-table th, .rep-table td {
    text-align: left;
    padding: 10px 14px;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
  }

  .rep-table th {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--muted);
  }

  .rep-table tbody tr:last-child td {
    border-bottom: none;
  }

.rep-actions {
  text-align: right;
}

/* ── Report list toolbar (search / filter / sort) ─────────────────────────── */
.rep-toolbar {
  display: flex;
  align-items: flex-end;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}

.rep-filter-group {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 140px;
}

.rep-filter-label {
  font-size: 9px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--muted);
}

.rep-filter-select {
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text);
  font-family: 'Barlow', sans-serif;
  font-size: 12px;
  padding: 7px 10px;
  outline: none;
  min-width: 140px;
  transition: border-color .15s;
}

  .rep-filter-select:focus {
    border-color: var(--accent);
  }

.rep-search-bar {
  display: flex;
  align-items: center;
  margin-bottom: 16px;
}

/* Explanatory notice shown under a report list (e.g. incident visibility caveat). */
.rep-list-notice {
  margin: 12px 2px 0;
  font-size: 12.5px;
  color: var(--text-muted, #6b7280);
  font-style: italic;
}

/* Active deep-link filter (e.g. a dashboard widget scope) shown as a dismissible chip. */
.rep-active-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: -6px 0 16px;
}
.rep-filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 600;
  padding: 4px 6px 4px 12px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent, #2563eb) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent, #2563eb) 40%, transparent);
  color: var(--text);
}
.rep-filter-chip-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--text-muted, #6b7280);
  cursor: pointer;
  font-size: 11px;
  line-height: 1;
}
.rep-filter-chip-x:hover { background: rgba(127,127,127,.2); color: var(--text); }

.rep-search-input {
  flex: 1;
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text);
  font-family: 'Barlow', sans-serif;
  font-size: 12px;
  padding: 7px 10px;
  outline: none;
  transition: border-color .15s;
}

  .rep-search-input:focus {
    border-color: var(--accent);
  }

.rep-state {
  padding: 32px;
  text-align: center;
  color: var(--muted);
}

.rep-empty {
  font-style: italic;
}

/* Status cell: the report status badge plus, for an escalated report, its NCR's status badge beside it. */
.rep-status-cell {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  align-items: center;
}

.rep-badge {
  display: inline-block;
  font-size: 11px;
  font-weight: 600;
  padding: 2px 9px;
  border-radius: 10px;
  border: 1px solid var(--border);
  color: var(--muted);
}

.rep-badge--open {
  color: var(--accent);
  border-color: var(--accent);
}

.rep-badge--submitted {
  color: #2f81f7;
  border-color: #2f81f7;
}

.rep-badge--escalated {
  color: var(--danger);
  border-color: var(--danger);
}

.rep-badge--closed {
  color: var(--muted);
  border-color: var(--border);
}

.rep-badge--pending {
  color: var(--accent);
  border-color: var(--accent);
}
/* yellow */
.rep-badge--signed {
  color: var(--success);
  border-color: var(--success);
}
/* green */
.rep-badge--progress {
  color: #2f81f7;
  border-color: #2f81f7;
}
/* blue */

/* Yes/No pill toggle (matches the NCR form's yes/no styling) */
/* Yes/No segmented selector — rounded like the Tickets list/kanban view toggle, but with a solid
   (muted, not hairline) outline so the control is clearly visible. */
.rep-yesno-group {
  display: flex;
  gap: 0;
  border: 1px solid var(--muted);
  border-radius: 6px;
  overflow: hidden;
  width: fit-content;
  margin-bottom: 8px;
}

.rep-yesno-opt {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 16px;
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
  border-right: 1px solid var(--muted);
  transition: background .12s, color .12s;
  letter-spacing: 0;
  text-transform: none;
  user-select: none;
}

  .rep-yesno-opt:last-child {
    border-right: none;
  }

  .rep-yesno-opt input[type="radio"] {
    display: none;
  }

  .rep-yesno-opt.active {
    background: var(--accent);
    color: var(--bg);
  }

.rep-signoff-required {
  align-self: center;
  font-size: 12px;
  font-style: italic;
  color: var(--muted);
}

/* Right-click menu on a list row (documents, projects, parts, notifications): opens at the pointer over a
   transparent backdrop that closes it on any click. "Open in new tab" is a real link, so the browser opens it. */
.row-ctx-backdrop {
  position: fixed;
  inset: 0;
  z-index: 500;
}

.row-ctx-menu {
  position: fixed;
  z-index: 501;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 6px 20px rgba(0,0,0,.28);
  min-width: 180px;
  padding: 4px;
}

.row-ctx-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  color: var(--text);
  font-family: 'Barlow', sans-serif;
  font-size: 12px;
  padding: 8px 12px;
  border-radius: 4px;
  text-decoration: none;
  cursor: pointer;
}

  .row-ctx-item:hover { background: var(--bg); color: var(--text); text-decoration: none; }

.row-ctx-danger { color: var(--danger); }

/* Sliding on/off toggle switch — the app's standard boolean control. */
.toggle-label {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  user-select: none;
}

.toggle-check {
  display: none;
}

.toggle-track {
  position: relative;
  width: 34px;
  height: 18px;
  background: var(--border);
  border-radius: 9px;
  transition: background .2s;
  flex-shrink: 0;
}

  .toggle-check:checked + .toggle-track {
    background: var(--accent);
  }

.toggle-thumb {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 12px;
  height: 12px;
  background: var(--muted);
  border-radius: 50%;
  transition: transform .2s, background .2s;
}

  .toggle-check:checked + .toggle-track .toggle-thumb {
    transform: translateX(16px);
    background: var(--bg);
  }

.toggle-text {
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
}

.rep-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 6px;
}

.rep-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  padding: 3px 8px;
  border-radius: 12px;
  background: var(--surface2);
  border: 1px solid var(--border);
  color: var(--text);
}

.rep-chip-x {
  background: none;
  border: none;
  color: var(--danger);
  cursor: pointer;
  font-size: 11px;
  padding: 0;
  line-height: 1;
}

.rep-person-picker {
  position: relative;
}
  /* Style the picker's input itself so it looks right anywhere it's used — not only inside a
   .rep-field (e.g. NCR linked-docs, ticket sidebar Share). Mirrors the .rep-field input styling. */
  .rep-person-picker input {
    width: 100%;
    padding: 7px 10px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--text);
    font-size: 14px;
  }

.rep-person-results {
  list-style: none;
  margin: 2px 0 0;
  padding: 4px;
  position: absolute;
  z-index: 20;
  left: 0;
  right: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  max-height: 240px;
  overflow-y: auto;
  box-shadow: 0 6px 16px rgba(0,0,0,.25);
}

  .rep-person-results li button {
    display: block;
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    color: var(--text);
    padding: 6px 8px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 14px;
  }

    .rep-person-results li button:hover {
      background: var(--surface2);
      color: var(--accent);
    }

/* Linked-report cards shown in NCR Step 1 */
.ncr-linked-reports {
  margin-bottom: 16px;
}

.ncr-linked-empty {
  font-size: 13px;
  color: var(--muted);
  font-style: italic;
  padding: 4px 0;
}

.rep-link-card {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
  margin-bottom: 10px;
  background: var(--surface);
}

/* .rep-link-head / .rep-link-title were the escalated report's own title bar. DEV-SW-96 (Phase 5) moved
   that job to the CollapsibleBox header, so they are gone rather than left behind to rot. */

.rep-link-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px 16px;
}

.rep-link-field {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 13px;
}

.rep-link-field--full {
  grid-column: 1 / -1;
  margin-top: 6px;
}

.rep-link-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
}
/* Grouped sub-sections inside a linked-report card (Items Affected, Immediate Action) — mirrors the
   report view's grouping. Spans the full card width and nests its own field grid. */
.rep-link-group {
  grid-column: 1 / -1;
  margin-top: 8px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface2);
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.rep-link-group-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--text);
}

.rep-link-group-flag {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  padding: 1px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--muted);
}

.rep-link-sub {
  font-size: 12px;
  color: var(--muted);
  margin-left: 6px;
}

.rep-link-desc {
  color: var(--text);
}

.rep-attach-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

/* An attachment plus its (optional) delete button. */
.rep-attach-item {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.rep-attach-del {
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 12px;
  line-height: 1;
  padding: 2px 4px;
}

  .rep-attach-del:hover { color: var(--danger); }

/* Add-attachment control row in the detail view. */
.rep-attach-add {
  margin-top: 10px;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.rep-attach-thumb {
  width: 72px;
  height: 72px;
  object-fit: cover;
  border: 1px solid var(--border);
  border-radius: 6px;
  display: block;
}

.rep-attach-link {
  display: inline-block;
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 13px;
  color: var(--text);
  text-decoration: none;
}

  .rep-attach-link:hover {
    border-color: var(--accent);
    color: var(--accent);
  }

/* Clickable report rows (open the detail view) */
.rep-table tbody tr.rep-row {
  cursor: pointer;
  transition: background .12s;
}

  .rep-table tbody tr.rep-row:hover {
    background: var(--surface);
  }

/* Detail view */
.rep-detail-head {
  display: flex;
  align-items: flex-start;
  gap: 16px;
}

.rep-detail-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

.rep-view-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px 24px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 20px;
}

.rep-view-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.rep-view-field--full {
  grid-column: 1 / -1;
}

.rep-view-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--muted);
}

.rep-view-value {
  color: var(--text);
  font-size: 14px;
  word-break: break-word;
}

  .rep-view-value.rep-view-title {
    font-weight: 700;
    font-size: 16px;
  }

.rep-view-empty {
  color: var(--muted);
}

.rep-view-sub {
  color: var(--muted);
  font-size: 12px;
  margin-left: 8px;
}
/* Grouped sub-sections inside the report detail view (Logged, Classification, Items Affected,
   Immediate Action) — same grouped format as the NCR linked-report card. */
.rep-view-group {
  grid-column: 1 / -1;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface2);
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.rep-view-group-title {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--text);
}

.rep-view-group-flag {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  padding: 1px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--muted);
}
/* A field grid nested inside a group sheds the outer box styling so it reads as a sub-section. */
.rep-view-group .rep-view-grid {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  gap: 12px 24px;
}

/* Grouped sub-sections inside the report CREATE/EDIT form (Logged, Incident Details) —
   the form-input mirror of .rep-view-group. */
.rep-form-group {
  grid-column: 1 / -1;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface2);
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.rep-form-group-title {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--text);
}

/* Nested field grid inside a form group. */
.rep-form-group .rep-grid { gap: 12px 24px; }

.rep-person-linked {
  margin-top: 6px;
  font-size: 12px;
  color: var(--muted);
}

/* Detail tabs (Comments | History) */
.rep-tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--border);
  margin: 24px 0 0;
}

.rep-tab {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 10px 14px;
  font-family: 'Barlow', sans-serif;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--muted);
  cursor: pointer;
}

  .rep-tab.active {
    color: var(--text);
    border-bottom-color: var(--accent);
  }

/* History timeline (synthesised lifecycle events) */
.rep-history {
  list-style: none;
  margin: 16px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.rep-history-item {
  display: flex;
  gap: 12px;
  align-items: baseline;
}

.rep-history-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
}

.rep-history-body { display: flex; flex-direction: column; gap: 2px; }
.rep-history-title { font-size: 13px; font-weight: 600; color: var(--text); }
.rep-history-detail { font-size: 12px; color: var(--muted); }
.rep-history-time { font-size: 11px; color: var(--muted); }

@media (max-width: 576px) {
  .rep-view-grid {
    grid-template-columns: 1fr;
  }
}

.rep-link-picker {
  margin-top: 8px;
  position: relative;
}

  .rep-link-picker input {
    width: 100%;
    padding: 7px 10px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--text);
    font-size: 14px;
  }

.rep-link-none {
  color: var(--muted);
  font-size: 13px;
  padding: 6px 8px;
}

/* Escalate popup (create new NCR vs add to existing) */
.rep-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  padding: 16px;
}

.rep-modal {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 20px;
  width: 100%;
  max-width: 460px;
  box-shadow: 0 12px 32px rgba(0,0,0,.35);
}

.rep-modal-title {
  font-size: 16px;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 10px;
}

.rep-modal-text {
  font-size: 14px;
  color: var(--muted);
  margin: 0 0 14px;
}
/* Form fields inside a modal (reusing the rep-field styles): even vertical rhythm between
   fields and clear space before the action buttons below. */
.rep-modal-fields {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 0 0 20px;
}

  .rep-modal-fields .rep-yesno-group {
    margin-bottom: 0;
  }

.rep-modal-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
  /* Clear of whatever it follows. The modals that existed first all ended in a list or a paragraph with
     its own bottom margin, so the buttons looked spaced; the moment one ended in a .rep-grid of inputs
     they sat directly against the last field, close enough to look like part of it. */
  margin-top: 16px;
}

/* A dialog whose actions do not fit on one line at the default width. 460px holds two buttons; the
   escalate dialog has three — "Create New NCR", "Add to Existing NCR" and "Cancel" — and Cancel wrapped
   onto a line of its own, which read as a lesser, separate thing rather than the third option it is.
   Widening the dialog is the fix rather than shrinking the buttons: the words are the choice being
   offered, and abbreviating them to fit is how a clear question becomes a cramped one. */
.rep-modal-wide {
  max-width: 560px;
}

.rep-modal-search {
  width: 100%;
  padding: 8px 10px;
  margin-bottom: 8px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  font-size: 14px;
}

.rep-modal-list {
  position: static;
  max-height: 260px;
  margin-bottom: 12px;
}

@media (max-width: 576px) {
  .rep-grid {
    grid-template-columns: 1fr;
  }

  .rep-link-grid {
    grid-template-columns: 1fr;
  }
}

/* ── PDF Markup: selectable-text overlay (Select Text tool) ──────────────────────
   pdf.js fills #pdf-text-layer with transparent, absolutely-positioned glyph spans.
   These rules are global (not scoped) because the spans are created dynamically. */
.pdf-text-layer {
  position: absolute;
  top: 0;
  left: 0;
  overflow: hidden;
  line-height: 1;
  text-align: initial;
  pointer-events: none; /* inert until the Select Text tool turns it on */
  z-index: 4;
}

  .pdf-text-layer.active {
    pointer-events: auto;
    z-index: 55; /* above annotations so any text can be selected */
    cursor: text;
  }

  .pdf-text-layer span {
    position: absolute;
    white-space: pre;
    color: transparent;
    transform-origin: 0 0;
    cursor: text;
    user-select: text;
    -webkit-user-select: text;
  }

  .pdf-text-layer ::selection {
    background: rgba(37, 99, 235, .35);
  }

  .pdf-text-layer span::selection {
    background: rgba(37, 99, 235, .35);
  }

  /* While Select Text is active, only the PDF's own text is selectable — never the review
   annotations (added text, comments, signatures) in the annotation layer beneath it. */
  .pdf-text-layer.active ~ .annotation-layer {
    user-select: none;
    -webkit-user-select: none;
  }

/* ── Reusable page shell + header ────────────────────────────────────────────
   Use on any page that wants the standard full-height column layout with a title on the
   left and an action button on the right.

   No z-index, for the same reason as .layout-main and body::before: a z-index here would make
   the shell its own stacking context, and every dialog on the page (position: fixed) would be
   painted under the top bar however high its own z-index — the bell and profile picture showing
   through it. */
.fs-pg-shell {
  display: flex;
  flex-direction: column;
  height: 100%;
  position: relative;
}

.fs-pg-header {
  flex-shrink: 0;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  /* The actions drop under the title when there is no room beside it, instead of the title being
     squeezed towards nothing while the buttons run off the right edge. Row gap first, column gap
     second, so the two lines are not jammed together once they do wrap. */
  flex-wrap: wrap;
  gap: 12px 16px;
  padding: 24px 24px 18px;
  border-bottom: 1px solid var(--border);
  /* Stay pinned to the top while the rest of the page content scrolls beneath it.
       It used to be fully transparent, to show the layout's translucent backdrop the way the Users
       header does. The z-index kept it on top, but with nothing filling it the page text scrolled
       THROUGH the title and its buttons and the two read as one jumble.
       Frosted instead of solid: the translucency that was wanted is kept, while the blur stops
       anything behind it being legible. The colour-mix fallback applies on its own where
       backdrop-filter is unsupported, so the header is never see-through. */
  position: sticky;
  top: 0;
  z-index: 10;
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

/* Pinned on a desktop, scrolled away on anything narrower.
   ---------------------------------------------------------------------------
   A pinned header costs the same vertical space on every screen, and a narrow one has the least to
   give: below ~992px the title and its buttons wrap onto two lines, so the header grows at exactly
   the width where holding it on screen hurts most — a tablet in portrait can lose a quarter of its
   height to a heading it has already read.

   Keeping where you are is worth more than keeping the title when the screen is wide and the header
   is one line. When it is neither, the record wins. 992px is the desktop boundary this app uses
   everywhere else (see the breakpoints in MonOps.Web/CLAUDE.md), and it is also about where the
   header stops fitting on one line, so the two agree. */
@media (max-width: 991px) {
  .fs-pg-header {
    position: static;
  }
}

.fs-pg-title {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 32px;
  font-weight: 500;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text);
}

/* ── Soft field limits ───────────────────────────────────────────────────────
   A field that is over its advisory length: outlined, with the reason under it.
   A warning, never a truncation — cutting somebody off mid-word as they type is
   worse than letting them run long and saying why not.

   Global, and !important for the same reason as .input--invalid below: each page
   styles its own inputs in scoped CSS (.field-input[b-…], .field-input:focus[b-…]),
   which outranks a single global class, so without it the outline never shows.
   ─────────────────────────────────────────────────────────────────────────── */

.field-input--warn,
.field-input--warn:focus {
  border-color: var(--danger) !important;
  box-shadow: none !important;
}

.field-warn {
  font-family: 'Barlow', sans-serif;
  font-size: 11px;
  color: var(--danger);
  margin-top: 4px;
}

/* ── Failed validation ───────────────────────────────────────────────────────
   A field that stopped the last save. Element-agnostic on purpose: .rep-invalid
   only bites inside a .rep-field wrapper, so it did nothing on a form built any
   other way. This works on any input, select or textarea.

   The outline survives :focus — focus moves to this field the moment it fails,
   and a highlight that vanishes on arrival tells the user nothing.
   ─────────────────────────────────────────────────────────────────────────── */

.input--invalid,
.input--invalid:focus {
  border-color: var(--danger) !important;
  box-shadow: none;
}

/* The reason, under the field. Shares .field-warn's look because they say the same kind of thing —
   this one is a blocker, that one an advisory. */
.input-error {
  font-family: 'Barlow', sans-serif;
  font-size: 11px;
  color: var(--danger);
  margin-top: 4px;
}

/* ── Breadcrumb page header ──────────────────────────────────────────────────
   An icon back button, then "where you are" as a trail: SECTION / REF: Name.
   Replaces the wide "← To Concessions" button plus a separate eyebrow — the two
   together cost a lot of width to say less, and the record's own name was not in
   the heading at all, so a detail page announced an id and nothing else.
   Global so the detail and the raise/edit page share one header.
   ─────────────────────────────────────────────────────────────────────────── */

.pg-crumb {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  /* Take the space going, but ask for 280px before the header gives up and wraps the buttons under.
     Without a basis the title would keep shrinking for as long as the buttons wanted room. */
  flex: 1 1 280px;
}

/* Square and icon-only. It sits beside a 32px heading, so it is sized to that rather than to the body
   text, and the hit area stays comfortable without a label. */
.pg-crumb-back {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--muted);
  cursor: pointer;
  transition: border-color .15s, color .15s, background .15s;
}

  .pg-crumb-back:hover {
    border-color: var(--accent);
    color: var(--accent);
    background: var(--surface);
  }

  .pg-crumb-back svg { width: 18px; height: 18px; }

.pg-crumb-title {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
  min-width: 0;
  margin: 0;
}

.pg-crumb-link {
  color: var(--muted);
  text-decoration: none;
  cursor: pointer;
  transition: color .15s;
}

  .pg-crumb-link:hover { color: var(--accent); }

.pg-crumb-sep { color: var(--border); font-weight: 300; }

.pg-crumb-ref { color: var(--text); }

/* The record's own name. Identical to the reference in every respect except case — same colour, same
   face, same weight, same tracking — because "CON-000001: Testing" is one thing. Earlier versions muted
   it and then lightened it, which each time split it into a dark id and a grey afterthought.

   text-transform is the ONLY override: .fs-pg-title uppercases everything, which is right for the trail
   and the reference but shouts back a sentence somebody typed. Nothing else may differ here. */
.pg-crumb-name {
  text-transform: none;
  /* break-word, NOT anywhere. They look identical until the heading is in a flex row that wants to
     shrink it: `anywhere` lets the element's MIN-CONTENT width fall to a single character, so the
     buttons beside it took all the space and the title came out as one letter per line, straight down
     the page. `break-word` keeps min-content at the longest word, so the row runs out of room and wraps
     instead — which is the behaviour that was wanted. It still breaks a word too long to fit. */
  overflow-wrap: break-word;
}

@media (max-width: 576px) {
  /* The trail is the first thing to give up its space on a phone — the back button still goes there. */
  .pg-crumb-link, .pg-crumb-sep { display: none; }

  /* 32px of condensed caps is a desktop heading. On a phone it eats the screen before the record does,
     and it is the reason the title needed four lines where two would do. */
  .fs-pg-title { font-size: 24px; }

  .fs-pg-header { padding: 16px 16px 14px; }

  /* Once the buttons are on their own line they may as well use it: left-aligned and allowed to wrap,
     rather than bunched at the right where they are furthest from a thumb. */
  .fs-pg-actions {
    flex: 1 1 100%;
    justify-content: flex-start;
  }
}

/* Right-hand action group for .fs-pg-header. Keeps one or more buttons grouped and
   evenly spaced, so the header's space-between layout still sees just two children
   (title + this group) even when there are multiple actions. */
.fs-pg-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  /* A record can carry five or more of these (Edit / Send for approval / Cancel / Print / Follow).
     Without this they stay on one line and overflow the viewport — the last one half cut off at the
     right edge, with no way to reach it. */
  flex-wrap: wrap;
}

/* ── Caps Lock indicator ──
   Floating warning shown next to a focused password field when Caps Lock is on.
   Created + positioned by wwwroot/js/capsLock.js; theme-aware via the tokens above. */
.capslock-indicator {
  position: fixed;
  z-index: 4000;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
  background: var(--surface2);
  border: 1px solid var(--danger);
  border-radius: 4px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  pointer-events: none;
}
.capslock-indicator__icon {
  color: var(--danger);
  font-size: 14px;
  line-height: 1;
}
.capslock-indicator[hidden] {
  display: none;
}

/* ── PDF markup focus mode ──────────────────────────────────────────────────
   Toggled by a <body> class (pdfCollab.setFocusMode, cleared on viewer dispose). Hides the app nav, the
   app topbar and the page header to give the document the whole window; the PDF toolbar stays. Each hidden
   bar peeks back as an overlay when its screen edge is hovered — a transparent grab strip on the element's
   own ::before/::after is the hot-zone, so nothing reflows the document. View-only; no persistence. */
body.pdf-focus-mode .layout-main { padding-left: 0 !important; padding-top: 0 !important; }

/* App topbar — hidden while focusing (exit focus mode to reach it). */
body.pdf-focus-mode .topbar { transform: translateY(-100%) !important; transition: transform .2s ease; }

/* App nav — slides off the left; hover the left screen edge to peek it. */
body.pdf-focus-mode .sidebar { transform: translateX(-100%); transition: transform .2s ease; z-index: 260; }
body.pdf-focus-mode .sidebar::after { content: ""; position: absolute; top: 0; right: -16px; width: 16px; height: 100%; }
body.pdf-focus-mode .sidebar:hover { transform: translateX(0); }

/* Page header — becomes a top overlay; hover the top edge to peek it. */
body.pdf-focus-mode .pmv-header {
  position: fixed; top: 0; left: 0; right: 0; z-index: 250;
  transform: translateY(-100%); transition: transform .2s ease;
  background: var(--surface); box-shadow: 0 2px 10px rgba(0, 0, 0, .25);
}
body.pdf-focus-mode .pmv-header::after { content: ""; position: absolute; left: 0; bottom: -16px; width: 100%; height: 16px; }
body.pdf-focus-mode .pmv-header:hover { transform: translateY(0); }

/* Right comments/annotations sidebar — slides off the right; hover the right edge to peek it. */
body.pdf-focus-mode .pdf-sidebar {
  position: fixed; top: 0; right: 0; height: 100vh; z-index: 250;
  transform: translateX(100%); transition: transform .2s ease;
  box-shadow: -2px 0 10px rgba(0, 0, 0, .25);
}
body.pdf-focus-mode .pdf-sidebar::before { content: ""; position: absolute; top: 0; left: -16px; width: 16px; height: 100%; }
body.pdf-focus-mode .pdf-sidebar:hover { transform: translateX(0); }

/* ===========================================================================
   Record history — the house standard (HistoryTimeline.razor)
   ---------------------------------------------------------------------------
   Modelled on Jira's History tab: each entry reads as a sentence — who did what
   to which field — with the before/after shown underneath as values rather than
   folded into the text.

   GLOBAL on purpose. This is meant to be used by every record that has a history
   (concessions first, then tickets, NCRs, reports and documents), and a scoped
   copy per page would drift the moment one of them was tweaked.
   =========================================================================== */

.hist-list { display: flex; flex-direction: column; gap: 22px; }

.hist-item { display: flex; gap: 12px; align-items: flex-start; }

/* Initials bubble. The colour is hashed from the name in the component, so one person is always the
   same colour and the eye can follow them down a long list. */
.hist-avatar {
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-family: 'Barlow', sans-serif;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .04em;
    user-select: none;
    /* The same class serves an <img> when the person has a photo (UserAvatar), so the image is cropped
       to the circle rather than squashed into it. */
    object-fit: cover;
}

.hist-body { min-width: 0; flex: 1 1 auto; }

/* Flex rather than inline flow so a comment's Edit button can sit at the far right of the same line
   (.comment-actions uses margin-left:auto). The gap replaces the spacing the inline version got from
   whitespace between the spans. */
.hist-head {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 5px;
    font-family: 'Barlow', sans-serif;
    font-size: 14px;
    color: var(--text);
    line-height: 1.4;
}

/* The actor and the field are the two things being scanned for, so they carry the weight and the
   connecting verb recedes. */
.hist-actor { font-weight: 600; }
.hist-action { opacity: .75; }
.hist-field { font-weight: 600; }

.hist-time {
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: 'Barlow', sans-serif;
    font-size: 12px;
    color: var(--muted);
    margin-top: 2px;
}

.hist-change {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 10px;
}

/* Values are pills so a before and after read as two things rather than one run-on string. */
.hist-val {
    font-family: 'Barlow', sans-serif;
    font-size: 12px;
    padding: 3px 9px;
    border-radius: 3px;
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--text);
    max-width: 100%;
    overflow-wrap: anywhere;
}

/* The old value is deliberately quieter — it is context for the new one, not the point. */
.hist-val--old { opacity: .6; }

/* "from … to …" rather than a bare arrow. An arrow leaves the direction to be inferred, and which way a
   change went is the entire point of the line. */
.hist-prep {
    font-family: 'Barlow', sans-serif;
    font-size: 12px;
    color: var(--muted);
    flex: 0 0 auto;
}

/* Prose does not fit in a pill: a description change laid out inline is unreadable past a few words, so
   long values become two labelled blocks side by side, stacking on a narrow screen. */
.hist-change--long { align-items: stretch; }

.hist-longblock { flex: 1 1 260px; min-width: 0; }

    .hist-longblock .hist-prep { display: block; margin-bottom: 4px; }

.hist-longval {
    font-family: 'Barlow', sans-serif;
    font-size: 13px;
    line-height: 1.5;
    padding: 10px 12px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 3px;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

    .hist-change--long .hist-longblock:first-child .hist-longval { opacity: .6; }

/* Changed words, where the page diffs the two sides of a long value (the ticket description does). The
   marks arrive inside the value's markup, so they are styled here: a page's scoped sheet cannot reach
   into HistoryItem. */
.hist-longval mark.hist-del {
    background: rgba(180, 60, 60, .22);
    color: inherit;
    text-decoration: line-through;
    border-radius: 2px;
    padding: 0 1px;
}

.hist-longval mark.hist-ins {
    background: rgba(60, 140, 60, .26);
    color: inherit;
    border-radius: 2px;
    padding: 0 1px;
}

@media (max-width: 576px) {
    .hist-change--long { flex-direction: column; }
}

.hist-empty {
    font-family: 'Barlow', sans-serif;
    font-size: 13px;
    color: var(--muted);
    padding: 8px 0;
}

/* Collapsible box around a history list (HistoryTimeline with a Title set).
   Closed by default: history is reference material, consulted when something is being questioned, and
   left open it pushes the record itself off the screen. */
/* Matches the surrounding group boxes (.rep-view-group and friends): same radius, same surface, so it
   reads as one more section of the record rather than something bolted on.

   Deliberately NOT overflow:hidden — that would be the easy way to keep the header's hover fill inside
   the rounded corners, but it also clips the user hover cards that pop out of the entries. The header
   rounds its own top corners instead. */
.cbox {
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface2);
}

/* A row, not a button: HeaderActions can hold a control of its own (the Attach label wraps a file
   input), which cannot live inside a <button>. The two toggle halves sit either side of it. */
.cbox-header {
    border-radius: 7px 7px 0 0;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 14px;
    color: var(--text);
    font-family: 'Barlow', sans-serif;
}

    .cbox-header:hover { background: var(--bg); }

/* The title half and the count/chevron half. Both toggle, so the bar reads as one target everywhere
   except over the actions between them. */
.cbox-toggle,
.cbox-end {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 0;
    background: transparent;
    border: none;
    color: inherit;
    font-family: inherit;
    cursor: pointer;
    text-align: left;
}

.cbox-toggle { flex: 1 1 auto; min-width: 0; }

.cbox-end { flex: 0 0 auto; }

/* Same type as every other section heading on a record page (see .rep-view-group-title): Barlow, 12px,
   semibold, wide-tracked caps. A section like any other, and it should not announce itself. */
.cbox-title {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
}

.cbox-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

/* The count is the reason to open it — how much there is inside, without opening it to find out. */
.cbox-count {
    font-family: 'Barlow', sans-serif;
    font-size: 11px;
    padding: 1px 8px;
    border-radius: 10px;
    background: var(--bg);
    border: 1px solid var(--border);
    color: var(--muted);
}

.cbox-chevron { font-size: 10px; color: var(--muted); flex: 0 0 auto; }

.cbox-body { padding: 4px 14px 18px; border-top: 1px solid var(--border); }

/* ===========================================================================
   The record list (.rec-table)
   ---------------------------------------------------------------------------
   One list, used for every set of records hanging off a record: the parts a
   concession covers, what cites it, its attachments, and anything added later.

   It started as the concessions page's own scoped .conc-table — the best
   looking list on that page — while the two beside it were a stack of bordered
   <a> rectangles and a bespoke row. Three patterns on one page. Being scoped,
   it could not be shared: a component such as AttachmentList carries its own
   CSS-isolation id, so the rules never reached it. Hence global, and named for
   what it is rather than for the first page that wanted it.

   The rule that keeps it coherent: A ROW EITHER CLICKS OR CARRIES BUTTONS,
   never both. A clickable row with buttons in it means every button press also
   navigates unless each one stops propagation — correct until someone adds the
   next button and forgets. Clickable rows take .rec-row; rows with controls
   leave it off and put them in a .rec-actions cell.
   =========================================================================== */
.rec-table-wrap { overflow-x: auto; }

.rec-table {
    width: 100%;
    border-collapse: collapse;
    font-family: 'Barlow', sans-serif;
    font-size: 13px;
}

    /* Barlow, matching .rep-table th. Across the app Barlow Condensed is for titles only (.rep-title);
       labels, headers and ids are the body face. */
    .rec-table th {
        text-align: left;
        font-size: 11px;
        letter-spacing: .08em;
        text-transform: uppercase;
        color: var(--muted, var(--text));
        padding: 8px 12px;
        border-bottom: 1px solid var(--border);
        white-space: nowrap;
    }

    .rec-table td {
        padding: 10px 12px;
        border-bottom: 1px solid var(--border);
        vertical-align: top;
    }

    /* The last row sits on the box's own edge; a second line there reads as a stray divider. */
    .rec-table tbody tr:last-child td { border-bottom: 0; }

.rec-row { cursor: pointer; transition: background .12s; }

    .rec-row:hover { background: var(--surface); }

/* A reference the register does not know — a part number on a concession that is not in shared.parts.
   Shown rather than dropped, because a concession lists what it covers and quietly shortening that list
   would hide scope. Dimmed and not clickable: there is nothing to open, and it is a data problem. */
.rec-row-unknown { opacity: .55; cursor: default; }

/* Matches .reg-cell-id on the project register: body face, weighted rather than condensed. */
.rec-cell-id { font-weight: 700; letter-spacing: .04em; white-space: nowrap; }

/* A reference in a row that does not click through, linking to its own record (a part number on a report
   item). Underlined lightly so it reads as a link without the row turning into a list of blue text. */
a.rec-cell-link { color: inherit; text-decoration: underline dotted; text-underline-offset: 3px; }

    a.rec-cell-link:hover { color: var(--accent); text-decoration-style: solid; }
.rec-cell-main { font-weight: 500; }
.rec-cell-muted { color: var(--muted, var(--text)); opacity: .85; }

/* The buttons column. Right-aligned and non-wrapping so the controls stay together and line up down the
   list however long the names to their left run. */
.rec-cell-actions { width: 1%; white-space: nowrap; text-align: right; }

.rec-actions { display: inline-flex; gap: 6px; justify-content: flex-end; }

/* The empty state belongs inside the box, in the body face, saying what the absence MEANS rather than
   just "none" — "no parts linked" is a fact; "not shown on any part or project" is the consequence. */
.rec-empty {
    font-family: 'Barlow', sans-serif;
    font-size: 13px;
    color: var(--muted, var(--text));
    opacity: .85;
    padding: 6px 2px 2px;
}

@media (max-width: 576px) {
    /* Buttons drop under the row's text rather than squeezing the name column to nothing. */
    .rec-cell-actions { width: auto; text-align: left; }
    .rec-actions { justify-content: flex-start; }
}

/* ── List toolbar: search box, filters and toggles above a list ────────────
   Shared by the concessions and licences lists. Wraps so the search keeps its own line on a phone and
   the filters fall underneath. */
.list-toolbar { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; flex-wrap: wrap; }

.list-search {
    flex: 1 1 320px;
    max-width: 460px;
    background: var(--bg);
    border: 1px solid var(--border);
    color: var(--text);
    font-family: 'Barlow', sans-serif;
    font-size: 12px;
    padding: 7px 12px;
}

    .list-search:focus { border-color: var(--accent); outline: none; }

.list-select {
    background: var(--bg);
    border: 1px solid var(--border);
    color: var(--text);
    font-family: 'Barlow', sans-serif;
    font-size: 12px;
    padding: 7px 10px;
}

/* ── Pager (Pager.razor) ───────────────────────────────────────────────────
   Under a paged list: what is showing on the left, the page buttons on the right. Global because a
   shared component's own markup is styled from here (see .rec-table). */
.pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-top: 14px;
    font-family: 'Barlow', sans-serif;
    font-size: 12px;
    color: var(--muted);
}

.pager-pages { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }

.pager-btn {
    min-width: 30px;
    height: 30px;
    padding: 0 8px;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--surface2);
    color: var(--text);
    font-family: inherit;
    font-size: 12px;
    cursor: pointer;
    transition: border-color .12s, color .12s;
}

    .pager-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
    .pager-btn:disabled { opacity: .4; cursor: default; }

    .pager-btn.is-current {
        border-color: var(--accent);
        background: color-mix(in srgb, var(--accent) 12%, var(--surface2));
        color: var(--text);
        font-weight: 600;
        cursor: default;
    }

.pager-gap { padding: 0 4px; }

/* DEV-SW-96 (Phase 5): boxes now stack — a step's Findings, Implications, Investigation and Corrective
   Action are each one. Spacing lives here rather than on every caller, which is how the first two
   (history, attachments) ended up spaced by whatever happened to sit above them. */
.cbox + .cbox { margin-top: 10px; }
.rep-view-grid + .cbox,
.rep-view-group + .cbox { margin-top: 12px; }
.cbox + .rep-view-grid,
.cbox + .rep-view-group { margin-top: 12px; }

/* "Open report ↗" on a folded escalated-report box. A link, not a button: it leaves the NCR for the
   report's own page, and it must not toggle the box on the way (hence stopPropagation at the call site). */
.rep-link-open {
    font-size: 12px;
    color: var(--muted);
    text-decoration: none;
    white-space: nowrap;
}

    .rep-link-open:hover { color: var(--text); text-decoration: underline; }

/* An investigation step: "1.  <question>  <answer>". Laid out as labelled rows rather than
   "question → answer" — an arrow between two sentences leaves the reader to work out which is which,
   and these run long. Collapses to stacked lines on a phone. */
.ncr-why-row {
    display: grid;
    grid-template-columns: 28px minmax(0, 1fr) minmax(0, 1fr);
    gap: 4px 10px;
    padding: 6px 0;
    border-bottom: 1px solid var(--border);
}

    .ncr-why-row:last-child { border-bottom: 0; }

.ncr-why-num { color: var(--muted); font-variant-numeric: tabular-nums; }
.ncr-why-q { color: var(--muted); }
.ncr-why-a { color: var(--text); }

@media (max-width: 576px) {
    .ncr-why-row {
        grid-template-columns: 28px minmax(0, 1fr);
    }

        .ncr-why-row .ncr-why-a { grid-column: 2; }
}

/* ===========================================================================
   Attachments (AttachmentList.razor) + file preview (FilePreview.razor)
   ---------------------------------------------------------------------------
   Global because both are shared components meant for every record that carries
   files. The preview replaces target="_blank": opening a new tab to glance at a
   photo loses the record you were reading.
   =========================================================================== */

.att-block { display: flex; flex-direction: column; gap: 10px; }

.att-head { display: flex; align-items: center; gap: 10px; }

    /* Only when the component draws its own header — in a collapsible box the Attach button is part of
       the box header instead, and lives left of the count. */
    .att-head .btn-attach { margin-left: auto; }

.att-title {
    font-family: 'Barlow', sans-serif;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--text);
}

.att-count {
    font-family: 'Barlow', sans-serif;
    font-size: 11px;
    padding: 1px 8px;
    border-radius: 10px;
    background: var(--bg);
    border: 1px solid var(--border);
    color: var(--muted);
}

/* The Attach control is a <label> wrapping a hidden <input type=file> — the only way to restyle a file
   picker. In a collapsible box it sits in the header, left of the count; standalone, .att-head's
   margin-left:auto on this pushes it to the far end of the heading row. */
.btn-attach {
    display: inline-flex;
    align-items: center;
    padding: 5px 12px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: transparent;
    color: var(--muted);
    font-family: 'Barlow', sans-serif;
    font-size: 11px;
    letter-spacing: .04em;
    cursor: pointer;
    transition: border-color .15s, color .15s;
}

    .btn-attach:hover { border-color: var(--accent); color: var(--accent); }

    .btn-attach--uploading { opacity: .6; cursor: default; }

.att-empty { font-family: 'Barlow', sans-serif; font-size: 12px; color: var(--muted); }

.att-rows { display: flex; flex-direction: column; }

.att-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 9px 0;
    border-bottom: 1px solid var(--border);
}

    .att-row:last-child { border-bottom: none; }

.att-file { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }

.att-name {
    font-family: 'Barlow', sans-serif;
    font-size: 13px;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.att-meta { font-family: 'Barlow', sans-serif; font-size: 11px; color: var(--muted); }

.att-actions { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }

.att-remove:hover { color: var(--danger); }

/* ── Cited concessions (ConcessionLinks.razor) ───────────────────────────────
   Built on the attachment rows: both are "other records hanging off this one",
   and two layouts for the same idea would read as two features.
   ─────────────────────────────────────────────────────────────────────────── */

.conc-cite-ref {
    font-weight: 700;
    letter-spacing: .04em;
    color: var(--accent);
    text-decoration: none;
    margin-right: 8px;
}

    .conc-cite-ref:hover { text-decoration: underline; }

.conc-cite-picker { margin-top: 12px; }

/* Spaces the cited-concessions box off the record above it. The report and NCR detail grids have no
   bottom margin, so without this the box sits flush against the last field. Top only — .rep-tabs and
   .ncr-activity below it already carry their own top margin. */
/* The ConcessionLinks panel's wrapper. Was declared twice, here and again 450 lines below — the second
   copy belonged to the concessions page's own "Cited by" list, which shared the name by accident. The
   two merged silently, so a change to either reached both components. One declaration now. */
.conc-links { margin-top: 16px; display: flex; flex-direction: column; gap: 6px; }

@media (max-width: 576px) {
    .att-row { flex-direction: column; align-items: stretch; }
    .att-actions { justify-content: flex-end; }
}

/* ── File preview overlay ──────────────────────────────────────────────────── */

.fprev {
    position: fixed;
    inset: 0;
    z-index: 9500;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 32px;
    background: rgb(0 0 0 / .8);
}

/* Always rendered so it can be reparented onto <html> once; hidden rather than removed. */
.fprev--hidden { display: none; }

.fprev-panel {
    display: flex;
    flex-direction: column;
    /* A document viewer earns most of the window: a page of A4 rendered into a small box is legible only
       by zooming, which is a fight with the viewer rather than reading. */
    width: min(1500px, 96vw);
    height: min(1100px, 94vh);
    border-radius: 8px;
    overflow: hidden;
    background: var(--surface);
    box-shadow: 0 12px 48px rgb(0 0 0 / .6);
}

/* Maximised: the panel takes the whole browser viewport, backdrop and gutter included. The PANEL has to
   grow for this to mean anything — the frame inside is sized off it, so enlarging only the backdrop
   leaves the rendered page exactly as it was. */
.fprev--max { padding: 0; }

.fprev--max .fprev-panel {
    width: 100vw;
    height: 100vh;
    max-width: none;
    max-height: none;
    border-radius: 0;
}

.fprev-head {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--border);
    background: var(--surface2);
}

.fprev-name {
    flex: 1 1 auto;
    min-width: 0;
    font-family: 'Barlow', sans-serif;
    font-size: 13px;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.fprev-download { flex: 0 0 auto; padding: 5px 14px !important; font-size: 11px !important; }

.fprev-close {
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    border: none;
    border-radius: 50%;
    background: transparent;
    color: var(--muted);
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
}

    .fprev-close:hover { background: var(--bg); color: var(--text); }

.fprev-body {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg);
    /* A zoomed image is scaled past the panel; clip it here rather than letting it cover the header.
       Positioned so the zoom readout can sit over the corner of it. */
    overflow: hidden;
    position: relative;
}

/* The image's zoom level, where a PDF viewer puts its own. Also the button back to a fit, which matters
   most when zoomed far enough in that the picture no longer tells you where you are. */
.fprev-zoom {
    position: absolute;
    right: 12px;
    bottom: 12px;
    padding: 4px 10px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: rgb(0 0 0 / .62);
    color: #fff;
    font-family: 'Barlow', sans-serif;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .08em;
    font-variant-numeric: tabular-nums;   /* so the badge does not twitch as the number changes */
    cursor: pointer;
    user-select: none;
}

    .fprev-zoom:hover { background: rgb(0 0 0 / .8); }

/* contain, not cover: a drawing cropped to fit is worse than one with space around it. */
.fprev-img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    /* Zoom scales about the middle and is then nudged so the point under the cursor holds still;
       touch-action keeps a drag to pan from also scrolling the page under it. */
    transform-origin: center center;
    touch-action: none;
    user-select: none;
}

.fprev-frame { width: 100%; height: 100%; border: none; background: #fff; }

.fprev-none {
    text-align: center;
    font-family: 'Barlow', sans-serif;
    font-size: 13px;
    color: var(--muted);
}

.fprev-none-sub { font-size: 11px; opacity: .7; }

/* ===========================================================================
   Activity panel (ActivityPanel.razor)
   ---------------------------------------------------------------------------
   Jira's Activity section: one heading, a row of tabs, and the conversation and
   the audit trail in one place instead of two boxes that scroll past each other.
   =========================================================================== */

.act-panel { margin-top: 24px; }

.act-head {
    font-family: 'Barlow', sans-serif;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--text);
    margin-bottom: 10px;
}

/* A segmented control, not underlined tabs: there are only three and they are a filter on one list,
   which a joined group says better than three separate headings. */
.act-tabs {
    display: inline-flex;
    gap: 2px;
    padding: 2px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface2);
    margin-bottom: 16px;
    flex-wrap: wrap;
}

.act-tab {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: none;
    background: transparent;
    border-radius: 6px;
    cursor: pointer;
    padding: 6px 14px;
    font-family: 'Barlow', sans-serif;
    font-size: 12px;
    color: var(--muted);
    transition: background .12s, color .12s;
}

    .act-tab:hover { color: var(--text); }

    .act-tab.is-active {
        background: var(--surface);
        color: var(--text);
        font-weight: 600;
        box-shadow: 0 1px 2px rgb(0 0 0 / .12);
    }

.act-tab-count {
    font-size: 10px;
    padding: 0 6px;
    border-radius: 9px;
    background: var(--bg);
    border: 1px solid var(--border);
    color: var(--muted);
}

.act-composer { margin-bottom: 20px; }

/* ── A record's linked records (LinkedListsPanel) ──
   The Activity panel's heading and tabs over lists of what a part, project or document is linked to. The list has
   a limited height and scrolls on its own, so a long one never pushes the rest of the record off the screen; the
   tab's controls (search, toggles, add buttons) sit above it and stay in view. */
.link-panel { margin-top: 28px; }

.link-panel .act-tabs { margin-bottom: 12px; }

.link-panel-tools {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 14px;
    margin-bottom: 10px;
}

.link-panel-body {
    max-height: min(440px, 60vh);
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
}

.link-panel-empty {
    padding: 12px 2px;
    font-size: 13px;
    color: var(--muted);
}

.link-panel-note {
    padding: 8px 2px 0;
    font-size: 12px;
    color: var(--muted);
}

/* A ticket's status in a linked list, in the status's colour (the tickets pages' status badge). */
.link-ticket-status {
    display: inline-block;
    padding: 2px 8px;
    font-family: 'Barlow', sans-serif;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    border: 1px solid var(--border);
    border-radius: 4px;
    color: var(--muted);
    white-space: nowrap;
}

    .link-ticket-status.status-neutral { border-color: var(--border);  color: var(--muted); }
    .link-ticket-status.status-muted   { border-color: var(--muted);   color: var(--muted); }
    .link-ticket-status.status-accent  { border-color: var(--accent);  color: var(--accent); }
    .link-ticket-status.status-success { border-color: var(--success); color: var(--success); }
    .link-ticket-status.status-info    { border-color: var(--info);    color: var(--info); }
    .link-ticket-status.status-warning { border-color: var(--warning); color: var(--warning); }
    .link-ticket-status.status-purple  { border-color: var(--purple);  color: var(--purple); }
    .link-ticket-status.status-pink    { border-color: var(--pink);    color: var(--pink); }
    .link-ticket-status.status-teal    { border-color: var(--teal);    color: var(--teal); }
    .link-ticket-status.status-orange  { border-color: var(--orange);  color: var(--orange); }
    .link-ticket-status.status-danger  { border-color: var(--danger);  color: var(--danger); }

/* A comment inside the merged stream. .hist-list sets the rhythm for history rows, and this keeps a
   comment on the same spacing so the two read as one timeline rather than two interleaved lists. */
.act-row--comment { margin: 0; }

    .act-row--comment .comment-item { padding: 0; border-bottom: none; }

/* The stream as one group, so where one entry ends and the next begins is visible — a comment and the
   history line under it no longer run together. The group carries the border and the rounded corners;
   inside it, entries are only divided, never rounded or boxed apart.

   Each entry is the page's box colour (.cbox uses --surface2), so it matches the other boxes on the page
   and follows the theme.

   No overflow:hidden on the group to clip its corners: it would cut off the user hover cards, which
   open out of these entries. The first and last entries round their own corners to match instead. */
.act-stream .hist-list,
.act-stream .comments-thread {
    gap: 0;
    margin-bottom: 0;
    border: 1px solid var(--border);
    border-radius: 8px;
}

    .act-stream .hist-list > *,
    .act-stream .comments-thread > * {
        padding: 12px 14px;
        background: var(--surface2);
        /* The page background, not --border: in the dark theme --border is within a shade of --surface2
           and the line vanishes, so entries ran together. The page colour reads as a gap in both themes,
           and 3px makes it unmistakable at a glance without breaking the group into separate boxes. */
        border-bottom: 3px solid var(--bg);
    }

    .act-stream .hist-list > :first-child,
    .act-stream .comments-thread > :first-child {
        border-top-left-radius: 7px;
        border-top-right-radius: 7px;
    }

    .act-stream .hist-list > :last-child,
    .act-stream .comments-thread > :last-child {
        border-bottom: none;
        border-bottom-left-radius: 7px;
        border-bottom-right-radius: 7px;
    }

/* ===========================================================================
   Comment threads
   ---------------------------------------------------------------------------
   The same .comment-* block was copied into six scoped stylesheets (incident,
   process and product-fault reports, NCRs, documents, tickets) before it landed
   here. Those copies still exist and win inside their own pages; this is the
   version new pages use, so a seventh copy is not made. Worth collapsing the
   others into this when one of them is next touched.
   =========================================================================== */

/* Same rhythm as .hist-list, because a comment IS a .hist-item here — bubble, name, relative time, then
   the content. The gap matches so the two interleave cleanly on the Activity panel's All tab. */
.comments-thread { display: flex; flex-direction: column; gap: 22px; margin-bottom: 20px; }

.comment-item { padding: 0; border-bottom: none; }

.comment-actions { margin-left: auto; display: flex; gap: 6px; }

/* No white-space: pre-wrap — the body is rich text and brings its own paragraphs. The older scoped
   copies on the report and ticket pages still set it, from when comments were plain text. */
.comment-body {
    font-size: 13px;
    color: var(--text);
    line-height: 1.6;
    margin: 8px 0 0;
}

/* Kept for the pages still on the old markup (reports, NCRs, tickets, documents). New surfaces build on
   .hist-item instead — see the comment template on the concessions page. */
.comment-header { display: flex; align-items: baseline; gap: 10px; margin-bottom: 6px; flex-wrap: wrap; }

.comment-author { font-size: 12px; font-weight: 600; color: var(--text); }
.comment-time { font-size: 10px; color: var(--muted); letter-spacing: .04em; }

.comment-edit-actions { display: flex; gap: 8px; margin-top: 8px; }
.comment-input-row { margin-top: 4px; }

/* ===========================================================================
   User hover card (UserHoverCard.razor)
   ---------------------------------------------------------------------------
   Hovering anything that names a person shows who they are and how to reach
   them, the way Teams does.

   Show/hide is pure CSS on purpose. A Blazor flag would mean a server round
   trip before the card appeared, so it would always trail the pointer; this
   way the card is on screen immediately and fills in when the fetch returns.

   GLOBAL, like .hist-*: a person should look the same wherever they are named.
   =========================================================================== */

/* flex:0 0 auto because the anchor now stands where the avatar used to in .hist-item's flex row — without
   it the bubble squashes when a history line runs long. */
.uc-anchor { position: relative; display: inline-flex; vertical-align: middle; flex: 0 0 auto; }

.uc-pop {
    position: absolute;
    z-index: 500;
    top: calc(100% + 8px);
    left: 0;
    /* Sized so a full name@monitech.co.za address fits on one line — an email broken across two lines is
       the one thing on this card nobody can use at a glance. */
    width: 300px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 14px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
    box-shadow: 0 8px 24px rgb(0 0 0 / .18);
    font-family: 'Barlow', sans-serif;
    text-align: left;
    cursor: default;

    /* Hidden rather than absent so there is something to transition, and so the markup does not churn
       on every pointer move. visibility (not just opacity) keeps it out of the hit-testing when shut. */
    visibility: hidden;
    opacity: 0;
    transform: translateY(-4px);
    transition: opacity .12s ease, transform .12s ease, visibility 0s linear .3s;
}

/* The delay is what stops a card firing at every name the pointer crosses on its way somewhere else.
   Hiding is immediate; .uc-pop:hover keeps it open while the pointer is inside, so the email and
   extension are actually clickable rather than vanishing on the way to them. */
.uc-anchor:hover > .uc-pop,
.uc-anchor:focus-within > .uc-pop,
.uc-pop:hover {
    visibility: visible;
    opacity: 1;
    transform: translateY(0);
    transition-delay: .35s, .35s, 0s;
}

/* A bridge over the gap between the trigger and the card, so the pointer does not cross dead space
   on the way down and close it. Follows the card when it flips. */
.uc-pop::before {
    content: "";
    position: absolute;
    top: -8px;
    left: 0;
    right: 0;
    height: 8px;
}

.uc-pop.popup-up::before { top: auto; bottom: -8px; }

/* Flipped by uiPlacePopup when the trigger is near the bottom or the right of the window — otherwise a
   card on the last row of a list opens into the fold and is unreadable. */
.uc-pop.popup-up {
    top: auto;
    bottom: calc(100% + 8px);
    transform: translateY(4px);
}

.uc-anchor:hover > .uc-pop.popup-up,
.uc-anchor:focus-within > .uc-pop.popup-up,
.uc-pop.popup-up:hover { transform: translateY(0); }

.uc-pop.popup-left { left: auto; right: 0; }

.uc-pop-head { display: flex; align-items: center; gap: 10px; }

.uc-photo {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    object-fit: cover;
}

.uc-photo--initials {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 15px;
    font-weight: 600;
    letter-spacing: .04em;
    user-select: none;
}

.uc-ident { display: flex; flex-direction: column; min-width: 0; }

.uc-name { font-size: 14px; font-weight: 600; color: var(--text); line-height: 1.3; }
.uc-job { font-size: 12px; color: var(--muted); line-height: 1.3; }

/* Someone who has left. Said plainly, because the alternative is ringing an extension nobody answers. */
.uc-gone { font-size: 11px; font-weight: 600; color: var(--danger); margin-top: 2px; }

.uc-rows { display: flex; flex-direction: column; gap: 6px; }

.uc-row { display: flex; gap: 8px; font-size: 12px; line-height: 1.4; }

.uc-k { flex: 0 0 68px; color: var(--muted); }
.uc-v { min-width: 0; color: var(--text); overflow-wrap: anywhere; }

.uc-link { color: var(--accent); text-decoration: none; }
    .uc-link:hover { text-decoration: underline; }

.uc-loading { font-size: 12px; color: var(--muted); }

/* On a touch screen there is no hover, and a card that can never open is worse than no card: it leaves
   a tap target that does nothing. The name still reads normally. */
@media (hover: none) {
    .uc-pop { display: none; }
}

/* ===========================================================================
   Concession form (Concessions.razor + ConcessionForm.razor)
   ---------------------------------------------------------------------------
   Global rather than scoped because TWO components render this form now: the
   concessions page and the standalone raise/edit page. Scoped CSS only applies
   to its own component, so a scoped copy would leave one of them unstyled.
   =========================================================================== */

/* ── Forms (inside the shared rep-modal) ─────────────────────────────────── */
/* Form label — same type as .rep-field label in app.css. */
.conc-label {
    display: block;
    font-size: 11px;
    letter-spacing: .08em;
    text-transform: uppercase;
    margin: 14px 0 5px;
}

.conc-opt { opacity: .6; text-transform: none; letter-spacing: 0; }

.conc-input {
    width: 100%;
    box-sizing: border-box;
    background: var(--bg);
    border: 1px solid var(--border);
    color: var(--text);
    font-family: 'Barlow', sans-serif;
    font-size: 13px;
    padding: 8px 10px;
}

    .conc-input:focus { border-color: var(--accent); outline: none; }

.conc-textarea { resize: vertical; min-height: 60px; }

.conc-hint { font-family: 'Barlow', sans-serif; font-size: 11px; opacity: .65; margin-top: 4px; }

.conc-form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }

/* ── Raise / edit page (ConcessionForm) ──────────────────────────────────── */

/* Capped rather than full width: a justification is prose, and prose set across a 2000px monitor is
   unreadable. Wide enough for the editor toolbars not to wrap. */
.conc-form-page { max-width: 900px; }

/* Save and Cancel live in the page header (see ConcessionForm). What was here was a sticky footer bar,
   which sat on top of whatever the form had below it — in practice the financial details editor. */
.conc-form-footnote {
    font-family: 'Barlow', sans-serif;
    font-size: 11px;
    opacity: .65;
    margin: 24px 0 8px;
}

@media (max-width: 576px) {
    .conc-form-row { grid-template-columns: 1fr; }
}

.conc-toggle { margin-top: 16px; }

.conc-picker { max-height: 170px; overflow-y: auto; border: 1px solid var(--border); margin-top: 6px; }

.conc-picker-item {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    width: 100%;
    text-align: left;
    background: transparent;
    border: none;
    border-bottom: 1px solid var(--border);
    color: var(--text);
    font-family: 'Barlow', sans-serif;
    font-size: 13px;
    padding: 8px 10px;
    cursor: pointer;
}

    .conc-picker-item:last-child { border-bottom: none; }
    .conc-picker-item:hover { background: var(--surface); }
    .conc-picker-item.selected { background: var(--surface); border-left: 3px solid var(--accent); }

.conc-picker-dept { opacity: .6; font-size: 11px; }

/* ── Part picker chips ───────────────────────────────────────────────────── */
/* The editable variant carries a part number, its name and a remove control, so it is taller and wider
   than the read-only chips on the detail view. */
.conc-chips--editable { margin-bottom: 10px; }

.conc-chip--removable { display: inline-flex; align-items: center; gap: 8px; padding: 4px 4px 4px 10px; }

.conc-chip-ref { font-weight: 600; }

/* Truncates rather than wrapping: a long part name should not make one chip taller than its neighbours. */
.conc-chip-name { opacity: .7; max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.conc-chip-x {
    background: transparent;
    border: none;
    color: var(--text);
    font-size: 16px;
    line-height: 1;
    padding: 2px 6px;
    cursor: pointer;
    opacity: .6;
    transition: opacity .12s, color .12s;
}

    .conc-chip-x:hover { opacity: 1; color: var(--danger, #e05555); }


.conc-chips { display: flex; flex-wrap: wrap; gap: 6px; }

.conc-chip {
    font-size: 12px;
    letter-spacing: .04em;
    padding: 3px 9px;
    border: 1px solid var(--border);
    background: var(--bg);
}

/* .conc-link / .conc-link-ref are gone. They drew the concessions page's "Cited by" list as a stack of
   square-cornered bordered <a> rectangles inside a rounded box — boxes in a box whose corners did not even
   agree — with the reference and title run together on one line. That list is a .rec-table now.
   .conc-links survives because the SHARED ConcessionLinks component (the "cite a concession" panel on
   reports and NCRs) still uses it as its wrapper; its single declaration is up beside that component's
   other rules rather than down here, where it was a second copy nobody would have found. */

.conc-approval {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    padding: 10px 0;
    border-bottom: 1px solid var(--border);
}

    .conc-approval:last-child { border-bottom: none; }

.conc-approval-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }

/* Matches the extra-small button size used on the register and document pages, which define it in their
   own scoped sheets and so cannot be reused from here. */
.reg-btn-xs { padding: 4px 10px; font-size: 11px; }

/* ===========================================================================
   Authenticator setup in the second sign-in step
   ---------------------------------------------------------------------------
   The QR an external user scans in TotpCodeDialog. Global because two pages show
   it — the login form after a password, and the Microsoft sign-in callback.
   =========================================================================== */

.totp-login-qr {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    margin: 4px 0 14px;
}

    .totp-login-qr img {
        width: 180px;
        height: 180px;
        background: #fff;
        padding: 6px;
    }

    .totp-login-qr summary {
        cursor: pointer;
        font-size: 12px;
        color: var(--muted);
    }

.totp-login-uri {
    margin-top: 6px;
    font-family: ui-monospace, "Cascadia Code", Consolas, monospace;
    font-size: 11px;
    word-break: break-all;
    color: var(--muted);
}

/* A comment a link pointed at (uiFocusComment): an accent ring that fades, so the eye finds it after the scroll. */
.comment-focus {
    border-radius: 6px;
    animation: comment-focus 3s ease-out;
}

@keyframes comment-focus {
    0%, 40% { box-shadow: 0 0 0 2px var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
    100% { box-shadow: 0 0 0 2px transparent; background: transparent; }
}
