/* IBM Plex, imported here rather than linked from twenty-four templates.
   Drawn for enterprise technical systems, which is what this is: half the
   text on these pages is a string that has to be read character by character
   -- REF02 (REF01=0F), wd5-services1.myworkday.com, an X12 segment.

   SERVED FROM THIS BOX, not from Google. It used to be an @import from
   fonts.googleapis.com, and that had to go for two reasons. The content
   security policy added with the response headers allows styles and fonts
   from this origin only, and an @import to somewhere else is exactly what
   such a policy exists to stop. The better reason is the one that would
   stand without the policy: an application holding benefits data should not
   tell a third party, on every page load, which of its pages somebody is
   looking at.

   The Sans is a VARIABLE font -- Google serves the same file for 400, 500,
   600 and 700, byte for byte -- so it is declared once across the range
   rather than downloaded four times. The Mono weights really are separate
   files.

   Latin only. Every word in this application is English, and the other five
   subsets Google offers would be five more files per weight for characters
   that never appear. */
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 100 700;
  font-display: swap;
  src: url('/static/fonts/IBMPlexSans.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/IBMPlexMono-400.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/IBMPlexMono-500.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/IBMPlexMono-600.woff2') format('woff2');
}

/* ---------------------------------------------------------------------------
   Tokens.

   The old palette was a warm cream ground with a terracotta accent, and it had
   one problem beyond taste: the accent and the warning were the same hue
   family, so "this needs your attention" and "this is a button" competed. The
   semantics here are deliberately nowhere near the accent.

   Every colour below is defined in this bare :root block. Anything defined
   only inside a dark-mode block would be undefined for the viewers who have
   no preference set at all, which is most of them.
   --------------------------------------------------------------------------- */
:root {
  /* Ground and surfaces. Cool, with a slight blue bias -- a flat neutral grey
     reads as unconsidered. */
  --bg: #e8ecef;
  --surface: #ffffff;
  --surface-2: #f4f6f8;
  --surface-3: #eef2f5;

  --ink: #0c1116;
  --ink-2: #33404c;
  --muted: #5c6b78;

  --border: #d3dae0;
  --line-soft: #e6eaee;
  --field-border: #9aa7b2;
  --field-border-hover: #64727e;
  --panel-border: #d3dae0;

  /* One accent, spent on the primary action and on "you are here". */
  --accent: #0b6e5f;
  --accent-ink: #075347;
  --accent-quiet: #dceae6;
  --accent-bright: #0f8a76;
  /* What sits ON a filled accent or ink surface. Needed because both invert
     between themes: white on a light-mode pine button is right, and white on
     the dark theme's mint one is unreadable. */
  --on-accent: #ffffff;
  --on-ink: #ffffff;

  /* Semantics, kept off the accent. */
  --error: #b3261e;
  --error-bg: #fdeceb;
  --error-line: #f3c9c6;
  --warning: #8a5a00;
  --warning-bg: #fdf3e2;
  --warning-line: #eddcbb;
  --ok: #14734f;
  --ok-bg: #e4f3ec;
  --ok-line: #b9dfcd;
  --info: #1f5fa8;
  --info-bg: #e8f0fa;
  --info-line: #c6d9ef;

  /* Comment authors' tones: mid-weight hues that read on both grounds. */
  --who-0: #2b8a7e;
  --who-1: #3f6fc4;
  --who-2: #b0652a;
  --who-3: #8a52b8;
  --who-4: #b8436e;
  --who-5: #6f8a2b;

  --radius: 10px;
  --radius-sm: 6px;
  --shadow: 0 1px 2px rgba(12, 17, 22, .05), 0 8px 24px -18px rgba(12, 17, 22, .35);

  --sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* Chart-only status colors -- deliberately distinct from each other for
     wedge separation, independent of the status chips above. Picked for max
     hue separation so no two wedges read as "basically the same color". */
  --chart-error: #c2410c;
  --chart-failed: #6d28d9;
  --chart-warning: #a16207;
  --chart-aborted: #0e7490;
  --chart-ok: #15803d;
  --chart-idle: #64748b;
  --chart-stalled: #9f1239;
}

/* Three states, not two. An explicit choice stamps data-theme; the default
   "system" setting stamps nothing, so prefers-color-scheme is all that
   separates light from dark for most people. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0d1418;
    --surface: #141d23;
    --surface-2: #1a242b;
    --surface-3: #1f2a32;
    --ink: #e8eef2;
    --ink-2: #b7c4cd;
    --muted: #8899a5;
    --border: #2a3841;
    --line-soft: #212d35;
    --field-border: #3d4e5a;
    --field-border-hover: #5f7382;
    --panel-border: #2a3841;
    --accent: #4bbfa6;
    --accent-ink: #8fdcc9;
    --accent-quiet: #17332e;
    --accent-bright: #63d6bc;
    --on-accent: #06231e;
    --on-ink: #0d1418;
    --error: #ef8079;
    --error-bg: #34191a;
    --error-line: #5b2b28;
    --warning: #d9a441;
    --warning-bg: #302512;
    --warning-line: #4d3c1b;
    --ok: #56be93;
    --ok-bg: #12291f;
    --ok-line: #234535;
    --info: #74a9e6;
    --info-bg: #14212f;
    --info-line: #274059;
    --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 8px 24px -18px rgba(0, 0, 0, .9);
    --chart-error: #fb923c;
    --chart-failed: #a78bfa;
    --chart-warning: #fbbf24;
    --chart-aborted: #38bdf8;
    --chart-ok: #4ade80;
    --chart-idle: #94a3b8;
    --chart-stalled: #fb7185;
  }
}
:root[data-theme="dark"] {
  --bg: #0d1418;
  --surface: #141d23;
  --surface-2: #1a242b;
  --surface-3: #1f2a32;
  --ink: #e8eef2;
  --ink-2: #b7c4cd;
  --muted: #8899a5;
  --border: #2a3841;
  --line-soft: #212d35;
  --field-border: #3d4e5a;
  --field-border-hover: #5f7382;
  --panel-border: #2a3841;
  --accent: #4bbfa6;
  --accent-ink: #8fdcc9;
  --accent-quiet: #17332e;
  --accent-bright: #63d6bc;
  --on-accent: #06231e;
  --on-ink: #0d1418;
  --error: #ef8079;
  --error-bg: #34191a;
  --error-line: #5b2b28;
  --warning: #d9a441;
  --warning-bg: #302512;
  --warning-line: #4d3c1b;
  --ok: #56be93;
  --ok-bg: #12291f;
  --ok-line: #234535;
  --info: #74a9e6;
  --info-bg: #14212f;
  --info-line: #274059;
  --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 8px 24px -18px rgba(0, 0, 0, .9);
  --chart-error: #fb923c;
  --chart-failed: #a78bfa;
  --chart-warning: #fbbf24;
  --chart-aborted: #38bdf8;
  --chart-ok: #4ade80;
  --chart-idle: #94a3b8;
  --chart-stalled: #fb7185;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  color: var(--ink);
  background: var(--bg);
}

header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  --header-pad-x: 28px;
  --header-pad-y: 14px;
  padding: var(--header-pad-y) var(--header-pad-x);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

header a { color: var(--muted); text-decoration: none; margin-left: 16px; }
header a:hover { text-decoration: underline; }

.mode-badge {
  margin-left: 12px;
  font-size: 12px;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 2px 10px;
}

/* ---------------------------------------------------------------- nav ---
   Two rows: the menus, then the trail. Every selector here is a class, so
   it outranks the plain `header a` rule above without needing !important.
   ----------------------------------------------------------------------- */

.app-header { gap: 16px; flex-wrap: wrap; }

.brand-block { display: flex; align-items: center; min-width: 0; }

.app-header .brand {
  margin-left: 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
  letter-spacing: -0.01em;
}

.nav { display: flex; align-items: center; gap: 2px; }
.nav > a { margin-left: 14px; }

.nav-menu { position: relative; }

.nav-trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border-radius: 6px;
  font-size: 14px;
  color: var(--muted);
  cursor: pointer;
  user-select: none;
  list-style: none;
  white-space: nowrap;
}
.nav-trigger::-webkit-details-marker { display: none; }
.nav-trigger::marker { content: ""; }
.nav-trigger:hover { background: var(--surface-2); color: var(--ink); }
.nav-trigger:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.nav-trigger.is-active { color: var(--ink); font-weight: 500; }

.nav-trigger .caret {
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid currentColor;
  opacity: 0.55;
}
.nav-menu[open] > .nav-trigger { background: var(--surface-2); color: var(--ink); }
.nav-menu[open] > .nav-trigger .caret { transform: rotate(180deg); }

.nav-panel {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  min-width: 212px;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 10px 28px rgba(26, 29, 35, 0.11);
}
.nav-menu-end .nav-panel { left: auto; right: 0; }

.nav-panel a {
  display: block;
  margin-left: 0;
  padding: 7px 10px;
  border-radius: 5px;
  font-size: 14px;
  color: var(--ink);
  white-space: nowrap;
}
.nav-panel a:hover { background: var(--bg); text-decoration: none; }
.nav-panel a.is-current { color: var(--accent); font-weight: 500; }

.nav-sep { border: 0; border-top: 1px solid var(--border); margin: 5px 4px; }

.crumbs {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 9px 28px;
  font-size: 13px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.crumbs .trail { display: flex; align-items: center; gap: 7px; color: var(--muted); }
.crumbs .trail a { color: var(--muted); text-decoration: none; }
.crumbs .trail a:hover { text-decoration: underline; }
/* After .trail a, and matching it selector-for-selector, so the back link
   keeps the accent whether it sits beside the trail or inside it. */
.crumbs a.back,
.crumbs .trail a.back { color: var(--accent); text-decoration: none; font-weight: 500; }
.crumbs a.back:hover,
.crumbs .trail a.back:hover { text-decoration: underline; }
.crumbs .crumb-now { color: var(--ink); font-weight: 500; }
.crumbs .sep { color: var(--border); }

@media (max-width: 720px) {
  header { --header-pad-x: 16px; --header-pad-y: 12px; }
  .crumbs { padding: 8px 16px; }
  .nav { flex-wrap: wrap; }
  .nav-trigger { padding: 6px 8px; }
}

main { max-width: 960px; margin: 0 auto; padding: 28px; }

h1 { font-size: 20px; }

.toolbar { display: flex; justify-content: flex-end; align-items: center; gap: 10px; margin: 20px 0; }

.chart-panel h2 {
  margin: 0 0 16px;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .04em;
}
.pie-wrap { display: flex; align-items: center; gap: 24px; flex-wrap: wrap; }
/* The fallback slice colour, set HERE on the svg rather than on
   `.pie-chart path`. An SVG path with no fill declared anywhere is painted
   black, which is what a status this stylesheet has no rule for came out as;
   its legend swatch, being a div, came out as nothing at all. But a fallback
   written as `.pie-chart path` would be more specific than the bare
   `.slice-failed` classes and would grey out every slice instead. `fill` is
   inherited, so putting it on the svg lets a slice class override it while
   still covering a class that does not exist. The status vocabulary is
   Workday's and is allowed to grow. */
.pie-chart { width: 88px; height: 88px; flex-shrink: 0; fill: var(--chart-idle); }
.pie-chart path { stroke: var(--surface); stroke-width: 2; transition: opacity .15s; cursor: default; }
.pie-chart path:hover { opacity: .85; }
.pie-legend { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.pie-legend li { display: flex; align-items: center; gap: 8px; font-size: 14px; }
.legend-count { color: var(--muted); }
.swatch { width: 12px; height: 12px; border-radius: 3px; display: inline-block; flex-shrink: 0; background: var(--chart-idle); }

.slice-error, .swatch.slice-error { fill: var(--chart-error); background: var(--chart-error); }
.slice-failed, .swatch.slice-failed { fill: var(--chart-failed); background: var(--chart-failed); }
.slice-warning, .swatch.slice-warning { fill: var(--chart-warning); background: var(--chart-warning); }
.slice-aborted, .swatch.slice-aborted { fill: var(--chart-aborted); background: var(--chart-aborted); }

table { width: 100%; border-collapse: collapse; margin-top: 16px; background: var(--surface); }
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--border); font-size: 14px; }
th { color: var(--muted); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: .04em; }

.message-cell { color: var(--muted); }

.status { padding: 2px 10px; border-radius: 999px; font-size: 12px; font-weight: 600; }
.status-error, .status-failed { background: var(--error-line); color: var(--error); }
.status-aborted { background: var(--warning-bg); color: var(--warning); }
.status-warning { background: var(--warning-bg); color: var(--warning); }

.category { font-size: 12px; color: var(--accent); }
.not-triaged { font-size: 12px; color: var(--muted); font-style: italic; }

button {
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  border-radius: 6px;
  padding: 9px 16px;
  font-size: 14px;
  cursor: pointer;
}
button.secondary { background: var(--surface); color: var(--ink); border: 1px solid var(--border); }

.login-wrap {
  max-width: 380px;
  margin: 12vh auto;
  background: var(--surface);
  padding: 32px;
  border-radius: 10px;
  border: 1px solid var(--border);
}
.brand-banner {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: -32px -32px 22px;
  padding: 22px 28px;
  background: linear-gradient(135deg, var(--ink) 0%, var(--ink-2) 100%);
  border-radius: 9px 9px 0 0;
  border-bottom: 3px solid var(--accent);
}
.brand-mark { width: 40px; height: 40px; flex-shrink: 0; }
.brand-text { display: flex; flex-direction: column; gap: 4px; line-height: 1; }
.brand-name { font-size: 19px; font-weight: 700; color: var(--on-ink); letter-spacing: .01em; }
.brand-num { color: var(--accent-bright); }
.brand-tag {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .18em;
  color: rgba(255, 255, 255, .62);
}
.login-wrap .brand-banner + h1 { margin-top: 0; }
.login-wrap h1 { font-size: 20px; margin-bottom: 4px; }
.subtitle { color: var(--muted); font-size: 14px; margin-top: 0; }
.login-wrap form { display: flex; flex-direction: column; gap: 14px; margin-top: 18px; }
.login-wrap label { font-size: 13px; color: var(--muted); display: flex; flex-direction: column; gap: 6px; }
.login-wrap input { padding: 9px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 14px; }
.hint { font-size: 12px; color: var(--muted); margin-top: 18px; }
.error { color: var(--error); font-size: 14px; }

.panel { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 20px; margin-top: 16px; }
.message-block { white-space: pre-wrap; background: var(--bg); padding: 12px; border-radius: 6px; font-size: 13px; }
.meta { color: var(--muted); font-size: 13px; }
.empty { color: var(--muted); }

.dropzone {
  position: relative;
  border: 2px dashed var(--border);
  border-radius: 10px;
  padding: 32px 20px;
  text-align: center;
  background: var(--bg);
  transition: border-color .15s, background .15s;
}
.dropzone-active { border-color: var(--accent); background: var(--surface); }
.dropzone input[type="file"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}
.dropzone-text { margin: 0; font-size: 14px; color: var(--ink); pointer-events: none; }
.dropzone-text strong { color: var(--accent); }
.dropzone-filename { margin: 10px 0 0; font-size: 13px; color: var(--muted); pointer-events: none; }

button:disabled { opacity: .6; cursor: not-allowed; }

.processing-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(26, 29, 35, 0.45);
  z-index: 1000;
  align-items: center;
  justify-content: center;
}
.processing-overlay.active { display: flex; }
.processing-box {
  background: var(--surface);
  border-radius: 10px;
  padding: 28px 36px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .18);
}
.spinner {
  width: 34px;
  height: 34px;
  border: 4px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.processing-text { margin: 0; font-size: 14px; color: var(--ink); font-weight: 600; }
.processing-hint { margin: -6px 0 0; font-size: 13px; color: var(--muted); max-width: 380px; text-align: center; line-height: 1.45; }
/* Reserves its line height even when empty, so the box doesn't jump the
   moment the elapsed counter first appears. */
.processing-elapsed { margin: 0; font-size: 12px; color: var(--muted); min-height: 15px; font-variant-numeric: tabular-nums; }

/* ---- tools: two-file compare ---- */
.dropzone-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 700px) { .dropzone-pair { grid-template-columns: 1fr; } }
.dropzone-slot > label { display: block; font-size: 13px; font-weight: 600; color: var(--ink); margin-bottom: 6px; }
.dropzone-slot .slot-hint { margin: 6px 0 0; font-size: 12px; color: var(--muted); }
.snapshot-label { margin-top: 8px; }
.snapshot-label input { width: 100%; padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 13px; box-sizing: border-box; }
.form-error { display: none; padding: 10px 14px; border-radius: 6px; font-size: 14px; margin: 14px 0 0; background: var(--error-bg); color: var(--error); border: 1px solid var(--error-line); }
.form-notes { display: none; padding: 10px 14px 10px 6px; border-radius: 6px; font-size: 13px; margin: 14px 0 0; background: var(--warning-bg); color: var(--warning); border: 1px solid var(--warning-line); }
.form-notes ul { margin: 0; padding-left: 22px; }
.form-notes li + li { margin-top: 6px; }

/* ---- carrier templates ---- */
.issue-list { margin: 8px 0 0; padding-left: 22px; font-size: 14px; }
.issue-list li + li { margin-top: 6px; }
.issue-list-error li { color: var(--error); }
.issue-list-warn li { color: var(--warning); }
.code-area { width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12.5px; line-height: 1.5; padding: 10px; border: 1px solid var(--border); border-radius: 6px; box-sizing: border-box; tab-size: 2; }
.source-list { margin: 6px 0 0; font-size: 13px; line-height: 2; }
.source-list code { white-space: nowrap; }
.source-list .sep { color: var(--muted); }
.checkbox-label { flex-direction: row !important; align-items: center; gap: 8px !important; }
.stacked-form select { padding: 9px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 14px; }
.stacked-form textarea { font-size: 13px; }

/* ---- admin ---- */
.flash { padding: 10px 14px; border-radius: 6px; font-size: 14px; margin: 16px 0 0; }
.flash-ok { background: var(--ok-bg); color: var(--ok); border: 1px solid var(--ok-line); }
.flash-error { background: var(--error-bg); color: var(--error); border: 1px solid var(--error-line); }

.inline-form { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-top: 12px; }
.inline-form input { padding: 9px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 14px; min-width: 200px; }
.inline-form form { display: inline; }

.stacked-form { display: flex; flex-direction: column; gap: 14px; margin-top: 14px; max-width: 480px; }
.stacked-form label { font-size: 13px; color: var(--muted); display: flex; flex-direction: column; gap: 6px; }
.stacked-form input { padding: 9px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 14px; }
.stacked-form button { align-self: flex-start; }

.user-card .user-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }
.ip-table { margin-top: 12px; }
.ip-table td, .ip-table th { padding: 8px 10px; }
.ip-table form { margin: 0; }
button.small { padding: 4px 10px; font-size: 12px; }
code { background: var(--bg); padding: 1px 6px; border-radius: 4px; font-size: 13px; }

/* ---- confirmation modal ---- */
.modal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(26, 29, 35, 0.5);
  z-index: 1100;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.modal-overlay.active { display: flex; }
.modal-box {
  background: var(--surface);
  border-radius: 10px;
  padding: 24px 28px;
  max-width: 460px;
  width: 100%;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .22);
  border-top: 4px solid var(--error);
}
.modal-box h2 { margin: 0 0 10px; font-size: 17px; }
.modal-box p { font-size: 14px; margin: 0 0 12px; }
.modal-list { font-size: 14px; margin: 0 0 14px; padding-left: 20px; color: var(--muted); }
.modal-confirm { display: flex; gap: 8px; align-items: flex-start; font-size: 14px; margin: 16px 0; }
.modal-confirm input { margin-top: 3px; flex-shrink: 0; }
.modal-actions { display: flex; gap: 10px; justify-content: flex-end; }
.danger { background: var(--error); }
.danger[disabled] { opacity: .45; }
.status-inactive { background: var(--surface-2); color: var(--muted); }
.row-inactive td { opacity: .62; }
.unrestricted { background: var(--warning-bg); color: var(--warning); }
.row-actions { display: flex; gap: 8px; align-items: center; }
.row-actions form { margin: 0; }
.stat-row { display: flex; gap: 32px; flex-wrap: wrap; margin: 14px 0 4px; }
.stat { display: flex; flex-direction: column; gap: 2px; }
.stat-value { font-size: 26px; font-weight: 700; line-height: 1; }
.stat-label { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.locked-panel { border-left: 4px solid var(--error); }
.inline-form select { padding: 9px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 14px; }

/* ---------- two-factor authentication ---------- */

.flash-warn { background: var(--warning-bg); color: var(--warning); border: 1px solid var(--warning-line); }
.status-ok { background: var(--ok-bg); color: var(--ok); }

/* Badges on an admin user card. Two-factor and address restriction have to be
   read together to know whether an account is exposed, so they sit adjacent
   rather than in separate corners of the card. */
.user-badges { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.mfa-count { margin-left: 8px; white-space: nowrap; }

/* Code entry. Monospace and generously tracked so a mistyped digit is visible
   before submitting, which matters when a wrong code costs a 30-second wait
   and counts against the lockout. */
.code-input {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 24px;
  letter-spacing: .35em;
  text-align: center;
  max-width: 260px;
}

.mfa-enroll {
  display: flex;
  gap: 32px;
  flex-wrap: wrap;
  align-items: flex-start;
  margin-top: 8px;
}
/* Fixed width, not auto: with an auto width the caption box ends up wider
   than the code and text-align:center then centres it against the wrong
   box. */
.mfa-qr { flex: 0 0 240px; max-width: 240px; text-align: center; }
/* The QR is drawn at a fixed module size; cap it so it stays scannable on a
   phone-width screen without the page scrolling sideways. The white padding
   is on top of the 4-module quiet zone inside the SVG -- that one is required
   by the spec, this one just keeps a dark page from crowding it. */
.mfa-qr svg {
  width: 100%;
  max-width: 240px;
  height: auto;
  border-radius: 6px;
  background: var(--surface);
  padding: 6px;
  border: 1px solid var(--border);
}
.mfa-qr p { margin: 8px 0 0; }
.mfa-key { flex: 1 1 260px; min-width: 240px; }
.mfa-key h3 { margin: 0 0 6px; font-size: 15px; }

.mfa-fields { margin: 12px 0 8px; font-size: 14px; }
.mfa-fields dt { color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: .04em; }
.mfa-fields dd { margin: 2px 0 12px; }
/* Wraps rather than overflowing: the key is 32 characters plus spaces and has
   to stay fully selectable on a narrow screen. */
.mfa-secret {
  font-size: 15px;
  letter-spacing: .06em;
  word-break: break-all;
  display: inline-block;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 10px;
}

.login-aside { margin-top: 18px; }
.login-wrap .code-input { margin: 0 auto; display: block; }

/* Link that needs to look like a button -- the two-factor entry point on the
   password page is navigation, not a form submission, so it must not be a
   <button> inside a stray <form>. */
.button {
  display: inline-block;
  padding: 10px 16px;
  border: none;
  border-radius: 6px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}
.button:hover { opacity: .9; text-decoration: none; }
.button.secondary { background: var(--surface); color: var(--ink); border: 1px solid var(--border); }

/* The grace countdown. Sits above the page heading, so it reads before the
   content rather than after it. */
.mfa-banner { margin: 0 0 18px; }
.mfa-banner a { color: inherit; font-weight: 600; }

/* ------------------------------------------------------ home + tools ---
   The landing page (company name, weather, tool cards) and the superadmin
   tool matrix.
   ----------------------------------------------------------------------- */

a.back { color: var(--accent); text-decoration: none; font-weight: 500; }
a.back:hover { text-decoration: underline; }

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

.meta.block { display: block; }

.home-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
}
.home-title { margin-bottom: 4px; }
.home-section {
  margin: 26px 0 12px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

/* An author `display` beats the browser's own [hidden] rule, so a card
   given `display: flex` here ignores the hidden attribute entirely and sits
   on the page as an empty box until its fetch lands -- which is the opposite
   of what .weather-card's markup intends. One line, once, for every such
   element. */
[hidden] { display: none !important; }

.weather-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 18px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  min-width: 232px;
}
.weather-icon { font-size: 30px; line-height: 1; color: var(--accent); }
.weather-body { display: flex; flex-direction: column; gap: 1px; }
.weather-temp {
  font-size: 26px;
  font-weight: 600;
  line-height: 1.1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.weather-unit { font-size: 15px; font-weight: 500; color: var(--muted); margin-left: 1px; }
.weather-summary { font-size: 14px; color: var(--ink); }
.weather-meta { display: flex; gap: 10px; font-size: 12px; color: var(--muted); }
.weather-range { font-variant-numeric: tabular-nums; }

.tool-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(268px, 1fr));
  gap: 14px;
}
.tool-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 18px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  text-decoration: none;
}
.tool-card:hover { border-color: var(--accent); text-decoration: none; }
.tool-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tool-name { font-size: 16px; font-weight: 600; color: var(--ink); }
.tool-blurb { font-size: 13px; color: var(--muted); line-height: 1.5; }
.tool-pages { font-size: 12px; color: var(--muted); margin-top: 2px; }
.tool-pages .sep { color: var(--border); }

/* Any table wider than the 960px main column scrolls inside its own box
   rather than taking the page sideways. Applied by static/tables.js. */
.table-scroll { overflow-x: auto; }

/* .tool-matrix is gone: the company-by-tool grid it styled was replaced by a
   panel per company, because a matrix grows a column with every tool added
   and there is no room in that direction. See the Tools by company block at
   the end of this file. */
.tool-check { display: inline-block; margin: 0; cursor: pointer; }
.tool-check input { width: 17px; height: 17px; cursor: pointer; accent-color: var(--accent); }
.cell-note { font-size: 11px; line-height: 1.3; margin-top: 4px; }

/* ------------------------------------------------- mapping editor ------
   Two views of one mapping: drag a field onto a carrier column, or pick
   from a list. Both write into the JSON textarea below them, which is
   still the field that gets posted.
   ----------------------------------------------------------------------- */

.mapping-editor { color: var(--accent); }
.mapping-editor .prog { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 12px; }
.mapping-editor .prog-bar { flex: 1 1 180px; height: 7px; min-width: 140px; border-radius: 99px; background: var(--surface-3); overflow: hidden; }
.mapping-editor .prog-fill { height: 100%; background: var(--accent); width: 0; transition: width .25s; }
.mapping-editor .prog-text { font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; }
.mapping-editor .prog-text b { color: var(--ink); }
.mapping-editor .toolrow { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 12px; }

.mapping-editor .seg { display: inline-flex; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
.mapping-editor .seg button { font-family: inherit; font-size: 12.5px; padding: 6px 13px; border: 0; background: var(--surface); color: var(--muted); cursor: pointer; border-right: 1px solid var(--border); }
.mapping-editor .seg button:last-child { border-right: 0; }
.mapping-editor .seg button.on { background: var(--accent); color: var(--on-accent); }

.mapping-editor .rail { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 8px; margin-bottom: 12px; }
.mapping-editor .rail button { font-family: inherit; font-size: 12.5px; white-space: nowrap; padding: 6px 11px; border: 1px solid var(--border); border-radius: 99px; background: var(--surface); color: var(--ink); cursor: pointer; }
.mapping-editor .rail button.on { background: var(--ink); color: var(--on-ink); border-color: var(--ink); }
.mapping-editor .rail button .n { font-size: 11px; opacity: .75; margin-left: 5px; }
.mapping-editor .rail button .warnDot { color: var(--error); font-weight: 700; margin-left: 5px; }
.mapping-editor .rail button.on .warnDot { color: var(--error-line); }

.mapping-editor .surface { position: relative; display: grid; grid-template-columns: 1fr 92px 1.15fr; }
/* overflow HIDDEN, not visible. A wire's endpoint is the position of a
   column node, and a node can sit outside this box -- so with overflow
   visible the paths painted straight down the page over everything below
   the editor. Clipping is also the honest picture: a wire to something you
   cannot see should stop at the edge. */
.mapping-editor #wires { position: absolute; inset: 0; pointer-events: none; overflow: hidden; z-index: 3; }
/* Scrollable again, which it was not, and the comment that used to sit here
   explained why: two panes with their own scrollbars put the nodes and the
   wires in different coordinate spaces, and a node scrolled out of view still
   reports a layout position -- which is how wires ended up painted down the
   page.

   What makes it safe now is in drawWires: every endpoint is clamped into its
   own pane's visible box and a clamped wire is drawn faint, and each pane
   redraws the wires as it scrolls. The geometry was always computed from
   client rects, so it was only ever missing the redraw and the clipping.

   The panes need this back because "Show all fields" exists: ninety-odd
   sources and every column in the workbook is not a page somebody should have
   to scroll past the editor to read. */
.mapping-editor .pane { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 8px; position: relative; z-index: 1; min-width: 0; max-height: 560px; overflow-y: auto; overscroll-behavior: contain; }

/* Scrollbars on the OUTER edges -- the left pane's on its left, the right
   pane's on its right -- so neither sits in the channel the wires run
   through. Flipping direction is what moves a scrollbar; the children flip
   back so their own content still reads left to right. */
.mapping-editor .pane.ours { direction: rtl; }
.mapping-editor .pane.ours > * { direction: ltr; }

.mapping-editor .surface-tools { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 0 0 10px; }
.mapping-editor .surface-tools .showall { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--ink); cursor: pointer; }
.mapping-editor .surface-tools .showall input { margin: 0; }
.mapping-editor .surface-tools .meta { font-size: 12px; color: var(--muted); }
.mapping-editor .pane h5 { display: flex; justify-content: space-between; font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin: 4px 6px 8px; }
.mapping-editor .gutter { display: flex; align-items: center; justify-content: center; font-size: 11px; color: var(--muted); text-align: center; }

.mapping-editor .node { font-size: 13px; padding: 7px 10px; border: 1px solid var(--border); border-radius: 6px; margin-bottom: 5px; background: var(--surface); color: var(--ink); }
.mapping-editor .node.src { cursor: grab; touch-action: none; }
.mapping-editor .node.src:active { cursor: grabbing; }
.mapping-editor .node.src.used { border-color: var(--accent); background: var(--accent-quiet); }
.mapping-editor .node.src.armed { outline: 2px solid var(--accent); outline-offset: 1px; }
.mapping-editor .node.tgt { cursor: pointer; }
.mapping-editor .node.tgt.linked { border-color: var(--accent); background: var(--accent-quiet); }
.mapping-editor .node.tgt.need { border-color: var(--error-line); border-style: dashed; background: var(--surface-2); }
.mapping-editor .node.tgt.skip { background: var(--surface-2); color: var(--muted); }
.mapping-editor .node.tgt.carrier { border-color: var(--info-line); background: var(--info-bg); }
.mapping-editor .node.tgt.sel { box-shadow: 0 0 0 2px var(--accent); }
.mapping-editor .node.drop { border-color: var(--accent); background: var(--warning-bg); }
.mapping-editor .node:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.mapping-editor .nhead { display: flex; align-items: baseline; gap: 7px; }
.mapping-editor .pos { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11px; color: var(--muted); flex-shrink: 0; }
.mapping-editor .nname { font-weight: 500; }
.mapping-editor .req { font-size: 9.5px; font-weight: 700; color: var(--error); }
.mapping-editor .nsub { font-size: 11px; color: var(--muted); }
.mapping-editor .nsample { font-size: 11px; color: var(--muted); font-style: italic; }
.mapping-editor .ndir { font-size: 11px; color: var(--info); }

.mapping-editor .asg { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.mapping-editor .asg-chip { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; background: var(--accent-quiet); border: 1px solid var(--border); color: var(--warning); border-radius: 4px; padding: 1px 2px 1px 6px; max-width: 100%; }
.mapping-editor .asg-chip .apos { font-family: ui-monospace, Menlo, monospace; opacity: .8; }
.mapping-editor .asg-chip .aname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mapping-editor .asg-x, .mapping-editor .unlink { border: 0; background: transparent; cursor: pointer; font-family: inherit; font-size: 14px; line-height: 1; border-radius: 3px; }
.mapping-editor .asg-x { color: var(--warning); padding: 0 4px; }
.mapping-editor .asg-x:hover { background: var(--warning-line); color: var(--warning); }
.mapping-editor .unlink { margin-left: auto; color: var(--muted); padding: 0 3px; }
.mapping-editor .unlink:hover { background: var(--surface-3); color: var(--error); }
.mapping-editor .asg-more { font-size: 11px; color: var(--muted); }

.mapping-editor .detail { margin-top: 12px; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 14px 16px; }
.mapping-editor .detail-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 4px; }
.mapping-editor .detail-head .dt { font-size: 15px; font-weight: 600; color: var(--ink); }
.mapping-editor .detail-rule { font-size: 12px; color: var(--ink); background: var(--surface-2); border-left: 3px solid var(--accent); padding: 8px 11px; border-radius: 0 4px 4px 0; margin: 8px 0 12px; }
.mapping-editor .kinds { display: flex; gap: 6px; flex-wrap: wrap; }
.mapping-editor .detail-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px; margin-top: 12px; }
.mapping-editor .detail label { display: block; font-size: 12px; font-weight: 600; margin-bottom: 4px; color: var(--ink); }
.mapping-editor .detail input[type="text"] { font-family: inherit; font-size: 13px; width: 100%; padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px; }
.mapping-editor .hint { font-size: 11px; color: var(--muted); margin-top: 4px; }
.mapping-editor .fmt-radios { font-size: 13px; display: flex; gap: 12px; }
.mapping-editor .fmt-radios label { display: inline-flex; align-items: center; gap: 4px; font-weight: 400; margin: 0; }
.mapping-editor .xlat { width: 100%; border-collapse: collapse; font-size: 12px; }
.mapping-editor .xlat td { padding: 3px 0; }
.mapping-editor .xlat .from { font-weight: 500; }
.mapping-editor .xlat .sep { color: var(--muted); padding: 0 8px; }
.mapping-editor .xlat select { font-size: 12px; padding: 2px 4px; max-width: 160px; border: 1px solid var(--border); border-radius: 4px; font-family: inherit; }

.mapping-editor .maplist { border: 1px solid var(--border); border-radius: 8px; background: var(--surface); }
.mapping-editor .maprow { display: grid; grid-template-columns: 1.2fr 1.1fr auto auto; gap: 12px; align-items: center; padding: 8px 12px; border-bottom: 1px solid var(--border); }
.mapping-editor .maprow:last-child { border-bottom: 0; }
.mapping-editor .maprow.needs { background: var(--surface-2); }
.mapping-editor .maprow select { font-family: inherit; font-size: 13px; width: 100%; padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--ink); }
.mapping-editor .maprow select.unset { color: var(--error); border-color: var(--error-line); }
.mapping-editor .cname { font-size: 14px; font-weight: 500; color: var(--ink); }
/* The connect pane gets this from .nhead's flex gap; the list row does not,
   so the badge was running straight into the column name. */
.mapping-editor .cname .req { margin-left: 6px; }
.mapping-editor .cdir { font-size: 11.5px; color: var(--info); }
.mapping-editor .cmeta { font-size: 11px; color: var(--muted); font-family: ui-monospace, Menlo, monospace; }

.mapping-editor .chip { font-size: 11px; font-weight: 600; border-radius: 99px; padding: 2px 9px; white-space: nowrap; }
.mapping-editor .chip.ok { background: var(--ok-bg); color: var(--ok); }
.mapping-editor .chip.guess { background: var(--warning-bg); color: var(--warning); }
.mapping-editor .chip.blank { background: var(--surface-3); color: var(--muted); }
.mapping-editor .chip.carrier { background: var(--info-bg); color: var(--info); }
.mapping-editor .chip.need { background: var(--error-line); color: var(--error); }

.mapping-editor .legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 11.5px; color: var(--muted); margin-top: 10px; }
.mapping-editor .legend b { font-weight: 600; color: var(--ink); }
.mapping-editor .sw { display: inline-block; width: 10px; height: 10px; border-radius: 3px; border: 1px solid var(--border); margin-right: 4px; vertical-align: -1px; }

.advanced-json { margin: 18px 0 4px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); padding: 0 14px; }
.advanced-json > summary { cursor: pointer; padding: 10px 0; font-size: 13px; color: var(--muted); }
.advanced-json > summary:hover { color: var(--ink); }
.advanced-json[open] > summary { border-bottom: 1px solid var(--border); margin-bottom: 12px; }
.advanced-json label { display: block; margin-bottom: 12px; }

@media (max-width: 820px) {
  .mapping-editor .surface { grid-template-columns: 1fr; }
  .mapping-editor #wires, .mapping-editor .gutter { display: none; }
  .mapping-editor .maprow { grid-template-columns: 1fr; gap: 6px; }
}

/* The app's global `button` rule is a dark filled button. The editor uses
   small secondary buttons throughout, so give .btn its own look rather than
   inheriting a page-action style for "Options". */
.mapping-editor .btn {
  font-family: inherit;
  font-size: 13px;
  padding: 7px 13px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}
.mapping-editor .btn:hover { border-color: var(--accent); }
.mapping-editor .btn.small { font-size: 12px; padding: 4px 9px; }
.mapping-editor .btn.primary { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.mapping-editor .btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Where a value comes from in the 834. Monospaced and muted: it is
   reference material for whoever reads the raw file, and must not compete
   with the plain-English label an HR administrator reads. */
.mapping-editor .nx12,
.mapping-editor .cx12 {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 10.5px;
  color: var(--muted);
  margin-top: 2px;
  overflow-wrap: anywhere;
}
.mapping-editor .cx12 { margin-top: 4px; }
/* No element to point at. Set in the body face and lighter, so the eye
   skips it and lands on the references that do name a place in the file. */
.mapping-editor .nx12.derived {
  font-family: inherit;
  font-style: italic;
  color: var(--muted);
}
.mapping-editor .detail-x12 {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 12px;
  margin: -4px 0 12px;
}
.mapping-editor .detail-x12 .px-label { color: var(--muted); flex-shrink: 0; }
.mapping-editor .detail-x12 .px-val {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  color: var(--ink);
}

/* -----------------------------------------------------------------------
   XSLT generator: the parts the carrier mapping editor does not already
   have. Everything else -- panes, nodes, wires, the list, the progress bar
   -- is shared with it deliberately, since two near-identical stylesheets
   are how two screens drift apart.
   ----------------------------------------------------------------------- */

/* The output settings. Above the panes because they decide what the whole
   file looks like, and every one of them changes what the preview shows. */
.mapping-editor .outset { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px; margin-bottom: 14px; }
.mapping-editor .outset h5 { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin: 0 0 10px; }
.mapping-editor .outset-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 12px 16px; }
.mapping-editor .outset-item { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.mapping-editor .outset-label { font-size: 12px; font-weight: 600; color: var(--ink); }
.mapping-editor .outset-hint { font-size: 11px; color: var(--muted); }
.mapping-editor .outset select,
.mapping-editor .cfmt select,
.mapping-editor .detail select { font-family: inherit; font-size: 13px; padding: 5px 7px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--ink); max-width: 100%; }
.mapping-editor .delim-row { display: flex; gap: 6px; align-items: center; }
.mapping-editor .fmt-box { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12.5px; padding: 5px 7px; border: 1px solid var(--border); border-radius: 6px; min-width: 0; width: 100%; }
.mapping-editor .fmt-box.narrow { width: 4.5em; flex: 0 0 auto; text-align: center; }
.mapping-editor .stage { min-width: 0; }

/* Per-node annotations. The XPath uses the same .nx12 treatment as the 834
   mapping's loop reference -- same job, same look. */
.mapping-editor .nwarn { font-size: 11px; color: var(--warning); }
.mapping-editor .ntype { font-size: 10px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.mapping-editor .nfmt { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11px; color: var(--info); }
.mapping-editor .nguess { font-size: 11px; color: var(--muted); font-style: italic; }

/* What the column actually produced. Monospaced and set apart, because this
   is the value being checked rather than a label describing one. */
.mapping-editor .nout,
.mapping-editor .cout { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11.5px; color: var(--ok); background: var(--ok-bg); border-radius: 3px; padding: 1px 5px; margin-top: 5px; display: inline-block; max-width: 100%; overflow-wrap: anywhere; }
.mapping-editor .cfmt { display: flex; flex-direction: column; gap: 5px; min-width: 0; }

/* The preview. Below everything, and the width of the editor, because the
   line it shows is the deliverable. */
.mapping-editor .prevbox { margin-top: 14px; background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px; }
.mapping-editor .prev-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.mapping-editor .prev-head strong { font-size: 13px; color: var(--ink); }
.mapping-editor .prev-head .meta { font-size: 11.5px; }
.mapping-editor .prev-head button { margin-left: auto; }
.mapping-editor .prev-ask { margin: 4px 0 8px; }
.mapping-editor .prev-ask p { margin: 0 0 6px; }
/* Its own horizontal scroll: a wide row must not make the page scroll
   sideways, and wrapping a delimited line would misrepresent it. */
.mapping-editor .prev-lines { overflow-x: auto; background: var(--surface-2); border: 1px solid var(--border); border-radius: 6px; padding: 8px 10px; }
.mapping-editor .prev-line { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; white-space: pre; color: var(--ink); line-height: 1.55; }
.mapping-editor .prev-header { color: var(--muted); border-bottom: 1px solid var(--border); padding-bottom: 3px; margin-bottom: 3px; }

.mapping-editor .detail h5 { font-size: 13px; font-weight: 600; color: var(--ink); margin: 0 0 8px; }
.mapping-editor .detail-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
.mapping-editor .detail .outset-item { gap: 4px; }

/* Reordering the output columns. A grip rather than the whole node: the
   layout pane is already a drop target for a field dragged in from the left,
   and a node that both accepts a drop and starts a drag makes every gesture
   a guess. */
.mapping-editor .grip {
  flex-shrink: 0;
  cursor: grab;
  touch-action: none;
  color: var(--field-border);
  font-size: 11px;
  letter-spacing: -2px;
  line-height: 1;
  padding: 2px 3px 2px 0;
  user-select: none;
}
.mapping-editor .grip:hover { color: var(--accent); }
.mapping-editor .grip:active { cursor: grabbing; }
.mapping-editor .cname-row { display: flex; align-items: baseline; gap: 4px; }

/* The row being moved, and where it would land. An inset shadow rather than
   a border, so the insertion line does not change the node's height and
   shuffle everything below it mid-drag. */
.mapping-editor .node.tgt.dragging,
.mapping-editor .maprow.dragging { opacity: .55; }
.mapping-editor .node.tgt.drop-above,
.mapping-editor .maprow.drop-above { box-shadow: inset 0 3px 0 0 var(--accent); }
.mapping-editor .node.tgt.drop-below,
.mapping-editor .maprow.drop-below { box-shadow: inset 0 -3px 0 0 var(--accent); }
.mapping-editor .maprow:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.mapping-editor .btn:disabled { opacity: .45; cursor: default; }

/* -----------------------------------------------------------------------
   XSLT generator: row sets, filters and conditions

   A file is no longer one loop. An employee row can be followed by a row
   per dependent, and a trailer can be followed by nothing at all, so the
   editor needs to say which set of rows is being mapped -- and every pane
   below the strip belongs to that one set. The strip is the only genuinely
   new piece of furniture; everything else reuses the panes, the nodes and
   the preview that were already there.
   ----------------------------------------------------------------------- */

.mapping-editor .rowsets { display: flex; gap: 8px; align-items: stretch; flex-wrap: wrap; margin-bottom: 10px; }
.mapping-editor .rowset { display: flex; flex-direction: column; gap: 2px; min-width: 190px; max-width: 300px; padding: 9px 12px; border: 1.5px solid var(--border); border-radius: 8px; background: var(--surface); cursor: pointer; text-align: left; }
/* The selected set carries the accent on its leading edge rather than a
   heavier border, so switching sets does not move anything. */
.mapping-editor .rowset.on { border-color: var(--accent); background: var(--accent-quiet); box-shadow: inset 3px 0 0 0 var(--accent); }
.mapping-editor .rowset:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.mapping-editor .rowset .rs-name { font-size: 13.5px; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
.mapping-editor .rowset .rs-path { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 10.5px; color: var(--muted); overflow-wrap: anywhere; }
.mapping-editor .rowset .rs-meta { font-size: 11px; color: var(--muted); }
.mapping-editor .rowset .rs-badge,
.mapping-editor .rowset .rs-filter { font-size: 9.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--warning); background: var(--accent-quiet); border: 1px solid var(--border); border-radius: 3px; padding: 0 4px; align-self: flex-start; margin-top: 3px; }
.mapping-editor .rowset .rs-filter { color: var(--info); background: var(--info-bg); border-color: var(--info-line); }
.mapping-editor .rowset.add { align-items: center; justify-content: center; color: var(--muted); border-style: dashed; min-width: 150px; font-size: 13px; }
.mapping-editor .rowset.add:hover { border-color: var(--accent); color: var(--accent); }
/* The arrow between a row set and the set nested inside it. */
.mapping-editor .nest { display: flex; align-items: center; color: var(--border); font-size: 18px; padding: 0 2px; }

/* Adding a loop or a single line. Same panel treatment as the output
   settings, because it is the same kind of decision. */
.mapping-editor .addset { background: var(--accent-quiet); border: 1.5px solid var(--accent); border-radius: 8px; padding: 12px 14px; margin-bottom: 12px; }
.mapping-editor .addset h5 { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin: 0 0 10px; }
.mapping-editor .addset .meta { font-size: 12px; margin: 0; }

/* The bar under the strip: what this row set is, and when it writes. */
.mapping-editor .setbar { background: var(--surface-2); border: 1.5px solid var(--border); border-radius: 8px; padding: 10px 12px; margin-bottom: 14px; display: flex; flex-direction: column; gap: 8px; }
.mapping-editor .setbar-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.mapping-editor .setbar-head .meta { font-size: 12px; }
.mapping-editor .setbar-head .btn { margin-left: auto; }
.mapping-editor .setbar-filter { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.mapping-editor .setbar-filter .outset-label { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }

/* Own fields above, the row above's fields below -- named, so a Legal Name
   on both sides is never a guess about which one a column got. */
.mapping-editor .pane .group-head { font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); font-weight: 700; margin: 12px 6px 6px; display: flex; justify-content: space-between; gap: 8px; }
.mapping-editor .pane .group-head:first-of-type { margin-top: 2px; }
.mapping-editor .node.parentfield { background: var(--info-bg); border-color: var(--info-line); }
.mapping-editor .node.parentfield.used { border-color: var(--info); }
.mapping-editor .node .pbadge { font-size: 9.5px; font-weight: 700; color: var(--info); background: var(--info-bg); border-radius: 3px; padding: 0 4px; white-space: nowrap; }

/* Conditions. Written as a sentence on one line -- when / field / operator
   / value -- because that is how somebody says the rule out loud, and the
   xsl:choose it becomes is the generator's problem, not the reader's. */
.mapping-editor .cond { display: flex; flex-direction: column; gap: 5px; width: 100%; }
.mapping-editor .cond-line { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.mapping-editor .cond-head { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.mapping-editor .cond-lead { font-size: 12px; color: var(--muted); }
.mapping-editor .cond-line .fmt-box { width: auto; flex: 0 1 12em; min-width: 6em; }
/* The joiner is a label, not a control -- the group's own all/any decides
   it. Fixed width so the tests below it line up as a column. */
.mapping-editor .cond-joiner { font-size: 11.5px; font-weight: 600; color: var(--accent); min-width: 2.4em; text-align: right; flex: 0 0 auto; }
.mapping-editor .cond-add { margin-top: 2px; }
.mapping-editor .cond-add .outset-hint { flex: 1 1 16em; }

/* A group inside a group: the brackets, drawn. The rule is bracketed on
   three sides and indented, because "(red or blue) and in stock" is a
   different rule from "red or (blue and in stock)" and the reader has to be
   able to see which one is on the screen. */
.mapping-editor .cond-slot { display: flex; align-items: flex-start; gap: 6px; }
.mapping-editor .cond-slot > .cond-joiner { padding-top: 12px; }
/* 1.5px and a warmer grey, not 1px of near-white: these blocks sit on a
   white panel, and at 1px against var(--border) the edge of a group was invisible
   -- which is the whole thing a bracket has to communicate. The leading edge
   takes the accent so the nesting reads at a glance. */
.mapping-editor .cond.cond-group { border: 1.5px solid var(--border); border-left-width: 4px; border-left-color: var(--accent); border-radius: 8px; background: var(--surface-2); padding: 8px 10px; gap: 4px; flex: 1 1 auto; min-width: 0; }
/* Alternating fills, so one level down is distinguishable from two. */
.mapping-editor .cond.cond-group .cond.cond-group { background: var(--surface); }
.mapping-editor .cond.cond-group > .cond-head .asg-x { margin-left: auto; }
/* The add row lines up with the tests above it rather than with the bracket. */
.mapping-editor .cond-add { padding-left: 3em; }
.mapping-editor .cond.cond-group > .cond-add { padding-left: 2.9em; }

/* The expression the server compiled the condition to. Read-only on
   purpose: it is here so somebody who knows XPath can confirm the brackets
   landed where they meant them to, or paste it into Workday -- not as a
   second way in, which is the injection surface the validator closes. */
.mapping-editor .cond-xpath { display: flex; align-items: baseline; gap: 8px; margin-top: 4px; padding-top: 6px; border-top: 1px dashed var(--border); overflow-x: auto; }
.mapping-editor .cond-xpath-label { flex: 0 0 auto; font-size: 9.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
.mapping-editor .cond-xpath code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11.5px; color: var(--info); white-space: pre; }
.mapping-editor .setbar select,
.mapping-editor .addset select,
.mapping-editor .cond select,
.mapping-editor .rules select { font-family: inherit; font-size: 13px; padding: 5px 7px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--ink); max-width: 100%; }
.mapping-editor .fmt-box.wide { width: auto; flex: 0 1 18em; min-width: 10em; }
/* In a column-flex cell the shorthand above stretches the box down the whole
   cell, which is why "Name it" came out as a text area the size of a
   paragraph. Only the ROW contexts want it to flex. */
.mapping-editor .outset-item > .fmt-box,
.mapping-editor .outset-item > .fmt-box.wide { flex: 0 0 auto; align-self: stretch; }

/* The rules on a column. Numbered, because they are checked in order and
   the number is the only thing that says so. */
.mapping-editor .rules { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 10px; }
.mapping-editor .rules-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.mapping-editor .rules-head strong { font-size: 12.5px; color: var(--ink); }
.mapping-editor .rules-head .meta { font-size: 11.5px; }
.mapping-editor .rule { border: 1.5px solid var(--border); border-radius: 6px; padding: 8px 10px; margin-bottom: 8px; background: var(--surface-2); }
.mapping-editor .rule-top { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
.mapping-editor .rule-no { font-size: 10.5px; font-weight: 700; color: var(--on-accent); background: var(--accent); border-radius: 999px; min-width: 17px; height: 17px; display: inline-flex; align-items: center; justify-content: center; }
.mapping-editor .rule-top .asg-x { margin-left: auto; }
/* The otherwise. Dashed and unfilled so it reads as the alternative to the
   numbered rules above rather than as another one of them, and its marker is
   a dash rather than the next number. */
.mapping-editor .rule.otherwise { background: var(--surface); border-style: dashed; }
.mapping-editor .rule.otherwise .rule-no { background: var(--muted); }
.mapping-editor .rule.otherwise .outset-hint { margin-top: 4px; }
.mapping-editor .nrule { font-size: 11px; font-weight: 600; color: var(--warning); }

/* Which row set a preview line came from. With nested loops the preview is
   the only place the interleaving is visible, so every line is labelled and
   the lines from other sets are dimmed rather than hidden -- the order is
   the thing being checked. */
.mapping-editor .prev-line { display: flex; align-items: baseline; gap: 10px; }
.mapping-editor .prev-key { flex: 0 0 9em; text-align: right; font-family: system-ui, -apple-system, "Segoe UI", sans-serif; font-size: 10px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--warning); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; position: sticky; left: 0; background: var(--surface-2); }
.mapping-editor .prev-text { flex: 0 0 auto; white-space: pre; }
.mapping-editor .prev-other { opacity: .6; }
.mapping-editor .prev-other .prev-key { color: var(--muted); }

/* -----------------------------------------------------------------------
   XSLT tester

   Two source boxes, a result, and -- the reason the tool exists -- an error
   panel that points at the line. Scoped under .mapping-editor so it reuses
   the .btn, .meta and panel treatment the other tools already have.
   ----------------------------------------------------------------------- */

/* The two boxes sit either side of a draggable divider. --split is the first
   column's share of the row; it is only ever set by the splitter, so the
   default 1fr keeps the even layout for anyone who never touches it. */
.mapping-editor .tester-grid { display: grid; grid-template-columns: minmax(160px, var(--split, 1fr)) 16px minmax(160px, 1fr); align-items: start; }
.mapping-editor .tester-grid.stacked { grid-template-columns: minmax(0, 1fr); gap: 16px; }
.mapping-editor .tester-grid.stacked .tester-split { display: none; }
@media (max-width: 1000px) {
  .mapping-editor .tester-grid { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .mapping-editor .tester-split { display: none; }
}

/* Overflow is hidden rather than visible because a textarea being dragged
   wider is briefly wider than its card: the card clips it for those few
   frames, and the script turns the drag into a divider move. Without this
   the whole page would shuffle sideways mid-drag. */
.mapping-editor .tester-box { background: var(--surface); border: 1.5px solid var(--border); border-radius: 8px; padding: 10px 12px 12px; min-width: 0; overflow: hidden; }

.mapping-editor .tester-split { align-self: stretch; justify-self: stretch; position: relative; cursor: col-resize; border-radius: 4px; min-height: 60px; }
.mapping-editor .tester-split::before { content: ""; position: absolute; top: 10px; bottom: 10px; left: 50%; width: 3px; margin-left: -1.5px; background: var(--field-border); border-radius: 2px; }
.mapping-editor .tester-split:hover::before,
.mapping-editor .tester-split:focus-visible::before { background: var(--accent); width: 4px; margin-left: -2px; }
.mapping-editor .tester-split:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
body.tester-dragging { cursor: col-resize; user-select: none; }

/* Layout controls. "Full width" lets the tool out of the page's reading
   column -- prose wants 960px, a 400-line stylesheet does not. */
.mapping-editor .tester-layout { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.mapping-editor .tester-layout .meta { margin-right: 2px; }
.mapping-editor .tester-layout .btn[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
/* Only the boxes want the extra width. Prose stays at its reading measure,
   and the panel keeps its full-width frame with the text inset. */
body.tester-wide main { max-width: none; }
body.tester-wide main > h1,
body.tester-wide main > p,
body.tester-wide main > noscript,
body.tester-wide main > .panel > * { max-width: 960px; }
.mapping-editor .tester-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.mapping-editor .tester-head h5 { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin: 0; }
.mapping-editor .tester-tools { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.mapping-editor .tester-stat { font-size: 11.5px; color: var(--error); }
/* Monospaced and tall: this is where somebody pastes a 400-line stylesheet,
   and a four-row box makes that miserable. resize is both, not vertical: the
   grip in the corner is the first thing anyone reaches for when a line runs
   off the edge, and dragging it sideways moves the divider. */
.mapping-editor .tester-box textarea { width: 100%; min-height: 340px; resize: both; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12.5px; line-height: 1.55; padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface-2); color: var(--ink); white-space: pre; overflow-wrap: normal; overflow-x: auto; }
.mapping-editor .tester-box textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.mapping-editor .tester-view { margin-top: 10px; border: 1.5px dashed var(--border); border-radius: 6px; padding: 8px 10px; background: var(--surface-2); }

.mapping-editor .tester-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 16px 0; }

.mapping-editor .tester-panel { background: var(--surface); border: 1.5px solid var(--border); border-radius: 8px; padding: 12px 14px; margin-bottom: 14px; }
.mapping-editor .tester-panel.ok { border-color: var(--ok-line); background: var(--ok-bg); }
.mapping-editor .tester-panel.bad { border-color: var(--error-line); background: var(--error-bg); }
.mapping-editor .tester-panel-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.mapping-editor .tester-panel-head strong { font-size: 13px; color: var(--ink); }
.mapping-editor .tester-panel-head .meta { font-size: 11.5px; }
.mapping-editor .tester-panel-head .btn { margin-left: auto; }
.mapping-editor .tester-code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 10.5px; font-weight: 700; color: var(--warning); background: var(--accent-quiet); border: 1px solid var(--border); border-radius: 3px; padding: 1px 5px; }
.mapping-editor .tester-msg { font-size: 13px; color: var(--ink); margin: 0 0 10px; overflow-wrap: anywhere; }

/* The failing line, in context, with a caret under the column. Its own
   horizontal scroll: a long line must not make the page scroll sideways. */
.mapping-editor .tester-excerpt,
.mapping-editor .tester-out { background: var(--surface-2); border: 1px solid var(--border); border-radius: 6px; padding: 8px 0; overflow-x: auto; }
.mapping-editor .tx-line { display: flex; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; line-height: 1.6; }
.mapping-editor .tx-line.fault { background: var(--error-bg); }
.mapping-editor .tx-caret { color: var(--error); font-weight: 700; }
.mapping-editor .tx-no { flex: 0 0 4.5em; text-align: right; padding-right: 12px; color: var(--muted); user-select: none; }
.mapping-editor .tx-text { white-space: pre; padding-right: 14px; }
.mapping-editor .tester-out { max-height: 460px; overflow-y: auto; }

.mapping-editor .tester-detail { margin: 10px 0 4px; }
.mapping-editor .tester-detail summary { font-size: 12px; color: var(--muted); cursor: pointer; }
.mapping-editor .tester-detail pre { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11.5px; white-space: pre-wrap; overflow-wrap: anywhere; background: var(--surface-2); border: 1px solid var(--border); border-radius: 6px; padding: 8px 10px; margin: 6px 0 0; }

.mapping-editor .tester-messages { display: flex; flex-direction: column; gap: 3px; }
.mapping-editor .tester-message { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; color: var(--info); background: var(--info-bg); border-left: 3px solid var(--info-line); padding: 3px 8px; overflow-wrap: anywhere; }

.mapping-editor .tester-pre { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; line-height: 1.55; white-space: pre; overflow-x: auto; margin: 0; max-height: 420px; overflow-y: auto; }

/* -----------------------------------------------------------------------
   A verse from the wisdom books

   Sign-in page and the superadmin's home. Quiet by design: it sits below
   the thing people came for, and it is the last thing in the reading order
   rather than a banner across the top. Hidden until the fetch lands -- see
   app/verse.py for why there is no fallback text.
   ----------------------------------------------------------------------- */
.verse {
  margin: 22px 0 0;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}
.verse blockquote {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.62;
  font-style: italic;
  color: var(--muted);
  text-wrap: pretty;
}
.verse figcaption {
  margin-top: 7px;
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
  opacity: .75;
}
/* On the home page it is one line in a wide column, so give it a measure
   rather than letting it run the full width of a desktop window. */
.verse-home {
  max-width: 46em;
  margin-top: 18px;
  padding-top: 14px;
}

/* -----------------------------------------------------------------------
   OE Changes Report

   Two sources for the same comparison. The source chooser sits above both
   panes because which one you are using changes what the rest of the page
   means -- it is a mode, not an option buried in a form.
   ----------------------------------------------------------------------- */
.oe2-source { display: flex; gap: 12px; flex-wrap: wrap; margin: 18px 0 6px; }
.oe2-radio {
  flex: 1 1 18rem; display: flex; gap: 10px; align-items: flex-start;
  background: var(--surface); border: 1.5px solid var(--border); border-radius: 8px;
  padding: 12px 14px; cursor: pointer; font-size: 14px;
}
.oe2-radio:has(input:checked) { border-color: var(--accent); background: var(--surface-2); }
.oe2-radio input { margin-top: 3px; }

.oe2-row { display: flex; gap: 14px; flex-wrap: wrap; align-items: flex-end; margin-bottom: 12px; }
.oe2-field { display: flex; flex-direction: column; gap: 6px; font-size: 13px; color: var(--muted); }
.oe2-field.grow { flex: 1 1 16rem; min-width: 0; }
.oe2-field select,
.oe2-field input {
  padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px;
  font-size: 14px; color: var(--ink); background: var(--surface); max-width: 100%;
}
.oe2-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 8px; }

#events table { width: 100%; }
#events td, #events th { vertical-align: middle; }

.probe-list { list-style: none; margin: 0; padding: 0; font-size: 13px; }
.probe-list li { padding: 2px 0; }
.probe-ok { color: var(--ok, var(--ok)); }
.probe-bad { color: var(--error); }

/* ---- OE v2: the SOAP envelopes, for troubleshooting a connection ----
   Monospace and scrollable in BOTH directions. A SOAP envelope has long
   lines and a lot of them, and wrapping them would destroy the one thing
   somebody is reading this for -- where an element sits. So it scrolls
   sideways rather than reflowing, and the height is capped so a 200KB
   response cannot push the rest of the page off the screen. */
.oe2-trace-toggle {
  display: flex; align-items: flex-start; gap: 8px;
  font-size: 13px; color: var(--muted);
  margin: 4px 0 12px; max-width: 52rem;
}
.oe2-trace-toggle input { margin-top: 3px; flex: 0 0 auto; }

.soap-trace { margin: 0 0 18px; display: flex; flex-direction: column; gap: 8px; }
.soap-trace details {
  border: 1px solid var(--border); border-radius: 6px;
  padding: 8px 12px; background: var(--bg);
}
.soap-trace summary {
  cursor: pointer; font-size: 13px; font-weight: 600; color: var(--ink);
}
.soap-trace h4 {
  margin: 10px 0 4px; font-size: 12px; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--muted);
}
.soap-trace .meta { margin: 6px 0 0; word-break: break-all; }
.xml-dump {
  margin: 0; padding: 10px 12px; max-height: 22rem;
  overflow: auto; white-space: pre;
  background: var(--surface); border: 1px solid var(--border); border-radius: 4px;
  font-size: 12px; line-height: 1.45; tab-size: 2;
}

/* ---- XSLT generator: where the sample comes from ----
   A radio per source with its own pane underneath. The panes are hidden
   rather than removed, so switching to look at the other one and back does
   not lose what was typed. */
.sample-source { border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px 12px; margin: 0; }
.sample-source legend { font-size: 13px; color: var(--muted); padding: 0 4px; }
.sample-source .radio-line { display: flex; align-items: flex-start; gap: 8px; font-size: 14px; color: var(--ink); margin: 6px 0 4px; cursor: pointer; }
.sample-source .radio-line input { margin-top: 3px; flex: 0 0 auto; }
.sample-source .radio-line input:disabled + span { color: var(--muted); cursor: default; }
.sample-source .src-pane { margin: 0 0 10px 24px; display: flex; flex-direction: column; gap: 10px; }
.sample-source .src-pane label { display: flex; flex-direction: column; gap: 6px; font-size: 13px; color: var(--muted); }
.sample-source .src-pane input[type="text"],
.sample-source .src-pane select { padding: 9px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 14px; }

/* ---- Field edges ----------------------------------------------------
   Appended rather than edited into the dozen rules that each set their own
   border, so the padding, radius and sizing decisions those rules made stay
   exactly as they were and only the edge changes. Later in the file and at
   matching specificity, so it wins.

   1.5px, not 2: at 2 the controls start to look like buttons, and the page
   already has real buttons a few pixels away. What was wrong was the colour
   far more than the weight -- var(--border) against white is almost nothing. */
.oe2-field select, .oe2-field input,
.stacked-form input:not([type="checkbox"]):not([type="radio"]),
.stacked-form select,
.stacked-form textarea,
.inline-form input:not([type="checkbox"]):not([type="radio"]),
.inline-form select,
.login-wrap input,
.snapshot-label input,
.sample-source .src-pane input,
.sample-source .src-pane select,
.code-area,
.mapping-editor .maprow select,
.mapping-editor .detail input[type="text"],
.mapping-editor .detail select {
  border: 1.5px solid var(--field-border);
}

/* Hover says "this is the one under the pointer" before a click commits to
   anything -- which matters most on the date inputs, where the clickable
   calendar icon sits inside the box. */
.oe2-field select:hover, .oe2-field input:hover,
.stacked-form input:not([type="checkbox"]):not([type="radio"]):hover,
.stacked-form select:hover,
.inline-form input:not([type="checkbox"]):not([type="radio"]):hover,
.inline-form select:hover,
.sample-source .src-pane input:hover,
.sample-source .src-pane select:hover {
  border-color: var(--field-border-hover);
}

/* A heavier resting edge makes a weak focus ring look like nothing at all, so
   focus gets the accent and a soft halo. outline-offset 0 keeps the halo on
   the border rather than floating outside it. */
.oe2-field select:focus, .oe2-field input:focus,
.stacked-form input:focus, .stacked-form select:focus,
.stacked-form textarea:focus,
.inline-form input:focus, .inline-form select:focus,
.login-wrap input:focus,
.snapshot-label input:focus,
.sample-source .src-pane input:focus,
.sample-source .src-pane select:focus,
.code-area:focus,
.mapping-editor .maprow select:focus,
.mapping-editor .detail input[type="text"]:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(180, 83, 9, 0.14);
}

/* Section edges, lifted just enough to separate one panel from the next
   without competing with the fields inside them. */
.panel { border-color: var(--panel-border); }

/* The grouped rows in OE v2 read as one step of the sequence -- tenant,
   integration and dates, the two runs -- so the fieldset that already groups
   the sample source gets the same treatment. */
.sample-source { border-color: var(--panel-border); }

/* ---- File Compare ----------------------------------------------------
   Two panes either side of a draggable divider, the same control the XSLT
   tester uses (static/split_layout.js). The layout buttons sit above the
   boxes because they change the shape of the page; the comparison options sit
   below them, next to the button they affect. */

.tc-toolbar { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; margin: 18px 0 10px; }
.tc-tools { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.tc-tools-end { margin-left: auto; }

.tc-grid { display: grid; grid-template-columns: minmax(180px, var(--split, 1fr)) 16px minmax(180px, 1fr); align-items: stretch; }
.tc-grid.stacked { grid-template-columns: minmax(0, 1fr); gap: 14px; }
.tc-grid.stacked .tc-split { display: none; }
@media (max-width: 1000px) {
  .tc-grid { grid-template-columns: minmax(0, 1fr); gap: 14px; }
  .tc-split { display: none; }
}

.tc-pane {
  background: var(--surface); border: 1px solid var(--panel-border); border-radius: 10px;
  padding: 10px 12px 12px; min-width: 0; display: flex; flex-direction: column;
  box-shadow: 0 1px 2px rgba(26, 29, 35, 0.04);
}
.tc-pane-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.tc-pane-name { font-size: 13px; font-weight: 600; color: var(--ink); }
.tc-pane-stat { font-size: 12px; font-variant-numeric: tabular-nums; }
.tc-area { width: 100%; resize: vertical; min-height: 14rem; }
/* The drop target has to be obvious while a file is over it, or nothing says
   the box will take one. */
.tc-area.tc-dropping { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(180, 83, 9, 0.14); }

.tc-split { align-self: stretch; justify-self: stretch; position: relative; cursor: col-resize; border-radius: 4px; }
.tc-split::before { content: ""; position: absolute; top: 14px; bottom: 14px; left: 50%; width: 3px; margin-left: -1.5px; background: var(--field-border); border-radius: 2px; }
.tc-split:hover::before, .tc-split:focus-visible::before { background: var(--accent); width: 4px; margin-left: -2px; }
.tc-split:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.tc-optbar { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin: 14px 0 0; }
.tc-shortcut { font-size: 12px; }
.tc-shortcut kbd {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px; background: var(--surface); border: 1px solid var(--field-border);
  border-bottom-width: 2px; border-radius: 4px; padding: 1px 5px; color: var(--ink);
}
.cmp-opt { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--ink); cursor: pointer; }

/* No overflow here. position:sticky on the header resolves against its
   nearest scrolling ancestor, and an overflow:hidden box never scrolls --
   which would pin the header to a box that does not move, i.e. not at all. */
.tc-result { padding-top: 0; }
/* Sticky, so the counts and the jump buttons stay reachable however far down
   a long comparison you have scrolled. */
.tc-resulthead {
  position: sticky; top: 0; z-index: 2; background: var(--surface);
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  flex-wrap: wrap; padding: 14px 0 12px; border-bottom: 1px solid var(--border);
  margin-bottom: 12px;
}
.tc-counts { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.tc-chip { font-size: 12px; padding: 2px 9px; border-radius: 99px; font-weight: 600; }
.tc-chip-chg { background: var(--warning-bg); color: var(--warning); border: 1px solid var(--warning-line); }
.tc-chip-add { background: var(--ok-bg); color: var(--ok); border: 1px solid var(--ok-line); }
.tc-chip-del { background: var(--error-bg); color: var(--error); border: 1px solid var(--error-line); }
.tc-jump { display: flex; align-items: center; gap: 8px; }
.tc-jump .meta { font-variant-numeric: tabular-nums; }

/* Four columns: a line number and a cell for each side. The numbers are the
   ones from the pasted text, so they still point at something real after
   blank lines have been dropped. */
/* ONE grid for the whole output, not one grid per row.
   position: relative so a row's offsetTop is measured against this box, which
   is what the jump buttons scroll by. */
.cmp-out {
  border: 1px solid var(--border); border-radius: 8px; background: var(--surface);
  overflow: auto; max-height: 34rem; position: relative;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px; line-height: 1.6;
  display: grid;
  grid-template-columns: 3.5em minmax(0, 1fr) 3.5em minmax(0, 1fr);
  align-content: start;
  justify-content: start;
}
/* The rows keep their classes -- every tint and marker below still selects
   through them -- but generate no box of their own, so their cells become
   items of the grid above. That is what holds the columns in line when the
   columns are sized to content: per-row grids agree only while each is told
   the same fraction, and the moment a column is max-content every row sizes
   itself and the two halves stop lining up. */
.cmp-row { display: contents; }

.cmp-num {
  text-align: right; padding: 1px 8px 1px 6px; color: var(--muted);
  background: var(--surface-2); border-right: 1px solid var(--border);
  user-select: none; font-variant-numeric: tabular-nums;
}
/* Wrapped by default: a long line matters more than aligned rows, and a diff
   you have to scroll sideways hides the thing you opened it to find. Not
   always, though -- fixed-width records and long EDI segments read better
   unwrapped, where a column means the same thing on every line. Hence the
   toggle -- see .cmp-split below, which rebuilds the output as two locked
   panes rather than merely turning wrapping off. */
.cmp-cell { padding: 1px 10px; white-space: pre-wrap; overflow-wrap: anywhere; min-height: 1.6em; }

/* Unwrapped: two panes, locked together.
   A single wide grid will not do here. Scrolling one would walk right through
   the whole left column, then the line numbers, then the right column -- so
   the same character offset in both halves could never be on screen at once,
   which is the entire point of reading a diff side by side.

   Each pane therefore owns BOTH scroll axes and the two are kept in step by
   file_compare.js. Both axes, not just the horizontal one: overflow-x: auto
   computes a visible overflow-y to auto as well, so a pane cannot be given a
   sideways scrollbar and still defer its vertical scrolling to a parent. With
   nothing wrapped every line is exactly one line tall, so matching scrollTop
   puts the same row against the same row. */
.cmp-out.cmp-split {
  display: grid; grid-template-columns: 1fr 1fr;
  overflow: hidden; align-content: stretch;
}
.cmp-half { overflow: auto; max-height: 34rem; min-width: 0; }
.cmp-half + .cmp-half { border-left: 1px solid var(--border); }

/* width: max-content so a long line extends the line box and its tint runs
   the whole way; min-width: 100% so a short one still fills the pane. */
.cmp-line {
  display: grid; grid-template-columns: 3.5em minmax(0, 1fr);
  width: max-content; min-width: 100%;
}
.cmp-split .cmp-cell { white-space: pre; overflow-wrap: normal; }

.cmp-half .cmp-del > .cmp-cell { background: var(--error-bg); }
.cmp-half .cmp-add > .cmp-cell { background: var(--ok-bg); }
.cmp-half .cmp-chg > .cmp-cell { background: var(--warning-bg); }
/* The side that has nothing on this line. Reads as absent rather than as
   unchanged, which is what an empty white row would say. */
.cmp-gap > .cmp-cell { background: var(--surface-2); }
.cmp-line .cmp-skip-note { grid-column: 1 / -1; }

@container (max-width: 640px) {
  /* No room for two panes side by side. One column, and the panes stack --
     which also means the sideways scroll of each is its own, and syncing
     them is meaningless, so the script leaves them alone. */
  .cmp-out.cmp-split { grid-template-columns: 1fr; }
  .cmp-half { max-height: 17rem; }
  .cmp-half + .cmp-half { border-left: 0; border-top: 1px solid var(--border); }
}

/* nth-CHILD, not nth-of-type: every child of a row is a div, so "first of
   type" is the line-number column rather than the cell. The four children are
   always num, cell, num, cell, so the left cell is 2 and the right is 4. */
.cmp-del > .cmp-cell:nth-child(2) { background: var(--error-bg); }
.cmp-add > .cmp-cell:nth-child(4) { background: var(--ok-bg); }
.cmp-chg > .cmp-cell { background: var(--warning-bg); }

/* The word-level marks. Stronger than the row tint they sit on, because the
   row only says "something here moved" and these say what. */
.cmp-word-del { background: var(--error-line); border-radius: 3px; }
.cmp-word-add { background: var(--ok-line); border-radius: 3px; }

/* Where the jump buttons have landed. A bar on the leading edge rather than a
   background, so it marks the row without arguing with the tint that already
   says what kind of change it is. On the cells rather than the row, because a
   display:contents row has no box to paint. */
.cmp-at > .cmp-num:nth-child(1) { box-shadow: inset 3px 0 0 var(--accent); }

/* Likewise: the note carries what the row used to, since the row no longer
   paints anything. */
.cmp-skip-note {
  grid-column: 1 / -1; padding: 4px 12px; font-size: 11.5px; color: var(--muted);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  background: var(--surface-2);
  border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
}

/* The input boxes follow the same switch. A textarea soft-wraps by default;
   turning that off gives it its own sideways scroll. */
.tc-area.cmp-nowrap { white-space: pre; overflow-wrap: normal; overflow-x: auto; }

/* Full width has to reach the diff as well. The shared rule keeps a panel's
   children in the reading column, which is right for prose and wrong for the
   one thing the button was pressed for. */
body.tester-wide main > .tc-result > *,
body.tester-wide main > .tc-grid,
body.tester-wide main > .tc-toolbar,
body.tester-wide main > .tc-optbar { max-width: none; }


/* ===========================================================================
   THE 2026 PASS
   ---------------------------------------------------------------------------
   Appended, not woven in. Everything above still declares its own padding,
   radius and layout; this layer changes type, surfaces, chrome and the narrow
   layout, and wins by coming last at equal specificity. Keeping it in one
   block means the whole visual change can be read -- or reverted -- as one
   thing, which matters for a restyle that touches every page at once.
   =========================================================================== */

body {
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4 { text-wrap: balance; letter-spacing: -.011em; }
h1 { font-size: 25px; font-weight: 600; }
h2 { font-size: 16px; font-weight: 600; }
h3 { font-size: 14px; font-weight: 600; }

/* A tool is not prose. The working column widens; the sentences inside it do
   not, so reading stays comfortable while tables and diffs get the room. */
main { max-width: 1160px; padding: 26px 22px 64px; }
main > p, .subtitle, .panel > p.meta { max-width: 72ch; }
.subtitle { font-size: 14.5px; color: var(--muted); }

/* Anything carrying an address -- an X12 element, a host, a WID -- reads as a
   mono chip. Taken from the app's own x12_reference(): the interface should
   speak the way the tool already writes. */
code, .mfa-key, .mfa-secret {
  font-family: var(--mono);
  font-size: .88em;
  background: var(--surface-2);
  border: 1px solid var(--line-soft);
  border-radius: 4px;
  padding: 1px 6px;
  color: var(--ink-2);
}
.code-area, .code-input, .message-block, .xml-dump, pre { font-family: var(--mono); }
.stat-value, .brand-num, .legend-count, .mfa-count {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------- surfaces --- */
.panel, .tool-card, .user-card, .chart-panel, .weather-card, .locked-panel,
.advanced-json, .login-wrap {
  background: var(--surface);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
}
/* Not everything is a card. Only what should sit above the page gets lifted;
   a uniform shadow on every block flattens the hierarchy it was meant to
   create. */
.panel, .login-wrap { box-shadow: var(--shadow); }
.tool-card, .user-card { box-shadow: none; }

/* --------------------------------------------------------------- chrome --- */
header, .app-header { background: var(--surface); border-bottom: 1px solid var(--border); }
.nav { background: var(--surface); border-bottom: 1px solid var(--border); }
.nav-trigger, .nav-menu a { font-size: 13.5px; border-radius: var(--radius-sm); }
.nav-trigger.is-active { background: var(--accent-quiet); color: var(--accent-ink); }
/* The trail earns its own band. This app has real depth -- tool, then
   template, then mapping -- and a breadcrumb sharing a row with the menus
   gets lost in them. */
.crumbs {
  background: var(--surface-2);
  border-bottom: 1px solid var(--line-soft);
  font-size: 12.5px;
}
.mode-badge { border-color: var(--border); color: var(--muted); }

/* --------------------------------------------------------------- chips --- */
.status {
  font-size: 11.5px; font-weight: 500; padding: 2px 9px;
  border: 1px solid transparent; border-radius: 999px;
}
.status-error, .status-failed { background: var(--error-bg); color: var(--error); border-color: var(--error-line); }
.status-warning, .status-aborted { background: var(--warning-bg); color: var(--warning); border-color: var(--warning-line); }
.status-ok { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-line); }
.status-inactive { background: var(--surface-2); color: var(--muted); border-color: var(--border); }

.flash { border-radius: var(--radius); font-size: 13.5px; border-width: 1px; border-style: solid; }
.flash-ok { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-line); }
.flash-error { background: var(--error-bg); color: var(--error); border-color: var(--error-line); }
.flash-warn { background: var(--warning-bg); color: var(--warning); border-color: var(--warning-line); }
.hint { background: var(--info-bg); border: 1px solid var(--info-line); color: var(--ink-2); border-radius: var(--radius); }

/* -------------------------------------------------------------- buttons --- */
button, .button, .btn {
  font-family: inherit; font-weight: 500; border-radius: var(--radius-sm);
  transition: background .12s ease, border-color .12s ease;
}
button:hover, .button:hover { background: var(--accent-ink); }
button.secondary, .button.secondary, .btn.secondary {
  background: var(--surface); color: var(--ink-2); border: 1px solid var(--border);
}
button.secondary:hover, .button.secondary:hover, .btn.secondary:hover {
  background: var(--surface-2); color: var(--ink); border-color: var(--field-border-hover);
}
button.danger, .button.danger { background: var(--error); color: var(--on-accent); border-color: var(--error); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* --------------------------------------------------------------- tables --- */
table { font-size: 13px; }
th {
  font-size: 11px; letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted); font-weight: 500;
  background: var(--surface-2); border-bottom: 1px solid var(--border);
}
td { border-bottom: 1px solid var(--line-soft); }
tbody tr:hover td { background: var(--surface-2); }
.table-scroll, .ip-table { border-radius: var(--radius); }

/* Severity as form, not only as a word: a stripe on the leading edge means a
   failed row is found before anything is read. */
tr.row-error td:first-child { box-shadow: inset 3px 0 0 var(--error); }
tr.row-warning td:first-child { box-shadow: inset 3px 0 0 var(--warning); }
tr.row-ok td:first-child { box-shadow: inset 3px 0 0 var(--ok); }

/* ============================================================ narrow ===== */
/* Container queries where the question is "how much room did this get" -- a
   table does not care how wide the window is. Media queries stay for the page
   chrome, which genuinely is about the window. */
main, .panel { container-type: inline-size; }

@container (max-width: 640px) {
  /* Four columns and no room is a table nobody reads. Each row becomes a
     record, and the headings that are no longer above the data move into it.
     Needs data-label on the cells; a table without them still scrolls. */
  .t-stack thead { display: none; }
  .t-stack tbody tr { display: block; }
  .t-stack tbody tr:hover td { background: none; }
  .t-stack tbody td { display: block; border: 0; padding: 2px 0; box-shadow: none !important; }
  .t-stack tbody td[data-label]::before {
    content: attr(data-label); display: block;
    font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
  }

  /* Side by side becomes unified: four children in a two-column grid wrap to
     two rows, which is the old line with the new one beneath it. */
  .cmp-out { grid-template-columns: 2.8em minmax(0, 1fr); }
  .cmp-row > .cmp-num:nth-child(3), .cmp-row > .cmp-cell:nth-child(4) { display: none; }
  .cmp-chg > .cmp-num:nth-child(3), .cmp-chg > .cmp-cell:nth-child(4) { display: block; }
  .cmp-add > .cmp-num:nth-child(1), .cmp-add > .cmp-cell:nth-child(2) { display: none; }
  .cmp-add > .cmp-num:nth-child(3), .cmp-add > .cmp-cell:nth-child(4) { display: block; }

  .oe2-row > .oe2-field, .oe2-row > .oe2-field.grow { flex: 1 1 100%; }
  .stacked-form { max-width: none; }
  .oe2-field select, .oe2-field input,
  button, .button, .btn { min-height: 40px; }
  .btn.small, button.small { min-height: 36px; }
}

@media (max-width: 860px) {
  main { padding: 18px 16px 48px; }
  h1 { font-size: 21px; }
  /* The nav itself is handled below, by how much room it has rather than by
     a guess about the device. Hiding navigation at phone
     width is how a tool becomes unusable on the device it is most often
     opened on -- and these pages get opened on a phone at 7am, when an
     integration has failed overnight. */
  .toolbar { justify-content: flex-start; flex-wrap: wrap; }
  .tool-grid { grid-template-columns: 1fr; }
}

@media (max-width: 560px) {
  .brand-name { font-size: 16px; }
  .brand-tag, .mode-badge { display: none; }
  .stat-row { flex-wrap: wrap; }
}


/* ---- Small screens, and the desktop view someone asks for ----------------
   Every page now declares width=device-width, which it did not before -- so
   until this commit a phone laid the app out at its default ~980px and zoomed
   out, and none of the narrow rules above could ever fire.

   Nothing here locks the scale. No user-scalable=no, no maximum-scale: pinch
   zoom stays, and "Request Desktop Site" keeps working, because that option
   is the browser overriding the viewport width with a wide one. The layout
   follows it on its own -- a container query asks how much room it was given,
   and in desktop mode the answer is ~980px, so the desktop layout is what
   renders. There is nothing to opt out of and nothing to detect. */
html {
  /* Stops iOS inflating text in ways that break a considered type scale. 100%
     rather than none, because none also disables the user's own zoom. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

img, svg, canvas { max-width: 100%; }

/* A hostname, a WID and an X12 document id have no spaces in them and are all
   over these pages. On a phone one of them is wider than the screen, and a
   single unbreakable string is enough to make the whole page scroll sideways. */
@container (max-width: 640px) {
  code, .mono, .ref, .mfa-key, .mfa-secret, td, .message-cell { overflow-wrap: anywhere; }
}
@media (max-width: 640px) {
  code, .mfa-key, .mfa-secret { overflow-wrap: anywhere; }
}



/* ---- The nav ribbon ------------------------------------------------------
   The menus take their own full-width row beneath the brand, at every width.

   They used to share a row with the brand and take whatever it left over,
   which is a squeeze that gets worse as the brand gets longer and collapses
   entirely on a phone -- the menus ended up a clipped vertical column against
   the right edge. A row of their own cannot be squeezed by anything.

   It is also simply the better shape on a wide screen: the brand identifies
   the product, the ribbon is how you move around it, and those are two jobs
   that read better stacked than interleaved.

   The negative margins cancel the header's own padding so the ribbon runs
   edge to edge; they are expressed against the same variables the padding
   uses, so the two cannot drift apart at a breakpoint. */
header { container-type: inline-size; }
.app-header { row-gap: 10px; }

.app-header > .nav {
  flex: 1 0 100%;
  width: 100%;
  margin-inline: calc(var(--header-pad-x) * -1);
  margin-bottom: calc(var(--header-pad-y) * -1);
  padding: 5px var(--header-pad-x);
  background: var(--surface-2);
  border-top: 1px solid var(--line-soft);
  border-bottom: 0;
  flex-wrap: wrap;
  row-gap: 4px;
  overflow: visible;
  position: relative;
}

/* The account menu sits at the far end of the ribbon, where an account menu
   belongs. On a wrapped row it takes the end of whatever row it landed on,
   which is still the right answer. */
.nav-menu-end { margin-left: auto; }
.nav-menu { overflow: visible; }

/* A panel can never be wider than the screen, whatever else is true. */
.nav-panel { max-width: calc(100vw - 20px); }

/* Placement is decided by the room the HEADER has, not by a device. That is
   what keeps working when the phone is rotated, when a window is dragged
   narrower, when text size is raised, and when somebody taps Request Desktop
   Site -- the header is about 980px wide again at that point and the wide
   placement returns by itself, with nothing to detect and nothing to toggle. */
@container (max-width: 700px) {
  /* Each menu stops being the panel's positioning context, so the panel hangs
     off the ribbon and spans it rather than off a trigger that could be
     anywhere in a wrapped row.

     That is the whole of the account-menu bug. right: 0 keeps the panel on
     screen while its trigger sits at the far right of one long row, and
     pushes it off the left edge the moment wrapping moves that trigger to the
     start of a second row. Left-aligning only moves the problem -- a trigger
     near the right of a row then overflows the other way. Spanning the ribbon
     is the one placement that is correct wherever the trigger landed. */
  .nav-menu { position: static; }
  .nav-panel,
  .nav-menu-end .nav-panel {
    left: 0;
    right: 0;
    width: auto;
    min-width: 0;
    max-width: none;
    top: calc(100% + 4px);
  }
}

/* ---- Tables that survive a narrow screen ---------------------------------
   "D e l e t e" down the page, one letter per line, is a button squeezed to a
   few pixels by a table with more columns than room. A word in a control is
   never a wrap opportunity -- if there is no space for it, the table should
   scroll or stack, not shred the label. */
button, .button, .btn, .row-actions a, .status, th { white-space: nowrap; }

/* Every table gets one of these, added by static/tables.js. The box scrolls;
   the page does not. Before this, a wide table widened the document and took
   every paragraph on the page sideways with it -- which is why the text
   beside one was half off screen. */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}
.table-scroll > table { margin: 0; }
/* Let a wide table be as wide as it needs inside its own box, rather than
   compressing every column until nothing is legible. */
.table-scroll > table { min-width: max-content; }

@container (max-width: 640px) {
  /* Stacked, the table is as wide as the screen and no wider. */
  .table-scroll > table.t-stack { min-width: 0; width: 100%; }

  .t-stack tbody td[data-label]::before { flex-basis: 100%; margin-bottom: 1px; }

  /* A cell of actions becomes a row of buttons rather than a column of them.
     :has() is what lets the layout depend on what the cell actually holds,
     which beats tagging every action cell in ten templates. */
  .t-stack tbody td:has(button),
  .t-stack tbody td:has(form),
  .t-stack tbody td:has(a.btn),
  .t-stack tbody td.row-actions {
    display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
    margin-top: 6px;
  }
  .t-stack tbody td form { margin: 0; }

  /* One place decides a record's padding and its separator, rather than two
     rules quietly adding a border each. */
  .t-stack tbody tr { padding: 12px 14px; }
  .t-stack tbody tr + tr { border-top: 1px solid var(--border); }
  .t-stack tbody td + td { margin-top: 7px; }
}

/* ---- File Compare: pulling a run from a tenant --------------------------
   Built for both widths at once. The integration picker is the part that
   would otherwise fail on a phone: a list of two hundred names needs its own
   scroll, and a scroll box needs a height that suits the screen it is on
   rather than a fixed one chosen for a laptop. */
.fc-pull > summary, .panel > summary {
  cursor: pointer;
  font-size: 14px;
  color: var(--ink-2);
  padding: 2px 0;
}
.fc-pull > summary::marker, .panel > summary::marker { color: var(--muted); }
.fc-pull[open] > summary, .panel[open] > summary { margin-bottom: 14px; }

/* Its own scroll, so a long list does not push the date range and the button
   below it off the bottom of the page. Capped in vh rather than px: half a
   laptop screen and half a phone screen are different numbers, and a fixed
   one is wrong on whichever it was not chosen for. */
.fc-systems {
  max-height: min(46vh, 340px);
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  padding: 6px;
  margin: 4px 0 10px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 2px;
}
.fc-system {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 9px;
  border-radius: var(--radius-sm);
  font-size: 13px;
  cursor: pointer;
  min-width: 0;
}
.fc-system:hover { background: var(--surface-2); }
.fc-system input { flex: none; }
/* An integration name is long and has no spaces to break on. It wraps rather
   than widening the box and taking the page sideways with it. */
.fc-system span { overflow-wrap: anywhere; }
.fc-system:has(input:checked) { background: var(--accent-quiet); color: var(--accent-ink); }

#fc-runs { margin-top: 12px; }
#fc-picked { margin: 0; font-variant-numeric: tabular-nums; }

@container (max-width: 640px) {
  /* One column of integrations, and a taller share of a short screen -- on a
     phone the list is the only thing on screen while you are using it. */
  .fc-systems { grid-template-columns: 1fr; max-height: 52vh; }
  .fc-pull > summary, .panel > summary { font-size: 13.5px; }
}

/* ============================================================ dashboard ===
 *
 * The Error Dashboard's filter panel and its Workday statuses.
 *
 * The status names come from the tenant, so the class on a chip is the status
 * slugged: "Completed with Errors" becomes .status-completed-with-errors. A
 * status this file has no rule for still renders -- it falls through to the
 * base .status chip, which is grey and readable. That is on purpose: the
 * vocabulary is Workday's and it is allowed to grow without the page
 * breaking.
 */
.slice-completed, .swatch.slice-completed { fill: var(--chart-ok); background: var(--chart-ok); }
.slice-completed-with-errors, .swatch.slice-completed-with-errors { fill: var(--chart-error); background: var(--chart-error); }
.slice-completed-with-warnings, .swatch.slice-completed-with-warnings { fill: var(--chart-warning); background: var(--chart-warning); }
/* The not-run family gets its own hue. It is not an abort and not a
   warning: the run never happened, which is a different thing to chase. */
.slice-not-run, .swatch.slice-not-run,
.slice-not-run-due-to-exceptions, .swatch.slice-not-run-due-to-exceptions { fill: var(--chart-stalled); background: var(--chart-stalled); }
.slice-terminated, .swatch.slice-terminated,
.slice-cancelled, .swatch.slice-cancelled,
.slice-processing, .swatch.slice-processing,
.slice-waiting, .swatch.slice-waiting,
.slice-unknown, .swatch.slice-unknown { fill: var(--chart-idle); background: var(--chart-idle); }

.status-completed { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-line); }
.status-completed-with-errors,
.status-not-run,
.status-not-run-due-to-exceptions { background: var(--error-bg); color: var(--error); border-color: var(--error-line); }
.status-completed-with-warnings,
.status-terminated,
.status-cancelled { background: var(--warning-bg); color: var(--warning); border-color: var(--warning-line); }
.status-processing,
.status-waiting { background: var(--surface-2); color: var(--muted); border-color: var(--border); }

/* The filter panel. One form; the rows wrap on their own, so there is no
   breakpoint that has to be kept in step with the number of controls. */
.dash-filters { display: flex; flex-direction: column; gap: 14px; }
.dash-row { display: flex; gap: 14px; flex-wrap: wrap; align-items: flex-end; }
.dash-field { display: flex; flex-direction: column; gap: 6px; font-size: 13px; color: var(--muted); min-width: 0; }
.dash-field.grow { flex: 1 1 16rem; }
.dash-field select,
.dash-field input {
  padding: 8px 10px; border: 1.5px solid var(--field-border); border-radius: 6px;
  font-size: 14px; color: var(--ink); background: var(--surface); max-width: 100%;
}
.dash-field select[multiple] { padding: 4px; min-height: 9rem; }
.dash-field select[multiple] option { padding: 4px 6px; border-radius: 4px; }

.dash-statuses {
  border: 1.5px solid var(--border); border-radius: 8px; padding: 12px 14px;
  margin: 0; display: flex; flex-wrap: wrap; gap: 8px 18px;
}
.dash-statuses legend { font-size: 13px; color: var(--muted); padding: 0 6px; }
.dash-check { display: flex; align-items: flex-start; gap: 8px; font-size: 14px; line-height: 1.4; }
.dash-check input { margin-top: 3px; flex-shrink: 0; }
.dash-actions { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; margin-top: 4px; }

@media (max-width: 640px) {
  /* Every control full width, and the integration list shorter: on a phone it
     is one thing in a stack rather than one column of three. */
  .dash-field { flex: 1 1 100%; }
  .dash-field select[multiple] { min-height: 7rem; max-height: 40vh; }
  .dash-statuses { gap: 10px; }
  .dash-statuses .dash-check { flex: 1 1 100%; }
  .dash-actions button { flex: 1 1 100%; }
}

/* ---- Copy button on an XML dump ----
 *
 * The button floats over the top-right corner of the dump rather than sitting
 * above it, because a trace entry is a stack of headings and boxes and one
 * more row per box would push the XML itself off the screen.
 *
 * It sits on the dump's own background with a border, not on the XML: at 12px
 * monospace the text runs right under it, and a transparent button over code
 * is unreadable in exactly the case somebody needs it.
 */
.xml-copy-wrap { position: relative; }
.xml-copy {
  position: absolute; top: 6px; right: 6px;
  padding: 3px 10px; font-size: 12px; line-height: 1.5;
  color: var(--ink-2); background: var(--surface-2);
  border: 1.5px solid var(--field-border); border-radius: 5px;
  cursor: pointer; opacity: .85;
}
.xml-copy:hover, .xml-copy:focus-visible { opacity: 1; border-color: var(--accent); color: var(--ink); }
.xml-copy.copy-bad { border-color: var(--warning-line); color: var(--warning); }

@media (max-width: 640px) {
  /* Bigger target, and out of the way of the first line of XML, which on a
     phone is where the element name somebody is looking for usually is. */
  .xml-copy { top: 4px; right: 4px; padding: 6px 12px; font-size: 13px; }
  .xml-dump { padding-top: 38px; }
}

/* ---- A back link inside the page ----
 *
 * The crumb bar has one, but it sits at the top of the window and a detail
 * page can be a long scroll -- an event carries its whole message log now.
 * So the page carries its own: once under the heading, once at the end,
 * where somebody actually is when they have finished reading.
 */
.page-back { margin: 0 0 18px; }
.page-back-end { margin: 22px 0 0; }
.page-back a.back { font-size: 14px; }

@media (max-width: 640px) {
  /* A real target rather than a line of text: on a phone this is the control
     somebody reaches for most on this page. */
  .page-back a.back {
    display: block; text-align: center;
    padding: 11px 14px; font-size: 15px;
    border: 1.5px solid var(--field-border); border-radius: 8px;
    background: var(--surface);
  }
}

/* ---- Reconciliation counts ----
 *
 * The three numbers are the answer; the tables under them are the working.
 * So they get their own block at the top, sized to be read from across a
 * desk, with the two that cost money marked.
 */
.recon-counts { list-style: none; margin: 0 0 4px; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.recon-counts li { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; font-size: 14.5px; }
.recon-n {
  font-variant-numeric: tabular-nums; font-weight: 600; font-size: 21px;
  min-width: 3.2ch; text-align: right; color: var(--ink);
}
.recon-counts li.is-bad .recon-n { color: var(--error); }
.recon-counts li.is-warn .recon-n { color: var(--warning); }

/* ---------------------------------------------------------- task board ----
   Columns by status. On a wide screen they sit side by side; below that they
   stack, because four columns on a phone is four unreadable columns. The
   board scrolls inside its own box rather than taking the page sideways --
   the same rule every table here follows. */
.board {
  display: grid;
  grid-template-columns: repeat(5, minmax(200px, 1fr));
  gap: 12px;
  margin: 14px 0 22px;
  overflow-x: auto;
  padding-bottom: 4px;
}
.board-col {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px;
  min-width: 0;
}
.board-col > h2 {
  margin: 0 0 8px; font-size: 13px; text-transform: uppercase;
  letter-spacing: .06em; color: var(--muted);
  display: flex; justify-content: space-between; align-items: baseline;
}
.col-count { font-variant-numeric: tabular-nums; font-weight: 700; color: var(--ink); }
.col-empty { margin: 0; font-size: 12.5px; color: var(--muted); }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 11px;
  margin-bottom: 9px;
  /* The priority stripe on the leading edge, the same device the event
     tables use for severity: found before it is read. */
  box-shadow: inset 3px 0 0 var(--border);
}
.card-p-high { box-shadow: inset 3px 0 0 var(--error); }
.card-p-low { box-shadow: inset 3px 0 0 var(--line-soft); }
.card-overdue { border-color: var(--error); }
.card h3 { margin: 0 0 4px; font-size: 14.5px; line-height: 1.35; }
.card-detail {
  margin: 0 0 6px; font-size: 12.5px; color: var(--muted); line-height: 1.45;
  overflow-wrap: anywhere;
}
.card-meta {
  margin: 0 0 8px; font-size: 12px; color: var(--muted);
  overflow-wrap: anywhere;
}
.card-none { font-style: italic; }
.card-org {
  display: inline-block; margin-left: 4px; padding: 0 6px;
  border-radius: 999px; background: var(--surface-2);
  font-size: 10.5px; letter-spacing: .03em;
}
.card-due { color: var(--ink); }
.card-overdue .card-due { color: var(--error); font-weight: 600; }
.card-pri { font-weight: 600; }

.card-move { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.card-select {
  flex: 1 1 100%; min-width: 0; font-size: 12.5px;
  /* A thumb target on a phone, where this whole board is read. */
  min-height: 36px;
}
.card-drop { margin-top: 6px; font-size: 12px; }

.board-switch {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  margin: 10px 0 4px;
}
.board-counts { flex-direction: row; flex-wrap: wrap; gap: 8px 22px; }

/* Visible to a screen reader, to nobody else. Each card's two selects need
   labels and the column heading is not one -- there are as many "status"
   selects on this page as there are cards. */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@container (max-width: 860px) {
  .board { grid-template-columns: 1fr; overflow-x: visible; }
}

/* Project first, then the narrowings. One row on a desktop, stacked on a
   phone, because four controls side by side at 380px are four unusable
   controls. */
.board-filters {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end;
  margin: 12px 0 6px;
}
.board-filters .oe2-field { flex: 1 1 160px; min-width: 0; }
.card-proj {
  white-space: nowrap; color: var(--accent);
}
.proj-list { list-style: none; margin: 0 0 10px; padding: 0; }
.proj-list li {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: baseline;
  padding: 5px 0; border-bottom: 1px solid var(--line-soft);
}

/* ---- task detail, in a dialog or on its own page ---- */
.task-dialog {
  width: 100%; max-width: none; height: 100%; max-height: none;
  border: 0; padding: 16px; margin: 0;
  background: transparent;
}
.task-dialog[open] { display: flex; align-items: flex-start; justify-content: center; }
.task-dialog::backdrop { background: rgba(8, 14, 20, .62); }
.task-dialog-card {
  position: relative;
  width: 100%; max-width: 720px; max-height: 92vh; overflow: auto;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 18px 20px 22px;
}
.task-dialog-close {
  position: sticky; top: 0; float: right;
  font: inherit; font-size: 13px; background: var(--surface);
  border: 1px solid var(--border); border-radius: 9px;
  /* A thumb, not a pointer. */
  min-height: 40px; padding: 0 16px; cursor: pointer;
}

.task-detail { min-width: 0; }
.td-title { margin: 0 0 4px; font-size: 19px; line-height: 1.3; }
.td-line { margin: 0 0 10px; font-size: 12.5px; color: var(--muted); }
.td-detail { margin: 0 0 14px; line-height: 1.55; overflow-wrap: anywhere; }
.td-head {
  margin: 20px 0 8px; font-size: 12px; text-transform: uppercase;
  letter-spacing: .07em; color: var(--muted);
  border-top: 1px solid var(--line-soft); padding-top: 12px;
}
.td-files { list-style: none; margin: 0; padding: 0; }
.td-files li {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline;
  padding: 4px 0; border-bottom: 1px solid var(--line-soft);
}
.td-upload { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; margin-top: 8px; }

.td-comments { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
/* A card per comment, edged and lightly washed in its author's colour, so a
   conversation reads as voices rather than one block of text. The tone is
   mixed into the surface, so the same six hues sit right on either theme. */
.td-comment {
  --tone: var(--who-0);
  padding: 10px 14px 10px 12px;
  border: 1px solid color-mix(in srgb, var(--tone) 35%, var(--border));
  border-left: 4px solid var(--tone);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--tone) 9%, var(--surface));
  color: var(--ink);
}
.td-tone-1 { --tone: var(--who-1); }
.td-tone-2 { --tone: var(--who-2); }
.td-tone-3 { --tone: var(--who-3); }
.td-tone-4 { --tone: var(--who-4); }
.td-tone-5 { --tone: var(--who-5); }
.td-who { margin: 0 0 6px; font-size: 12.5px; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
.td-name { color: color-mix(in srgb, var(--tone) 70%, var(--ink)); }
.td-who .meta { font-weight: 400; margin-left: 6px; }
.td-body { font-size: 14px; line-height: 1.55; overflow-wrap: anywhere; }
.td-body p { margin: 0 0 8px; }
.td-body ul, .td-body ol { margin: 0 0 8px; padding-left: 20px; }
.td-body blockquote {
  margin: 0 0 8px; padding-left: 10px;
  border-left: 3px solid var(--border); color: var(--muted);
}
.td-body code {
  background: var(--surface-2); padding: 0 4px; border-radius: 4px;
  font-size: 12.5px;
}
/* A pasted screenshot, kept inside the column it was pasted into. */
.rt-img {
  max-width: 100%; height: auto; display: block;
  margin: 6px 0; border: 1px solid var(--border); border-radius: 8px;
}
.td-hint code { font-size: 11.5px; }
.td-say { margin-top: 10px; }

/* ---- file share ---- */
.drop-zone {
  border: 2px dashed var(--border); border-radius: var(--radius);
  padding: 22px 16px; text-align: center; cursor: pointer;
  background: var(--surface-2);
}
.drop-zone.over { border-color: var(--accent); background: var(--surface); }
.drop-say { margin: 0 0 4px; font-size: 15px; }
.drop-zone input[type="file"] { margin-top: 10px; }

.upload-bar { margin-top: 10px; }
.upload-track {
  height: 8px; border-radius: 999px; overflow: hidden;
  background: var(--surface-2); border: 1px solid var(--border);
}
.upload-fill {
  height: 100%; width: 0; background: var(--accent);
  transition: width .2s linear;
}

.share-link {
  width: 100%; max-width: 320px; font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.share-table td { vertical-align: top; }
.rename-note summary {
  cursor: pointer; font-size: 12.5px; color: var(--muted);
  /* A thumb target even as a disclosure triangle. */
  padding: 6px 0;
}
.rename-note input { width: 100%; max-width: 260px; margin-bottom: 6px; }

/* The page somebody with no account sees. Narrow and quiet: it is the file
   and nothing else, with no navigation into an application they have no
   business in. */
.share-get {
  max-width: 520px; margin: 48px auto; padding: 0 16px;
}

/* ---- the editing box ----
   A contenteditable that has to look and behave like a field, because to
   the person typing in it that is what it is. */
.rt-box {
  min-height: 120px; padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 0 0 var(--radius) var(--radius);
  background: var(--surface); color: var(--ink);
  font-size: 14.5px; line-height: 1.55;
  overflow-wrap: anywhere;
}
.rt-box:focus { outline: 2px solid var(--accent); outline-offset: -2px; }
.rt-box:empty::before {
  content: attr(data-placeholder);
  color: var(--muted);
}
.rt-box p { margin: 0 0 8px; }
.rt-box ul, .rt-box ol { margin: 0 0 8px; padding-left: 24px; }
.rt-box blockquote {
  margin: 0 0 8px; padding-left: 10px;
  border-left: 3px solid var(--border); color: var(--muted);
}
.rt-box img { max-width: 100%; height: auto; border-radius: 8px; margin: 6px 0; }

/* The real field. It IS the editor when script does not run, so it is
   hidden by script rather than by CSS -- a stylesheet that loaded while the
   script did not would leave nowhere to type. */
.rt-fallback { width: 100%; }

/* ---- the formatting bar ----
   Buttons that write the marks, so nobody has to know the marks exist. It
   should read as a word processor's toolbar, which means the controls look
   like controls and sit directly on top of the box they act on. */
.rt-bar {
  display: flex; flex-wrap: wrap; gap: 4px; align-items: center;
  padding: 6px; margin-bottom: -1px;
  border: 1px solid var(--border);
  border-radius: var(--radius) var(--radius) 0 0;
  background: var(--surface-2);
}
.rt-btn {
  font: inherit; font-size: 13px; line-height: 1;
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--border); border-radius: 6px;
  /* 34px so a thumb can hit one. This is used on a phone as well. */
  min-height: 34px; min-width: 34px; padding: 0 9px;
  cursor: pointer;
}
.rt-btn:hover { border-color: var(--accent); color: var(--accent); }
.rt-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.rt-btn.is-on { background: var(--accent); color: var(--surface); border-color: var(--accent); }
.rt-mono { font-family: var(--mono, monospace); font-size: 12px; }
.rt-sep {
  width: 1px; align-self: stretch; margin: 2px 4px;
  background: var(--border);
}
.td-say textarea {
  width: 100%; border-radius: 0 0 var(--radius) var(--radius);
}
.rt-preview {
  border: 1px solid var(--border); border-top: 0;
  border-radius: 0 0 var(--radius) var(--radius);
  padding: 10px 12px; background: var(--surface-2);
  font-size: 14px; line-height: 1.55;
}
.rt-preview p:last-child { margin-bottom: 0; }
.rt-preview img { max-width: 100%; height: auto; border-radius: 8px; }

.td-sort { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
.td-sort label { font-size: 12px; color: var(--muted); }
.td-sort select { min-height: 34px; font-size: 12.5px; }

/* What happened to a task. Quieter than the conversation on purpose: the
   history is a reference, the comments are the discussion. */
.td-history { list-style: none; margin: 0; padding: 0; }
.td-history li {
  padding: 3px 0; font-size: 12.5px; color: var(--muted);
  border-bottom: 1px solid var(--line-soft);
}
.td-actor { color: var(--ink); font-weight: 600; }
.td-edit-note { margin-top: 6px; }
/* A plain link, not a disclosure triangle: the triangle made every comment
   look folded shut. */
.td-edit-note summary {
  cursor: pointer; font-size: 12px; color: var(--muted);
  list-style: none; display: inline-block;
  padding: 6px 0; text-decoration: underline; text-underline-offset: 2px;
}
.td-edit-note summary::-webkit-details-marker { display: none; }
.td-edit-note[open] summary { color: var(--ink); }
.td-edit-note form { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-end; }
.td-edit-note textarea { width: 100%; }

/* Dragging. The cursor says a card can be picked up; the column says it will
   be caught. Both are set by static/tasks.js and neither exists on a touch
   screen, where the Column dropdown is the interface rather than a fallback. */
.card[draggable="true"] { cursor: grab; }
.card-dragging { opacity: .45; cursor: grabbing; }
.board-col.over {
  background: var(--surface);
  outline: 2px dashed var(--accent); outline-offset: -4px;
}
.card h3 a {
  /* COLOURED LIKE A LINK, because it is one. Inheriting the body colour
     made the only way into a task look like a heading, which on a phone --
     where there is no hover to discover it with -- meant it read as not
     tappable at all. */
  color: var(--accent); text-decoration: none;
  /* A block with room around it, so the whole title line is the target
     rather than the glyphs. This is tapped with a thumb. */
  display: block; padding: 2px 0 4px;
}
.card h3 a:hover, .card h3 a:focus-visible { text-decoration: underline; }

/* And an explicit control beside it, so nobody has to guess. */
.card-open-btn {
  display: inline-flex; align-items: center;
  margin-top: 6px; padding: 0 12px;
  min-height: 34px;
  border: 1px solid var(--border); border-radius: 8px;
  font-size: 12.5px; font-weight: 600;
  color: var(--accent); text-decoration: none;
}
.card-open-btn:hover, .card-open-btn:focus-visible {
  border-color: var(--accent); background: var(--surface-2);
}
.card-tally { white-space: nowrap; }

/* ---- Launch parameter diff, on File Compare ---- */
.param-diff { width: 100%; }
.param-diff td, .param-diff th { vertical-align: top; }
.param-diff tr.is-diff td { background: var(--warning-bg); }
.param-diff .param-group { color: var(--muted); font-size: 12px; }

/* ---- Tools by company ----
 *
 * Replaces a company-by-tool matrix that grew a column with every tool and
 * was unreadable on a phone. A panel per company grows DOWNWARDS as tools
 * are added, which is the direction there is room in, and each tool gets its
 * name and its description rather than a column header and a tooltip.
 */
.tool-company { margin-bottom: 12px; }
.tool-company > summary { display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap; }
.tool-company > summary strong { font-size: 15px; }

.tool-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 6px 0 4px; }
.tool-actions-end { margin-left: auto; display: flex; gap: 8px; }

.tool-section {
  font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); margin: 16px 0 6px; font-weight: 600;
}
.tool-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.tool-list > li { border-top: 1px solid var(--line-soft); }
.tool-list > li:first-child { border-top: 0; }

/* The whole row is the label, so the description is part of the hit area
   rather than a thing beside it that does nothing when tapped. */
.tool-list .tool-check {
  display: flex; align-items: flex-start; gap: 11px;
  padding: 9px 4px; cursor: pointer; border-radius: 6px;
}
.tool-list .tool-check:hover { background: var(--surface-2); }
.tool-list .tool-check input { margin-top: 2px; flex-shrink: 0; }
.tool-list .tool-check strong { font-weight: 500; }

.tool-save {
  position: sticky; bottom: 0; z-index: 5;
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-top: 16px; padding: 12px 0;
  background: var(--bg); border-top: 1px solid var(--border);
}

@media (max-width: 640px) {
  .tool-actions-end { margin-left: 0; width: 100%; }
  .tool-actions-end .btn { flex: 1 1 0; }
  .tool-save button { flex: 1 1 100%; }
}

/* ---- Reconciliation column mapping ----
 *
 * One row per field: what it is on the left, which carrier column feeds it in
 * the middle, and what is actually in that column on the right. The samples
 * are the point of the panel -- two columns called "ID" are ordinary, and the
 * values are the only way to tell them apart.
 */
.rc-columns { margin: 0 0 14px; }
.rc-map { display: flex; flex-direction: column; gap: 2px; margin-top: 8px; }
.rc-map-row {
  display: grid;
  grid-template-columns: minmax(11rem, 1.1fr) minmax(9rem, 1fr) minmax(0, 1.3fr);
  gap: 12px; align-items: start;
  padding: 8px 4px; border-top: 1px solid var(--line-soft);
}
.rc-map-row:first-child { border-top: 0; }
.rc-map-name { font-size: 14px; }
.rc-map-row select {
  padding: 6px 8px; border: 1.5px solid var(--field-border); border-radius: 6px;
  font-size: 13px; color: var(--ink); background: var(--surface); max-width: 100%;
}
/* The values can be long and are somebody's data, so they wrap and break
   rather than widening the panel. */
.rc-map-sample { font-family: var(--mono); font-size: 12px; overflow-wrap: anywhere; }

@media (max-width: 720px) {
  /* One field per block on a phone: label, then picker, then samples. */
  .rc-map-row { grid-template-columns: 1fr; gap: 6px; }
  .rc-map-row select { width: 100%; }
}

/* ---- Output watch: recent file sizes ----
   A short column of run-and-size pairs, tabular so the digits line up and a
   number that has fallen off a cliff is visible without reading any of them. */
.watch-sizes { list-style: none; margin: 0; padding: 0; font-size: 12.5px; }
.watch-sizes li { display: flex; gap: 10px; justify-content: space-between; padding: 1px 0; }
.watch-sizes .mono { font-variant-numeric: tabular-nums; }

/* The file name under each run's date and size. Its own line, so the date and
   the size stay on one: with several files matched across runs by name, and
   every name carrying its own date, a column of sizes alone does not say
   which file any row is about. */
.watch-sizes li { flex-wrap: wrap; }
.ws-file {
  flex-basis: 100%; min-width: 0;
  font-size: 11.5px; color: var(--muted);
  overflow-wrap: anywhere;
}

/* Four columns where one of them is a sentence. Left at the max-content width
   every table in this application gets, the sentence sets the table's width
   and pushes the other three off the side -- which is the thing that made
   this page need sideways scrolling at all. Percentages instead, and the
   sentence wraps. */
.table-scroll > table.watch-table { min-width: 0; width: 100%; }
.watch-table th:nth-child(1), .watch-table td:nth-child(1) { width: 1%; white-space: nowrap; }
.watch-table th:nth-child(2), .watch-table td:nth-child(2) { width: 25%; }
.watch-table th:nth-child(3), .watch-table td:nth-child(3) { width: 38%; }
.watch-table th:nth-child(4), .watch-table td:nth-child(4) { width: 26%; }
.watch-table td { vertical-align: top; }
.watch-table .mono, .watch-table strong { overflow-wrap: anywhere; }

/* ---- Counts that filter ----
   The summary chip is now a button. It has to keep reading as a number and a
   label rather than as a control, so it borrows nothing from .btn: no fill,
   no border until it is hovered or on. */
.count-filter {
  display: flex; align-items: baseline; gap: 10px;
  font: inherit; text-align: left; color: inherit;
  background: none; border: 1px solid transparent; border-radius: var(--radius);
  padding: 4px 10px 4px 4px; margin: -4px 0 -4px -4px;
  cursor: pointer;
  /* A thumb, not a mouse pointer. This page is read on a phone. */
  min-height: 40px;
}
.count-filter:hover { border-color: var(--border); background: var(--surface-2); }
.count-filter:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.count-filter.is-on { border-color: var(--accent); background: var(--surface-2); }
.count-filter.is-on::after {
  content: "only this"; font-size: 10.5px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}

.link-button {
  font: inherit; color: var(--accent); background: none; border: 0;
  padding: 0; cursor: pointer; text-decoration: underline;
}
.link-button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---- Dragging a wide table sideways ----
   The scrollbar of an overflowing table sits at the bottom of the TABLE,
   which on a long result list is far below the window. These make the box
   itself the control: it says there is more to the right, and it can be
   grabbed and pulled. Classes are set by static/tables.js, so a table that
   does not overflow gets neither. */
.table-scroll.can-pan { cursor: grab; }
.table-scroll.is-panning { cursor: grabbing; user-select: none; }
.table-scroll.has-more {
  /* Inset, so it reads as content continuing under the edge rather than as a
     border drawn on it. */
  box-shadow: inset -16px 0 14px -14px var(--muted);
}

/* A grab cursor over a paragraph of text would be a lie -- those are still
   selectable, and selection is what most people want from a table cell. */
.table-scroll.can-pan td, .table-scroll.can-pan th { cursor: auto; }

/* ---- File Compare: the counts are the filter ----
 *
 * Clicking "11 removed" shows only those rows. The chips were spans; as
 * buttons they need the button reset undone, because the global button rule
 * gives them a filled accent background that would drown the colour that
 * tells you which kind they are.
 */
button.tc-chip {
  cursor: pointer; line-height: 1.5; min-height: 0;
  font-family: inherit; letter-spacing: 0;
}
button.tc-chip:hover { filter: brightness(1.08); }
button.tc-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* On: the chip inverts to its own colour so it reads as pressed without
   needing a tick, and stays legible in both themes. */
button.tc-chip.is-on { box-shadow: inset 0 0 0 2px currentColor; font-weight: 700; }

/* One class, one job. The wrapped rows are `display: contents` and the split
   panes are grids of their own, so anything that tried to restore a display
   value would have to know which layout it was in. This only ever removes. */
.cmp-hidden { display: none !important; }
.tc-counts-hint { margin: 6px 0 0; }

/* ---------------- Integration Investigator ----------------
   A trail of what it did, beside the report it wrote. Side by side on a wide
   screen so the evidence and the reasoning can be read together; stacked on
   a phone, trail first, because that is what moves while it works. */
.inv-form { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; margin-top: 12px; }
.inv-form .dash-field { flex: 1 1 16rem; }
.inv-past-title { font-size: 14px; margin: 18px 0 6px; }
.inv-past, .inv-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.inv-past li, .inv-list-row { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: baseline; }
.inv-list-row { padding: 10px 0; border-bottom: 1px solid var(--line-soft); }
.inv-list-row:last-child { border-bottom: 0; }
.inv-list-name { font-weight: 600; }
.inv-past-head, .inv-list-head { flex: 1 1 100%; color: var(--ink-2); font-size: 14px; }
.inv-pill { display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: 12px;
  font-weight: 600; letter-spacing: .02em; background: var(--surface-2); color: var(--ink-2);
  border: 1px solid var(--border); }
.inv-running { background: var(--info-bg); color: var(--info); border-color: var(--info-line); }
.inv-done { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-line); }
.inv-failed { background: var(--error-bg); color: var(--error); border-color: var(--error-line); }
.inv-eyebrow { margin: 0; font-size: 12px; text-transform: uppercase; letter-spacing: .08em; color: var(--accent); font-weight: 600; }
.inv-hidden-note { background: var(--warning-bg); color: var(--warning); border: 1px solid var(--warning-line); }
.inv-layout { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: 16px; align-items: start; }
.inv-layout > .panel { margin-top: 16px; min-width: 0; }
.inv-steps { list-style: none; margin: 0; padding: 0 0 0 14px; border-left: 2px solid var(--line-soft); display: grid; gap: 10px; }
.inv-step { position: relative; display: grid; grid-template-columns: 3.4em minmax(0, 1fr); gap: 8px; font-size: 14px; }
.inv-step::before { content: ""; position: absolute; left: -20px; top: .45em; width: 10px; height: 10px;
  border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 3px var(--surface); }
.inv-step-bad::before { background: var(--warning); }
.inv-step-report::before { background: var(--ok); }
.inv-step-at { color: var(--muted); font-family: var(--mono); font-size: 12px; font-variant-numeric: tabular-nums; padding-top: 2px; }
.inv-step-said { overflow-wrap: anywhere; }
.inv-step-new { animation: inv-in .35s ease-out; }
@keyframes inv-in { from { opacity: .2; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.inv-working { display: flex; align-items: center; gap: 8px; color: var(--muted); margin: 14px 0 0; }
.inv-working[hidden] { display: none; }
.spinner-sm { width: 14px; height: 14px; border-width: 2px; margin: 0; }
.inv-cost { margin-top: 12px; }
.inv-trail h2, .inv-report h2:first-child { margin-top: 0; }
.inv-report-head { display: flex; flex-wrap: wrap; gap: 8px; }
.inv-conf { background: var(--accent-quiet); color: var(--accent-ink); border-color: transparent; }
.inv-headline { font-size: 1.3rem; line-height: 1.3; margin: 12px 0 8px; text-wrap: balance; }
.inv-cause { color: var(--ink-2); line-height: 1.55; max-width: 68ch; }
.inv-resolves { background: var(--ok-bg); color: var(--ok); border: 1px solid var(--ok-line);
  border-radius: var(--radius-sm); padding: 8px 12px; }
.inv-report h3 { font-size: 13px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 20px 0 6px; }
.inv-evidence, .inv-fix { margin: 0; padding-left: 1.3em; display: grid; gap: 6px; line-height: 1.5; }
.inv-evidence li, .inv-fix li { overflow-wrap: anywhere; }
.inv-email { white-space: pre-wrap; overflow-wrap: anywhere; background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 12px 14px; font-family: var(--sans); font-size: 14px; line-height: 1.55; margin: 0 0 10px; }
@media (max-width: 860px) {
  .inv-layout { grid-template-columns: minmax(0, 1fr); gap: 0; }
  /* The answer first once there is one; while it works, the trail is all
     there is, and it is what moves. */
  .inv-report { order: -1; }
  .inv-form button { flex: 1 1 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .inv-step-new, .mc-changed .mc-tile-btn, .mc-tile.mc-running .mc-dot:last-child { animation: none; }
}

/* ---------------- Mission Control ----------------
   One tile per integration, worst first. State is carried three ways -- a
   coloured edge, a word, and the run history strip -- so it reads at a
   glance and without colour. The detail is a side panel on a wide screen
   and a sheet from the bottom on a phone. */
.mc-form .dash-actions { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.mc-live { display: inline-flex; gap: 6px; align-items: center; font-size: 14px; color: var(--ink-2); }
.mc-board { margin-top: 18px; }
.mc-board[hidden] { display: none; }
.mc-head { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px; align-items: flex-end; }
.mc-tenant { margin: 0; font-weight: 600; font-size: 1.05rem; }
.mc-head .meta { margin: 2px 0 0; }
.mc-counts { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.mc-count { display: flex; align-items: baseline; gap: 6px; padding: 6px 12px; border-radius: var(--radius-sm);
  background: var(--surface); border: 1px solid var(--border); font-size: 13px; color: var(--ink-2); }
.mc-count strong { font-size: 1.25rem; font-variant-numeric: tabular-nums; color: var(--ink); }
.mc-count.mc-failed strong { color: var(--error); }
.mc-count.mc-overdue strong { color: var(--chart-stalled); }
.mc-count.mc-running strong { color: var(--info); }
.mc-grid { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 230px), 1fr)); }
.mc-tile { min-width: 0; }
.mc-tile-btn { all: unset; box-sizing: border-box; display: grid; gap: 4px; width: 100%; height: 100%; cursor: pointer;
  padding: 12px 14px 12px 16px; background: var(--surface); border: 1px solid var(--border);
  border-left: 5px solid var(--mc-edge, var(--border)); border-radius: var(--radius-sm); color: var(--ink); }
.mc-tile-btn:hover { border-color: var(--field-border-hover); border-left-color: var(--mc-edge, var(--border)); }
.mc-tile-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.mc-tile-state { font-size: 11px; text-transform: uppercase; letter-spacing: .07em; font-weight: 700; color: var(--mc-edge, var(--muted)); }
.mc-tile-name { font-weight: 600; font-size: 14px; overflow-wrap: anywhere; line-height: 1.3; }
.mc-tile-sub { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.mc-strip { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 4px; }
.mc-dot { display: inline-block; width: 9px; height: 9px; border-radius: 2px; background: var(--mc-edge, var(--chart-idle)); flex: none; }
.mc-failed { --mc-edge: var(--error); }
.mc-overdue { --mc-edge: var(--chart-stalled); }
.mc-running { --mc-edge: var(--info); }
.mc-warning { --mc-edge: var(--warning); }
.mc-healthy { --mc-edge: var(--ok); }
.mc-quiet { --mc-edge: var(--chart-idle); }
.mc-tile.mc-failed .mc-tile-btn { background: linear-gradient(90deg, var(--error-bg), var(--surface) 70%); }
.mc-tile.mc-overdue .mc-tile-btn { background: linear-gradient(90deg, var(--warning-bg), var(--surface) 70%); }
.mc-tile.mc-running .mc-dot:last-child { animation: mc-blink 1.2s ease-in-out infinite; }
@keyframes mc-blink { 50% { opacity: .25; } }
.mc-changed .mc-tile-btn { animation: mc-pulse 1.6s ease-out 2; }
@keyframes mc-pulse { 0% { box-shadow: 0 0 0 0 var(--mc-edge); } 100% { box-shadow: 0 0 0 10px transparent; } }
.mc-detail { position: fixed; inset: 0; background: rgba(8, 12, 16, .35); z-index: 60; display: flex; justify-content: flex-end; }
.mc-detail[hidden] { display: none; }
.mc-detail-box { position: relative; width: min(420px, 100%); height: 100%; overflow-y: auto; background: var(--surface);
  border-left: 1px solid var(--border); padding: 22px 20px 28px; box-shadow: var(--shadow); box-sizing: border-box; }
.mc-close { position: absolute; top: 10px; right: 10px; background: transparent; border: 0; font-size: 26px; line-height: 1;
  color: var(--muted); cursor: pointer; padding: 4px 10px; }
.mc-d-state { margin: 0; font-size: 12px; text-transform: uppercase; letter-spacing: .07em; font-weight: 700; color: var(--mc-edge, var(--muted)); }
.mc-detail-box h2 { margin: 4px 36px 4px 0; overflow-wrap: anywhere; }
.mc-d-message { background: var(--surface-2); border-radius: var(--radius-sm); padding: 10px 12px; font-size: 13px; overflow-wrap: anywhere; }
.mc-d-message:empty { display: none; }
.mc-detail-box h3 { font-size: 13px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 18px 0 8px; }
.mc-runs { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.mc-run { display: grid; grid-template-columns: 12px 9.5em minmax(0, 1fr); gap: 8px; align-items: center; font-size: 13px; }
.mc-run-when { font-family: var(--mono); font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.mc-d-actions { margin-top: 18px; display: grid; gap: 8px; }
.mc-d-actions button[hidden] { display: none; }
@media (max-width: 640px) {
  /* Once the board is up, the form shrinks to its controls so the board is
     on the first screen rather than below a paragraph of explanation. */
  .mc-form.mc-loaded { padding: 14px; }
  .mc-form.mc-loaded .mc-explain { display: none; }
  .mc-form.mc-loaded .dash-actions button { flex: 0 1 auto; }
  .mc-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px; }
  .mc-tile-btn { padding: 10px 10px 10px 12px; }
  .mc-detail { align-items: flex-end; }
  .mc-detail-box { width: 100%; height: auto; max-height: 85vh; border-left: 0; border-top: 1px solid var(--border);
    border-radius: 14px 14px 0 0; }
  .mc-d-actions button { width: 100%; }
}
@media (max-width: 380px) { .mc-grid { grid-template-columns: minmax(0, 1fr); } }

/* Editing a tenant connection in place, under its own row. */
.edit-row > td { background: var(--surface-2); }
.edit-form { max-width: none; margin-top: 0; display: grid; gap: 12px 16px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); padding: 6px 0; }
.edit-form label { display: flex; flex-direction: column; gap: 6px; font-size: 13px; color: var(--muted); min-width: 0; }
.edit-form input, .edit-form select { width: 100%; box-sizing: border-box; }
.edit-actions { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.probe-detail { display: block; margin: 2px 0 0 1.4em; color: var(--muted); font-size: 12.5px; overflow-wrap: anywhere; }
@media (max-width: 640px) {
  .edit-actions button { flex: 1 1 auto; }
}

/* ---------------- Event Search ---------------- */
.es-page .es-files { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px 14px; margin: 4px 0 12px; display: grid; gap: 8px; }
.es-page .es-files legend { font-size: 13px; color: var(--muted); padding: 0 4px; }
.es-page .es-files label { display: flex; gap: 10px; align-items: flex-start; font-size: 14px; color: var(--ink-2); }
.es-page .es-files input { margin-top: 3px; }
.es-check { display: inline-flex; gap: 6px; align-items: center; font-size: 14px; color: var(--ink-2); align-self: flex-end; padding-bottom: 9px; }
.es-list-actions { align-self: flex-end; }
.es-search-row { align-items: flex-end; }
.es-progress p { display: flex; gap: 8px; align-items: center; margin: 0 0 8px; }
.es-bar { height: 6px; background: var(--surface-2); border-radius: 3px; overflow: hidden; }
.es-bar i { display: block; height: 100%; width: 0; background: var(--accent); transition: width .3s; }
.es-summary h2 { margin: 0 0 6px; font-size: 1.1rem; }
.es-summary p { margin: 4px 0; }
.es-summary.es-sent { border-left: 5px solid var(--ok); }
.es-summary.es-seen_not_sent, .es-summary.es-inconclusive { border-left: 5px solid var(--warning); }
.es-summary.es-not_found { border-left: 5px solid var(--error); }
.es-filters { display: flex; flex-wrap: wrap; gap: 6px; margin: 14px 0 10px; }
.es-chip { font: inherit; font-size: 13.5px; padding: 6px 12px; border-radius: 999px; border: 1px solid var(--border); background: var(--surface); color: var(--ink-2); cursor: pointer; }
.es-chip.on { background: var(--accent-quiet); color: var(--accent-ink); border-color: var(--accent); font-weight: 600; }
.es-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.es-count { display: inline-block; min-width: 1.6em; padding: 0 6px; margin-left: 2px; border-radius: 999px; background: var(--surface-2); font-variant-numeric: tabular-nums; text-align: center; }
.es-table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.es-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.es-table th, .es-table td { text-align: left; vertical-align: top; padding: 10px 12px; border-bottom: 1px solid var(--line-soft); }
.es-table th { font-size: 12px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.es-when { white-space: nowrap; font-variant-numeric: tabular-nums; }
.es-row.es-found .es-yes { color: var(--ok); font-weight: 700; }
.es-row.es-unreadable .es-yes { color: var(--warning); font-weight: 600; }
.es-file { overflow-wrap: anywhere; display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: baseline; }
.es-file-unread { color: var(--muted); }
.es-tag { font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; padding: 1px 7px; border-radius: 999px; background: var(--ok-bg); color: var(--ok); border: 1px solid var(--ok-line); }
.es-found { min-width: 18em; }
.es-match-head { display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: baseline; overflow-wrap: anywhere; }
.es-lines { font-family: var(--mono); font-size: 12.5px; margin: 6px 0 10px; display: grid; gap: 3px; overflow-wrap: anywhere; }
.es-why { border-left: 3px solid var(--warning); padding: 4px 10px; margin: 6px 0; color: var(--ink-2); font-size: 13px; overflow-wrap: anywhere; }
@media (max-width: 640px) {
  .es-table thead { display: none; }
  .es-table, .es-table tbody, .es-table tr, .es-table td { display: block; width: 100%; box-sizing: border-box; }
  .es-table tr { border-bottom: 1px solid var(--border); padding: 6px 0; }
  .es-table td { border: 0; padding: 4px 12px; }
  .es-found { min-width: 0; }
  .es-check { padding-bottom: 0; }
}
/* Long file names and single-line XML must wrap, not run off a phone:
   flex items will not shrink below their longest word without this. */
.es-file > span, .es-match-head > strong, .es-match-head > span { min-width: 0; overflow-wrap: anywhere; }
.es-lines > div, .es-why { overflow-wrap: anywhere; word-break: break-word; }
.es-table td { min-width: 0; }
/* tables.js wraps every table in .table-scroll and sizes it to its widest
   content -- right for data grids, wrong here, where a file name or a line of
   one-line XML is the widest thing and must wrap. Same opt-out as watch-table. */
.table-scroll > table.es-table { min-width: 0; width: 100%; }

/* ---------------- Mass Update Periods ---------------- */
.mp-load { align-self: flex-end; }
.mp-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-end; gap: 10px 16px; margin-bottom: 10px; }
.mp-head h2 { margin: 0; }
.mp-filters { display: flex; flex-wrap: wrap; gap: 10px; }
.mp-table-wrap { border: 1px solid var(--border); border-radius: var(--radius-sm); max-height: 60vh; overflow: auto; }
.table-scroll > table.mp-table { min-width: 0; width: 100%; }
.mp-table, .mp-log { width: 100%; border-collapse: collapse; font-size: 14px; }
.mp-table th, .mp-table td, .mp-log th, .mp-log td { text-align: left; padding: 8px 12px; border-bottom: 1px solid var(--line-soft); }
.mp-table thead th { position: sticky; top: 0; background: var(--surface-2); z-index: 1; font-size: 12px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.mp-table tbody tr { cursor: pointer; }
.mp-table tbody tr:hover { background: var(--surface-2); }
.mp-table tbody tr.on { background: var(--accent-quiet); }
.mp-check { width: 1%; white-space: nowrap; }
.mp-check input { width: 18px; height: 18px; vertical-align: middle; cursor: pointer; }
.mp-all { display: inline-flex; gap: 6px; align-items: center; cursor: pointer; text-transform: none; }
.mp-date { white-space: nowrap; font-variant-numeric: tabular-nums; }
.mp-name { overflow-wrap: anywhere; }
.mp-pill { display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: 12px; font-weight: 600; background: var(--surface-2); color: var(--ink-2); border: 1px solid var(--border); white-space: nowrap; }
.mp-complete { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-line); }
.mp-in-progress { background: var(--info-bg); color: var(--info); border-color: var(--info-line); }
.mp-not-yet-started { background: var(--surface-2); color: var(--muted); }
.mp-bar { position: sticky; bottom: 0; display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: center; padding: 12px 0 4px; background: var(--surface); }
.mp-bar #mp-count { font-weight: 600; flex: 1 1 12rem; }
.mp-to { display: inline-flex; gap: 8px; align-items: center; font-size: 14px; color: var(--ink-2); }
.mp-results { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; font-size: 14px; }
.mp-results .mp-done strong, .mp-ok { color: var(--ok); }
.mp-results .mp-fail strong, .mp-bad { color: var(--error); }
.mp-results .mp-skip { color: var(--muted); }
.mp-results li span { overflow-wrap: anywhere; }
.mp-confirm { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--ink); max-width: min(520px, calc(100vw - 32px)); padding: 20px 22px; }
.mp-confirm::backdrop { background: rgba(8, 12, 16, .45); }
.mp-confirm h2 { margin-top: 0; }
.mp-confirm-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 14px; }
@media (max-width: 640px) {
  .mp-table thead tr { display: flex; }
  .mp-table thead th:not(.mp-check) { display: none; }
  .mp-table thead th.mp-check { width: 100%; }
  .mp-table tbody tr { display: grid; grid-template-columns: auto 1fr auto; gap: 2px 10px; padding: 8px 10px; border-bottom: 1px solid var(--line-soft); }
  .mp-table tbody td { padding: 0; border: 0; }
  .mp-table tbody td.mp-check { grid-row: 1 / span 2; align-self: center; }
  .mp-table tbody td.mp-name { grid-column: 2; font-weight: 600; }
  .mp-table tbody td:last-child { grid-column: 3; grid-row: 1; }
  .mp-table tbody td.mp-date { grid-column: 2; font-size: 12.5px; color: var(--muted); display: inline; }
  .mp-table tbody td.mp-date::before { color: var(--muted); }
  .mp-table tbody td.mp-date:nth-child(3)::before { content: "Start "; }
  .mp-table tbody td.mp-date:nth-child(4)::before { content: "End "; }
  .mp-table tbody td.mp-date:nth-child(5)::before { content: "Pay "; }
  .mp-bar button { flex: 1 1 100%; }
}
.mp-to select { font: inherit; font-size: 14px; padding: 7px 10px; border: 1px solid var(--field-border); border-radius: var(--radius-sm); background: var(--surface); color: var(--ink); }

/* A checkbox with its label beside it, in a stacked form. */
.stacked-form .check-row { flex-direction: row; align-items: center; gap: 10px; font-size: 15px; color: var(--ink); }
.stacked-form .check-row input { width: 18px; height: 18px; }

/* ---- task board: the full window ----
   The board is the one page here that is wider than it is long to read:
   with a lot of cards, four columns held to the reading measure are four
   narrow strips. So the board takes the window and the columns share it;
   the prose above it and the forms below keep their usual measure. */
body.board-wide main { max-width: none; }
body.board-wide main > h1,
body.board-wide main > p,
body.board-wide main > form,
body.board-wide main > .recon-counts,
body.board-wide main > .panel { max-width: 1160px; }
body.board-wide .board { grid-template-columns: repeat(5, minmax(220px, 1fr)); gap: 14px; }

/* ---- task: followers and time ---- */
.td-followers, .td-time { list-style: none; margin: 0 0 8px; padding: 0; }
.td-followers li, .td-time li {
  display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: baseline;
  padding: 5px 0; border-bottom: 1px solid var(--line-soft);
}
.td-followers .inline-form, .td-time .inline-form { margin: 0 0 0 auto; }
.td-follower { overflow-wrap: anywhere; min-width: 0; }
.td-follow { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 8px 0 4px; }
.td-follow-add { display: flex; gap: 8px; flex: 1 1 320px; min-width: 0; }
.td-follow-add input[type="email"] {
  flex: 1 1 auto; min-width: 0; min-height: 36px;
  padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px;
  font: inherit; font-size: 14px; color: var(--ink); background: var(--surface);
}
.td-time-total {
  margin-left: 6px; font-variant-numeric: tabular-nums;
  color: var(--accent); text-transform: none; letter-spacing: 0;
}
.td-time-amount { font-weight: 700; font-variant-numeric: tabular-nums; min-width: 4.5em; }
.td-time-note { flex: 1 1 12em; min-width: 0; overflow-wrap: anywhere; }
.td-time-add { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; margin-top: 8px; }
.td-time-add .td-time-qty { flex: 0 0 7em; }
.td-time-add .td-time-unit { flex: 0 0 8em; }
.td-time-add .grow { flex: 1 1 14em; min-width: 0; }
.card-time { color: var(--accent); font-variant-numeric: tabular-nums; }

@container (max-width: 640px) {
  .td-follow-add { flex-basis: 100%; }
  .td-time-add .td-time-qty, .td-time-add .td-time-unit { flex: 1 1 40%; }
  .td-time-add .grow { flex-basis: 100%; }
}
/* The wide rule above outranks the plain stacking rule, so the phone gets
   its single column back at the same strength. */
@container (max-width: 860px) {
  body.board-wide .board { grid-template-columns: 1fr; }
}

/* A screenshot that did not upload says so where it was pasted. */
.td-hint-bad { color: var(--error); }

/* ---- tickets by email ---- */
.card-flag { margin: 0 0 6px; font-size: 12px; overflow-wrap: anywhere; }
.td-from { overflow-wrap: anywhere; }
.td-move { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-end; margin: 0 0 12px; }
.td-move .oe2-field { flex: 0 1 260px; min-width: 0; }

/* The task opens in a <dialog>, which browsers paint in their OWN default
   text colour (black) whatever the page's theme -- which put black text on
   the dark theme's dark ground. Taken from the theme like everything else. */
.task-dialog, .task-dialog-card { color: var(--ink); }
.task-dialog select, .td-sort-pick { color: var(--ink); background: var(--surface); }

/* ---- Task Board: the hours report ---- */
.board-head { display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: center; justify-content: space-between; }
.board-head h1 { margin: 0; }
.hr-filters .hr-notes { align-self: center; }
.hr-export { margin-left: auto; }
/* Its own scroll box: a column per person can outgrow a phone, and the
   page itself must not scroll sideways. */
.hr-wrap { overflow-x: auto; margin: 10px 0 24px; border: 1px solid var(--border);
           border-radius: var(--radius); background: var(--surface); }
.table-scroll > table.hr-grid { min-width: 0; width: 100%; }
.hr-grid { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.hr-grid th, .hr-grid td { padding: 8px 10px; border-bottom: 1px solid var(--line-soft); text-align: left; vertical-align: top; }
.hr-grid thead th { background: var(--surface-2); font-size: 12px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); white-space: nowrap; }
.hr-grid .hr-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.hr-grid .hr-total { font-weight: 700; background: color-mix(in srgb, var(--accent) 7%, var(--surface)); }
.hr-grid tfoot th { background: var(--surface-2); color: var(--ink); font-weight: 700; border-top: 2px solid var(--border); }
.hr-task { min-width: 16em; overflow-wrap: anywhere; }
.hr-task .meta { display: block; font-size: 11.5px; }
.hr-id { color: var(--muted); font-variant-numeric: tabular-nums; }
.hr-none { color: var(--line-soft); }
.hr-notes-row td { background: var(--surface-2); padding-top: 4px; }
.hr-note-list { list-style: none; margin: 0; padding: 0 0 0 4px; font-size: 13px; }
.hr-note-list li { padding: 3px 0; overflow-wrap: anywhere; }
.hr-note-h { display: inline-block; min-width: 4em; font-weight: 700; font-variant-numeric: tabular-nums; }
@container (max-width: 640px) {
  .hr-export { margin-left: 0; }
  .hr-task { min-width: 11em; }
}

/* ---- internal notes, reply options, my work, search ---- */
.td-internal {
  border-style: dashed; border-left-style: solid;
  border-left-color: var(--warning);
  background: color-mix(in srgb, var(--warning) 8%, var(--surface));
}
.td-internal-tag {
  display: inline-block; margin-left: 6px; padding: 0 7px; border-radius: 999px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .03em;
  color: var(--warning); background: var(--warning-bg); border: 1px solid var(--warning-line);
}
.td-say-opts { display: flex; flex-wrap: wrap; gap: 10px 14px; align-items: flex-end; margin: 8px 0; }
.td-say-opts .oe2-field { flex: 0 1 240px; min-width: 0; }
.td-internal-pick { align-self: center; }
.td-billable { align-self: center; }
.td-merge { margin: 0 0 12px; }
.td-merge summary { cursor: pointer; font-size: 12.5px; color: var(--muted); padding: 4px 0; }
.board-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: flex-end; flex: 1 1 420px; }
.board-search { display: flex; gap: 6px; align-items: center; flex: 1 1 260px; min-width: 0; }
.board-search input[type="search"] {
  flex: 1 1 auto; min-width: 0; min-height: 36px; padding: 6px 10px;
  border: 1px solid var(--border); border-radius: 6px; font: inherit; font-size: 14px;
  color: var(--ink); background: var(--surface);
}
.board-search-big { margin: 12px 0 6px; max-width: 720px; }
.board-search-big input[type="search"] { min-height: 42px; font-size: 15px; }
.mw-list { list-style: none; margin: 14px 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.mw-item {
  display: flex; flex-wrap: wrap; gap: 4px 14px; align-items: baseline; justify-content: space-between;
  padding: 10px 14px; border: 1px solid var(--border); border-left: 4px solid var(--border);
  border-radius: var(--radius-sm); background: var(--surface);
}
.mw-reply { border-left-color: var(--error); }
.mw-overdue { border-left-color: var(--warning); }
.mw-title { font-weight: 600; overflow-wrap: anywhere; }
.mw-meta { display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline; font-size: 12.5px; }

/* ---- custom reports ---- */
.rb-form, .rb-save { max-width: 1160px; }
.rb-row { display: flex; flex-wrap: wrap; gap: 10px 14px; align-items: flex-end; margin: 6px 0 10px; }
.rb-row .oe2-field { flex: 0 1 220px; min-width: 0; }
.rb-source { flex: 0 1 340px !important; }
.rb-hint { margin: 0 0 8px; }
.rb-set { border: 1px solid var(--line-soft); border-radius: var(--radius-sm); padding: 8px 12px 10px; margin: 0 0 12px; }
.rb-set legend { font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); padding: 0 6px; }
.rb-checks { display: flex; flex-wrap: wrap; gap: 4px 16px; }
.rb-checks .check-row { margin: 0; }
.rb-pick { flex: 1 1 300px; min-width: 0; }
.rb-pick-h { display: block; font-size: 13px; color: var(--muted); margin: 0 0 4px; }
.rb-summary { align-self: center; }
.rb-actions { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; }
.rb-save { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; }
.rb-save .oe2-field { flex: 1 1 280px; min-width: 0; }
.rb-saved-list { list-style: none; margin: 0; padding: 0; }
.rb-saved-list li { display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: baseline; padding: 6px 0; border-bottom: 1px solid var(--line-soft); }
.rb-saved-list li.is-current > a { font-weight: 700; }
.rb-saved-acts { margin-left: auto; display: flex; gap: 12px; align-items: baseline; }
.rb-saved-acts .inline-form { margin: 0; }
.table-scroll > table.rb-table { min-width: 0; width: 100%; }
.rb-table td.rb-long { min-width: 18em; max-width: 40em; white-space: pre-wrap; overflow-wrap: anywhere; }
.rb-group-head th { background: color-mix(in srgb, var(--accent) 10%, var(--surface)); color: var(--ink); text-transform: none; letter-spacing: 0; font-size: 13.5px; }
.rb-subtotal td { font-weight: 700; background: var(--surface-2); }

/* ---- Admin: ticket data ---- */
.table-scroll > table.td-data { min-width: 0; width: 100%; }
.small-btn { padding: 4px 12px; min-height: 32px; font-size: 13px; }
.pg-form { max-width: 720px; }
.pg-confirm { max-width: 720px; border-color: var(--error-line); background: color-mix(in srgb, var(--error) 5%, var(--surface)); }
.card-by { overflow-wrap: anywhere; }

/* ---- a ticket's own link (no account) ---- */
.tg { max-width: 760px; margin: 0 auto; padding: 24px 16px 56px; }
.tg-brand { font-weight: 700; letter-spacing: .02em; color: var(--accent); margin: 0 0 16px; }
.tg-head { display: block; margin: 0 0 14px; }
.tg-head h1 { margin: 2px 0 6px; overflow-wrap: anywhere; }
.tg-id { margin: 0; font-family: var(--mono); font-size: 12.5px; color: var(--muted); }
.tg-form { max-width: none; }
.tg-form textarea { width: 100%; }
.tg-stop { margin-top: 18px; }

/* ---- view-only files ---- */
.fv { max-width: 1100px; margin: 0 auto; padding: 20px 16px 48px; }
.fv-name { overflow-wrap: anywhere; margin-bottom: 4px; }
.fv-frame { margin-top: 14px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px; }
.fv-img { display: block; max-width: 100%; height: auto; margin: 0 auto; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
.fv-text { margin-top: 14px; max-height: 70vh; overflow: auto; background: var(--surface); border: 1px solid var(--border);
           border-radius: var(--radius); padding: 12px; font-size: 13px; white-space: pre; }

/* ---- my notifications ---- */
.nt-sub { margin-left: 26px; }
.nt-rules { list-style: none; margin: 0; padding: 0; }
.nt-rules li { display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: baseline; justify-content: space-between;
               padding: 8px 0; border-bottom: 1px solid var(--line-soft); }
.nt-rules li.is-off { opacity: .65; }
.nt-rule-main { min-width: 0; flex: 1 1 320px; overflow-wrap: anywhere; }
.nt-rule-main .meta { display: block; }
.nt-rule-acts { display: flex; gap: 12px; }
.nt-rule-acts .inline-form { margin: 0; }
.nt-form { max-width: 760px; }

.proj-closed { margin: 6px 0 12px; }
.proj-closed summary { cursor: pointer; font-size: 13px; color: var(--muted); padding: 4px 0; }

/* =================================================== the task panel ===
   A task is a side panel over the board (a <dialog>, styled as a sheet), or
   the same section on its own page: the conversation in the middle, its
   fields in a column beside it. Below 900px the column comes first and
   everything stacks. */
.task-dialog { padding: 0; }
.task-dialog[open] { display: block; }
.task-dialog-card {
  position: fixed; top: 0; right: 0; bottom: 0; left: auto;
  width: min(1180px, 100vw); max-width: none; max-height: none; height: 100%;
  border-radius: 0; border: 0; border-left: 1px solid var(--border);
  padding: 0; overflow: hidden; display: flex; flex-direction: column;
}
.task-dialog-close {
  position: absolute; top: 10px; right: 14px; float: none; z-index: 3;
  min-height: 34px; padding: 0 12px;
}
#task-dialog-body { flex: 1; min-height: 0; display: flex; }
#task-dialog-body > .tp { flex: 1; min-height: 0; }
@media (prefers-reduced-motion: no-preference) {
  .task-dialog[open] .task-dialog-card { animation: tp-in .18s ease-out; }
  @keyframes tp-in { from { transform: translateX(24px); opacity: .6; } }
}

.tp { display: flex; flex-direction: column; min-width: 0; background: var(--surface); color: var(--ink); }
.tp-head { display: flex; flex-wrap: wrap; gap: 6px 12px; align-items: center; padding: 12px 20px; padding-right: 110px;
           border-bottom: 1px solid var(--border); }
.tp-crumb { font-size: 13px; color: var(--muted); }
.tp-tag { font-size: 11px; font-weight: 600; padding: 1px 8px; border-radius: 999px; background: var(--accent-quiet); color: var(--accent); }
.tp-head-acts { display: flex; gap: 6px; margin-left: auto; }
.tp-icon { display: inline-grid; place-items: center; min-width: 32px; height: 32px; padding: 0 8px; border-radius: 8px;
           border: 1px solid var(--border); background: var(--surface); color: var(--ink-2); text-decoration: none; cursor: pointer; font-size: 14px; }
.tp-icon:hover { border-color: var(--accent); color: var(--accent); }
.tp-body { flex: 1; min-height: 0; display: grid; grid-template-columns: minmax(0, 1fr) 320px; }
.tp-main { min-width: 0; overflow-y: auto; display: flex; flex-direction: column; border-right: 1px solid var(--border); }
.tp-side { min-width: 0; overflow-y: auto; background: var(--surface-2); padding: 14px 18px 28px; display: flex; flex-direction: column; gap: 6px; }
.tp-side h3, .tp-block-head h3 { margin: 4px 0 6px; font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }

/* title and description, edited in place */
.tp-intro { padding: 16px 24px 4px; display: flex; flex-direction: column; gap: 4px; }
.tp-title { font: 700 21px/1.3 var(--sans); color: var(--ink); border: 1px solid transparent; border-radius: 8px; padding: 4px 6px; margin-left: -7px; background: transparent; width: 100%; }
.tp-title:hover, .tp-detail:hover { border-color: var(--border); }
.tp-title:focus, .tp-detail:focus { border-color: var(--accent); outline: none; background: var(--surface); }
.tp-sub { margin: 0; font-size: 12.5px; color: var(--muted); }
.tp-detail { font: 14.5px/1.55 var(--sans); color: var(--ink-2); border: 1px solid transparent; border-radius: 8px; padding: 6px; margin-left: -7px;
             background: transparent; width: 100%; resize: vertical; min-height: 2.5em; }
.is-saved { box-shadow: 0 0 0 3px var(--accent-quiet); }

/* checklist and links */
.tp-block { padding: 6px 24px 4px; }
.tp-block-head { display: flex; align-items: baseline; gap: 10px; }
.tp-progress { height: 6px; border-radius: 999px; background: var(--line-soft); overflow: hidden; margin: 0 0 6px; }
.tp-progress i { display: block; height: 100%; background: var(--accent); border-radius: 999px; }
.tp-progress-label { font-size: 12px; color: var(--muted); }
.tp-check, .tp-links { list-style: none; margin: 0 0 6px; padding: 0; }
.tp-check li, .tp-links li { display: flex; gap: 8px; align-items: center; padding: 3px 0; min-width: 0; }
.tp-check li.is-done .tp-check-text { color: var(--muted); text-decoration: line-through; }
.tp-check-text { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.tp-box { width: 20px; height: 20px; border-radius: 6px; border: 1.5px solid var(--field-border); background: var(--surface);
          display: grid; place-items: center; padding: 0; cursor: pointer; color: var(--on-accent); font-size: 12px; line-height: 1; }
.tp-box[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); }
.tp-inline { display: inline; margin: 0; }
.tp-hover { opacity: .35; }
li:hover > .tp-hover, .tp-hover:focus-within { opacity: 1; }
.tp-add input { width: 100%; border: 1px dashed var(--border); border-radius: 8px; background: transparent; padding: 6px 10px; font: inherit; font-size: 13.5px; color: var(--ink); }
.tp-add input:focus { border-style: solid; border-color: var(--accent); outline: none; background: var(--surface); }
.tp-link-kind { font-size: 12px; font-weight: 600; color: var(--muted); min-width: 6.5em; }
.tp-link-kind.is-blocking { color: var(--error); }
.tp-links a { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tp-link-add { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.tp-link-add select, .tp-link-add input { height: 32px; border: 1px solid var(--border); border-radius: 8px; padding: 0 8px; background: var(--surface); color: var(--ink); font: inherit; font-size: 13px; }
.tp-link-add input { width: 7em; }

/* one timeline */
.tp-tabs { display: flex; gap: 2px; padding: 10px 24px 0; border-bottom: 1px solid var(--border); margin-top: 8px; position: sticky; top: 0; background: var(--surface); z-index: 1; }
.tp-tab { border: 0; background: none; padding: 8px 12px 10px; cursor: pointer; color: var(--muted); font: 600 13.5px var(--sans); border-bottom: 2px solid transparent; margin-bottom: -1px; }
.tp-tab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--accent); }
.tp-tab .mono { font-size: 11px; font-weight: 500; margin-left: 3px; }
.tp-timeline { list-style: none; margin: 0; padding: 14px 24px 18px; display: flex; flex-direction: column; gap: 12px; flex: 1; }
.tp-ev { display: grid; grid-template-columns: 30px minmax(0, 1fr); gap: 10px; }
.tp-av { width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; font: 700 10.5px var(--sans);
         color: #fff; background: var(--tone, var(--who-0)); }
.tp-bubble { border: 1px solid color-mix(in srgb, var(--tone) 30%, var(--border)); border-radius: 10px; padding: 9px 14px;
             background: color-mix(in srgb, var(--tone) 6%, var(--surface)); min-width: 0; }
.tp-bubble.td-internal { border-style: dashed; background: color-mix(in srgb, var(--warning) 8%, var(--surface)); }
.tp-ev-foot { display: flex; gap: 12px; align-items: center; margin-top: 6px; }
.tp-react { border: 1px solid var(--border); background: var(--surface); border-radius: 999px; padding: 0 10px; height: 24px; font-size: 12px; color: var(--muted); cursor: pointer; }
.tp-react[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); background: var(--accent-quiet); }
.tp-sys { display: grid; grid-template-columns: 30px minmax(0, 1fr); gap: 10px; align-items: baseline; font-size: 13px; color: var(--muted); overflow-wrap: anywhere; }
.tp-sys b { color: var(--ink-2); font-weight: 600; }
.tp-dot { display: grid; place-items: center; }
.tp-dot i { width: 8px; height: 8px; border-radius: 50%; background: var(--border); }
.tp-empty { list-style: none; }
.mention { color: var(--accent); background: var(--accent-quiet); border-radius: 4px; padding: 0 3px; font-weight: 600; }

/* the comment box, held at the foot of the conversation */
.tp-composer { position: sticky; bottom: 0; background: var(--surface); border-top: 1px solid var(--border); padding: 10px 24px 14px;
               margin: 0; display: flex; flex-direction: column; gap: 6px; z-index: 1; }
.tp-composer .rt-box { min-height: 64px; max-height: 40vh; overflow-y: auto; }
.tp-composer.is-internal .rt-box { background: color-mix(in srgb, var(--warning) 8%, var(--surface)); border-style: dashed; }
.tp-say-row { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; }
.tp-say-row select { height: 34px; border: 1px solid var(--border); border-radius: 8px; padding: 0 8px; background: var(--surface); color: var(--ink); font: inherit; font-size: 13px; max-width: 100%; }
.tp-then { display: inline-flex; gap: 6px; align-items: center; font-size: 13px; color: var(--muted); }
.tp-grow { flex: 1; }
.tp-say-foot { margin: 0; }
.tp-mention-menu { border: 1px solid var(--border); border-radius: 8px; background: var(--surface); box-shadow: var(--shadow); max-height: 200px; overflow-y: auto; }
.tp-mention-menu button { display: block; width: 100%; text-align: left; border: 0; background: none; padding: 7px 12px; font: inherit; font-size: 13.5px; color: var(--ink); cursor: pointer; }
.tp-mention-menu button:hover, .tp-mention-menu button.is-on { background: var(--accent-quiet); }

/* the side column */
.tp-fields { display: flex; flex-direction: column; gap: 2px; margin: 0; }
.tp-field { display: grid; grid-template-columns: 82px minmax(0, 1fr) auto; gap: 6px; align-items: center; padding: 2px 0; }
.tp-field > span:first-child { font-size: 12.5px; color: var(--muted); }
.tp-field select, .tp-field input { width: 100%; min-width: 0; height: 32px; border: 1px solid transparent; border-radius: 7px;
                                    background: transparent; padding: 0 6px; font: inherit; font-size: 13.5px; color: var(--ink); cursor: pointer; }
.tp-field select:hover, .tp-field input:hover { border-color: var(--border); background: var(--surface); }
.tp-field select:focus, .tp-field input:focus { border-color: var(--accent); background: var(--surface); outline: none; }
.tp-value { font-size: 13.5px; padding-left: 7px; overflow-wrap: anywhere; }
.tp-saved { font-style: normal; font-size: 11px; font-weight: 600; color: var(--accent); min-width: 3em; }
.tp-status { font-weight: 600; }
.tp-status.s-blocked { color: var(--error); }
.tp-status.s-waiting { color: var(--warning); }
.tp-status.s-done { color: var(--ok); }
select.p-high { color: var(--error); font-weight: 600; }
.tp-side-block { border-top: 1px solid var(--border); padding-top: 10px; margin-top: 6px; display: flex; flex-direction: column; gap: 6px; }
.tp-more { border-top: 1px solid var(--border); padding-top: 8px; margin-top: 6px; }
.tp-more summary { cursor: pointer; font-size: 13px; color: var(--muted); }
.tp-more .td-move, .tp-more .td-time-add { margin-top: 8px; }
.tp-note { margin: 0; font-size: 12px; }
.tp-people { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.tp-person { display: flex; gap: 8px; align-items: center; min-width: 0; font-size: 13px; }
.tp-person-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tp-av-s { width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center; font: 700 9.5px var(--sans); background: var(--accent-quiet); color: var(--accent); flex: none; }
.tp-time-total { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.tp-time-total b { font-size: 19px; }
.tp-quick { display: flex; gap: 6px; flex-wrap: wrap; }
.tp-live-pill { margin-left: 8px; height: 28px; padding: 0 12px; border: 1px solid var(--accent); border-radius: 999px; background: var(--accent-quiet); color: var(--accent-ink); font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer; }
.tp-live-pill:hover { background: var(--accent); color: var(--on-accent); }
.tp-quick-form { display: flex; flex-direction: column; gap: 6px; }
.tp-quick-form input[type="text"] { width: 100%; height: 32px; border: 1px solid var(--field-border); border-radius: 8px; padding: 0 8px; background: var(--surface); color: var(--ink); font: inherit; font-size: 13px; }
.tp-quick-form input[type="text"]:focus { border-color: var(--accent); outline: none; }
.tp-form-error { margin: 0; color: var(--error); font-size: 12.5px; }
.tp-chip { border: 1px solid var(--border); background: var(--surface); border-radius: 7px; height: 30px; padding: 0 10px; cursor: pointer; font-size: 12.5px; color: var(--ink); }
.tp-chip:hover { border-color: var(--accent); color: var(--accent); }
.tp-files { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.tp-file { position: relative; min-width: 0; }
.tp-thumb { display: grid; place-items: center; aspect-ratio: 4 / 3; max-width: 100%; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); overflow: hidden; text-decoration: none; }
.tp-thumb img { width: 100%; height: 100%; object-fit: cover; }
.tp-thumb-doc span { font-size: 12px; font-weight: 600; color: var(--muted); }
.tp-file-name { display: block; font-size: 11px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 3px; }
.tp-file-del { position: absolute; top: 2px; right: 4px; background: var(--surface); border-radius: 50%; opacity: 0; }
.tp-file:hover .tp-file-del, .tp-file-del:focus { opacity: 1; }
.tp-upload { position: relative; margin: 0; }
.tp-drop { display: block; border: 1px dashed var(--border); border-radius: 8px; padding: 8px 10px; text-align: center; font-size: 13px; color: var(--muted); cursor: pointer; }
.tp-drop:hover, .tp-drop.is-over { border-color: var(--accent); color: var(--accent); background: var(--accent-quiet); }
.tp-drop input { position: absolute; width: 1px; height: 1px; opacity: 0; }

/* a picture, large */
.tp-lightbox { position: fixed; inset: 0; z-index: 50; background: rgba(8, 14, 20, .86); display: grid; place-items: center; padding: 24px; }
.tp-lightbox img { max-width: 100%; max-height: 82vh; border-radius: 6px; box-shadow: var(--shadow); background: #fff; }
.tp-lightbox p { color: #fff; margin: 10px 0 0; text-align: center; font-size: 13px; overflow-wrap: anywhere; }
.tp-lightbox button { position: absolute; top: 14px; right: 16px; }

/* the task on its own page */
main > .tp { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
main > .tp .tp-main, main > .tp .tp-side { overflow: visible; }

@media (max-width: 900px) {
  .tp-body { grid-template-columns: 1fr; }
  .tp-side { order: -1; border-bottom: 1px solid var(--border); }
  .tp-main { border-right: 0; }
  .tp-body, .tp-main, .tp-side { overflow: visible; }
  #task-dialog-body > .tp { overflow-y: auto; }
  .tp-intro, .tp-block, .tp-tabs, .tp-timeline, .tp-composer { padding-left: 16px; padding-right: 16px; }
  .tp-head { padding-right: 90px; }
  .tp-files { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* The first voice's colour, inherited; td-tone-N on the bubble picks another. */
.tp-ev { --tone: var(--who-0); }

/* ---- the bell ---- */
.nav-bell { position: relative; display: inline-grid; place-items: center; width: 36px; height: 36px; border-radius: 8px;
            color: var(--muted); text-decoration: none; }
.nav-bell:hover { color: var(--accent); background: var(--accent-quiet); }
.nav-bell.has-new { color: var(--ink); }
.nav-bell-n { position: absolute; top: 2px; right: 0; min-width: 17px; height: 17px; padding: 0 4px; border-radius: 999px;
              background: var(--error); color: #fff; font: 700 10px/17px var(--sans); text-align: center; }
.nf-head { display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: center; justify-content: space-between; }
.nf-list { list-style: none; margin: 14px 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.nf-item a { display: grid; grid-template-columns: 28px minmax(0, 1fr); gap: 2px 10px; align-items: baseline; padding: 10px 14px;
             border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); color: var(--ink-2); text-decoration: none; }
.nf-item.is-new a { border-left: 4px solid var(--accent); color: var(--ink); font-weight: 600; }
.nf-item a:hover { border-color: var(--accent); }
.nf-kind { grid-row: span 2; width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center; background: var(--accent-quiet); color: var(--accent); font-weight: 700; }
.nf-mention .nf-kind { background: var(--warning-bg); color: var(--warning); }
.nf-text { overflow-wrap: anywhere; }
.nf-meta { font-size: 12px; color: var(--muted); font-weight: 400; }
/* Small controls in the panel keep their size: the 40px thumb rule for
   buttons on a phone would stretch a tick-box into a bar. */
.tp .tp-box { min-height: 22px; height: 22px; width: 22px; flex: none; }
.tp .tp-react { min-height: 26px; }

/* ==================================================== the board, v2 ===
   Cards carry what you scan for -- title, who, when, what is inside -- and
   put every action in their "..." menu. */
.board-views { display: flex; flex-wrap: wrap; gap: 6px 8px; align-items: center; margin: 12px 0 6px; }
.view-chip { display: inline-flex; align-items: center; gap: 4px; padding: 4px 12px; border-radius: 999px; border: 1px solid var(--border);
             background: var(--surface); color: var(--ink-2); text-decoration: none; font-size: 13px; }
.view-chip a { color: inherit; text-decoration: none; }
.view-chip:hover { border-color: var(--accent); color: var(--accent); }
.view-chip.is-on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.view-chip-mine { padding-right: 6px; }
.view-chip-mine .link-button { font-size: 13px; color: inherit; opacity: .7; }
.view-save summary { cursor: pointer; font-size: 13px; color: var(--muted); }
.view-save .tp-add input { width: 14em; margin-top: 4px; }
.view-switch { display: inline-flex; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.view-switch a { padding: 5px 12px; font-size: 13px; color: var(--ink-2); text-decoration: none; background: var(--surface); }
.view-switch a.is-on { background: var(--accent-quiet); color: var(--accent); font-weight: 600; }

.bulk-bar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; padding: 8px 12px; margin: 8px 0;
            border: 1px solid var(--accent); border-radius: var(--radius-sm); background: var(--accent-quiet);
            position: sticky; top: calc(var(--app-top-h, 0px) + 8px); z-index: 5; }
.bulk-bar.is-scripted { display: none; }
.bulk-bar.is-scripted.is-shown { display: flex; }
.bulk-bar select { height: 32px; border: 1px solid var(--border); border-radius: 7px; background: var(--surface); color: var(--ink); font: inherit; font-size: 13px; max-width: 100%; }
.bulk-n { font-size: 13px; color: var(--ink-2); margin-right: 4px; }
.board-updated { display: block; margin: 6px auto; border: 1px solid var(--accent); background: var(--accent); color: var(--on-accent);
                 border-radius: 999px; padding: 6px 16px; font: 600 13px var(--sans); cursor: pointer; }

.card { padding: 9px 10px 8px; }
.card-top { display: flex; gap: 6px; align-items: flex-start; }
.card-top h3 { flex: 1; min-width: 0; margin: 0; font-size: 14px; line-height: 1.35; overflow-wrap: anywhere; }
.card-top h3 a { padding: 0; }
.card-pick { margin: 3px 0 0; flex: none; opacity: 0; transition: opacity .12s; width: 15px; height: 15px; }
.card:hover .card-pick, .card-pick:checked, .card-pick:focus { opacity: 1; }
@media (pointer: coarse) { .card-pick { opacity: 1; } }
.card.is-picked { border-color: var(--accent); box-shadow: inset 3px 0 0 var(--accent), 0 0 0 2px var(--accent-quiet); }
.card-unread { flex: none; width: 9px; height: 9px; border-radius: 50%; background: var(--accent); margin-top: 6px; }
.card.is-unread h3 a { font-weight: 700; }
.card-menu { position: relative; flex: none; }
.card-menu summary { list-style: none; cursor: pointer; width: 26px; height: 22px; display: grid; place-items: center; border-radius: 6px; color: var(--muted); font-size: 16px; line-height: 1; }
.card-menu summary::-webkit-details-marker { display: none; }
.card-menu summary:hover, .card-menu[open] summary { background: var(--surface-2); color: var(--ink); }
.card-menu-body { position: absolute; right: 0; top: 26px; z-index: 6; min-width: 210px; background: var(--surface); border: 1px solid var(--border);
                  border-radius: 10px; box-shadow: var(--shadow); padding: 6px; display: flex; flex-direction: column; gap: 2px; }
.card-menu-body > a.card-menu-open { display: block; padding: 7px 10px; border-radius: 6px; color: var(--ink); text-decoration: none; font-size: 13.5px; font-weight: 600; }
.card-menu-h { display: block; padding: 6px 10px 2px; font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.card-menu-item { display: block; width: 100%; text-align: left; border: 0; background: none; padding: 6px 10px; border-radius: 6px; font: inherit; font-size: 13.5px; color: var(--ink); cursor: pointer; min-height: 0; }
.card-menu-item:hover, .card-menu-body > a.card-menu-open:hover { background: var(--accent-quiet); }
.card-menu-danger { color: var(--error); }
.card-menu-assign { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; padding: 0 4px 4px; margin: 0; }
.card-menu-assign select { flex: 1; min-width: 0; height: 30px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--ink); font: inherit; font-size: 12.5px; }
.card-foot { display: flex; flex-wrap: wrap; gap: 4px 6px; align-items: center; margin-top: 8px; font-size: 12px; color: var(--muted); }
.card-av { display: inline-grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; background: var(--accent-quiet); color: var(--accent);
           font: 700 9.5px var(--sans); flex: none; }
.card-av-none { background: var(--surface-2); color: var(--muted); }
.due-badge { padding: 1px 7px; border-radius: 999px; background: var(--surface-2); border: 1px solid var(--line-soft); font-size: 11.5px; white-space: nowrap; }
.due-badge.due-soon { background: var(--warning-bg); color: var(--warning); border-color: var(--warning-line); }
.due-badge.due-late { background: var(--error-bg); color: var(--error); border-color: var(--error-line); font-weight: 600; }
.card-pri { color: var(--error); font-weight: 600; font-size: 11.5px; }
.card-pri-low { font-size: 11.5px; }
.card-icon { display: inline-flex; gap: 3px; align-items: center; font-variant-numeric: tabular-nums; }
.card-icon.is-unread { color: var(--accent); font-weight: 700; }
.card-icon.is-done { color: var(--ok); }

/* the list */
.task-list-wrap { overflow-x: auto; margin: 10px 0 24px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.table-scroll > table.task-list { min-width: 760px; }
.task-list { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.task-list th, .task-list td { padding: 8px 10px; border-bottom: 1px solid var(--line-soft); text-align: left; vertical-align: middle; }
.task-list thead th { background: var(--surface-2); font-size: 11.5px; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); white-space: nowrap; }
.task-list th[data-sort] { cursor: pointer; }
.task-list th[aria-sort="ascending"]::after { content: " \25B2"; font-size: 9px; }
.task-list th[aria-sort="descending"]::after { content: " \25BC"; font-size: 9px; }
.task-list .tl-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.task-list .tl-pick { width: 28px; }
.task-list .tl-pick .card-pick { opacity: 1; }
.task-list .tl-title { min-width: 16em; overflow-wrap: anywhere; }
.task-list .tl-title .card-unread { display: inline-block; margin: 0 6px 1px 0; }
.task-list tr.is-unread .tl-title a { font-weight: 700; }
.task-list tr.is-picked td { background: var(--accent-quiet); }
.tl-status { font-weight: 600; font-size: 12.5px; }
.tl-status.s-blocked { color: var(--error); }
.tl-status.s-waiting { color: var(--warning); }
.tl-status.s-done { color: var(--ok); }
.task-list td span.is-unread { color: var(--accent); font-weight: 700; }

/* ---- Task dashboard ----
   Two categorical series, validated (scripts/validate_palette.js) against
   this app's own surfaces: #ffffff light, #141d23 dark. Text never wears
   the series color; the swatch beside it carries identity. */
.dash-root {
  --series-opened: #2a78d6;
  --series-closed: #eb6834;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .dash-root {
    --series-opened: #3987e5;
    --series-closed: #d95926;
  }
}
:root[data-theme="dark"] .dash-root {
  --series-opened: #3987e5;
  --series-closed: #d95926;
}
.tdash-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; margin: 0 0 16px; }
.tdash-filters label { font-size: 13px; color: var(--muted); }
.tdash-filters select { width: auto; max-width: 100%; min-width: 0; height: 34px; border: 1.5px solid var(--field-border); border-radius: 6px; padding: 0 8px; background: var(--surface); color: var(--ink); font: inherit; font-size: 14px; }
.dash-root a:not(.dash-tile-in) { color: var(--accent-ink); }
.dash-tiles {
  list-style: none; margin: 0 0 20px; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px;
}
.dash-tile { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
.dash-tile-in { display: flex; flex-direction: column; gap: 2px; padding: 14px 16px; text-decoration: none; color: inherit; border-radius: inherit; }
a.dash-tile-in:hover .dash-label { text-decoration: underline; }
a.dash-tile-in:hover { background: var(--surface-2); }
.dash-n { font-size: 28px; font-weight: 600; font-variant-numeric: tabular-nums; line-height: 1.1; color: var(--ink); }
.dash-label { font-size: 13px; color: var(--muted); }
.dash-tile.is-bad .dash-n { color: var(--error); }
.dash-tile.is-bad { border-color: var(--error-line); }
.dash-tile.is-warn .dash-n { color: var(--warning); }
.dash-tile.is-warn { border-color: var(--warning-line); }

.dash-grid { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 16px; align-items: start; }
.dash-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; min-width: 0; }
.dash-card h2 { margin: 0 0 4px; font-size: 16px; }
.dash-card > .meta { margin: 0 0 10px; }
.dash-flow { grid-row: span 2; }
.dash-wide { grid-column: 1 / -1; }

.dash-legend { list-style: none; margin: 0 0 6px; padding: 0; display: flex; gap: 16px; font-size: 13px; color: var(--ink-2); }
.dash-legend li { display: flex; align-items: center; gap: 6px; }
.dash-swatch { display: inline-block; width: 10px; height: 10px; border-radius: 3px; flex: none; }
.dash-swatch.is-opened { background: var(--series-opened); }
.dash-swatch.is-closed { background: var(--series-closed); }

.dash-chart-wrap { position: relative; }
.dash-chart { display: block; width: 100%; height: auto; overflow: visible; }
.dash-grid-line { stroke: var(--line-soft); stroke-width: 1; }
.dash-grid-line.is-base { stroke: var(--border); }
.dash-tick { fill: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; }
.dash-direct { fill: var(--ink-2); font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums; }
.dash-bar.is-opened { fill: var(--series-opened); }
.dash-bar.is-closed { fill: var(--series-closed); }
.dash-hit { fill: var(--ink); fill-opacity: 0; cursor: default; outline: none; }
.dash-hit.is-on { fill-opacity: .05; }
.dash-hit:focus-visible { stroke: var(--accent); stroke-width: 2; }
.dash-tip {
  position: absolute; transform: translateY(-100%); pointer-events: none; z-index: 2;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: var(--shadow); padding: 8px 10px; font-size: 12.5px; min-width: 130px;
}
.dash-tip-head { font-weight: 600; margin-bottom: 4px; color: var(--ink); }
.dash-tip-row { display: flex; align-items: center; gap: 6px; color: var(--ink-2); }
.dash-tip-row strong { margin-left: auto; color: var(--ink); font-variant-numeric: tabular-nums; }
.dash-table-view { margin-top: 10px; font-size: 13px; }
.dash-table-view summary { cursor: pointer; color: var(--accent-ink); }

.dash-table { margin-top: 4px; }
.dash-table th.num, .dash-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.dash-late { color: var(--error); font-weight: 600; }

.dash-ages { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.dash-ages li { display: grid; grid-template-columns: 96px 1fr 36px; align-items: center; gap: 10px; font-size: 13px; }
.dash-age-label { color: var(--ink-2); }
.dash-age-track { height: 12px; }
.dash-age-bar { display: block; height: 100%; min-width: 0; background: var(--series-opened); border-radius: 0 4px 4px 0; }
.dash-age-n { text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; }

@media (max-width: 760px) {
  .dash-grid { grid-template-columns: minmax(0, 1fr); }
  .dash-flow { grid-row: auto; }
  .dash-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .dash-n { font-size: 24px; }
}
.mp-wws summary { cursor: pointer; }
.mp-wws[open] summary { margin-bottom: 10px; }
.mp-wws-table { margin-top: 4px; }
.mp-wws-table td { vertical-align: top; }
.mp-wws-head { margin: 12px 0 4px; }
.mp-wws-ends { margin: 0 0 10px; padding-left: 18px; font-size: 13px; }
.mp-wws-ends li { overflow-wrap: anywhere; margin: 2px 0; }

.mp-call { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 4px 14px; margin: -4px 0 14px; padding: 12px 14px; background: var(--surface); border: 1px solid var(--error-line); border-radius: var(--radius-sm); font-size: 13px; }
.mp-call[hidden] { display: none; }
.mp-call dt { color: var(--muted); font-weight: 600; }
.mp-call dd { margin: 0; overflow-wrap: anywhere; }
@media (max-width: 560px) { .mp-call { grid-template-columns: minmax(0, 1fr); } .mp-call dd { margin-bottom: 6px; } }

.mp-sched summary { cursor: pointer; }
.mp-sched[open] > summary { margin-bottom: 8px; }
.mp-sched-one { margin-top: 10px; border-top: 1px solid var(--line-soft); padding-top: 8px; }
.mp-sched-table td.meta { font-size: 12px; }
.check-form { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; margin: 10px 0; }
.check-form .check-row { display: flex; align-items: flex-start; gap: 8px; font-size: 14px; }
.check-form .check-row input[type="checkbox"] { margin-top: 3px; flex: none; width: auto; }

/* ---- categories, issue types, approvals ---- */
.card-tags { display: flex; flex-wrap: wrap; gap: 4px 6px; margin: 4px 0 6px; font-size: 11.5px; }
.card-type { padding: 1px 7px; border-radius: 999px; background: var(--surface-3); color: var(--ink-2); font-weight: 600; }
.card-cat { padding: 1px 7px; border-radius: 999px; border: 1px solid var(--border); color: var(--muted); }
.approval-badge { display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 11.5px; font-weight: 700; white-space: nowrap; border: 1px solid transparent; }
.approval-badge.a-pending { background: var(--warning-bg); color: var(--warning); border-color: var(--warning-line); }
.approval-badge.a-approved { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-line); }
.approval-badge.a-rejected { background: var(--error-bg); color: var(--error); border-color: var(--error-line); }
.task-list td .approval-badge { margin-left: 6px; }
.tp-approval-state { margin: 0 0 6px; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; font-size: 13px; }
.tp-approval-form { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }
.tp-approval-form input[type="text"] { width: 100%; height: 32px; border: 1px solid var(--field-border); border-radius: 8px; padding: 0 8px; background: var(--surface); color: var(--ink); font: inherit; font-size: 13px; }
.tp-approval-buttons { display: flex; gap: 6px; flex-wrap: wrap; }
.tp-setup-hint { margin: 4px 0 0; }
.task-class-row { margin-top: 4px; }
.mw-approvals { margin-bottom: 18px; }
.mw-approvals h2, .mw-assigned-h { font-size: 16px; margin: 0 0 8px; }

/* ---- board setup ---- */
.bs-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; align-items: start; }
.bs-starter { margin-bottom: 16px; }
.bs-cat { padding: 10px 0; border-bottom: 1px solid var(--line-soft); }
.bs-cat:last-of-type { border-bottom: 0; }
.bs-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.bs-rename, .bs-add, .bs-type-form { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; flex: 1 1 16rem; min-width: 0; }
.bs-rename input[type="text"], .bs-add input[type="text"], .bs-type-form input[type="text"] { flex: 1 1 10rem; min-width: 0; height: 34px; border: 1.5px solid var(--field-border); border-radius: 6px; padding: 0 8px; background: var(--surface); color: var(--ink); font: inherit; font-size: 14px; }
.bs-cat > .bs-row .bs-rename input[type="text"] { font-weight: 600; }
.bs-subs { list-style: none; margin: 8px 0 0; padding: 0 0 0 18px; border-left: 2px solid var(--line-soft); display: flex; flex-direction: column; gap: 6px; }
.bs-subs .bs-rename input[type="text"], .bs-subs .bs-add input[type="text"] { height: 30px; font-size: 13px; }
.bs-add-top { margin-top: 14px; }
.bs-types { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.bs-type { padding-bottom: 10px; border-bottom: 1px solid var(--line-soft); display: flex; flex-direction: column; gap: 4px; }
.is-archived input[type="text"] { color: var(--muted); text-decoration: line-through; }
.bs-danger { color: var(--error); }
@media (max-width: 760px) { .bs-grid { grid-template-columns: minmax(0, 1fr); } }
.bs-type > .bs-type-form { flex: none; }
.login-sso { margin: 14px 0 0; text-align: center; font-size: 14px; }
.sso-redirect { display: inline-block; max-width: 100%; overflow-wrap: anywhere; padding: 4px 8px; background: var(--surface-2); border-radius: var(--radius-sm); }

/* ---- tabs on long admin pages (static/admin_tabs.js) ----
   Folder tabs: each a tab with rounded top corners on a shared baseline;
   the chosen one is the page's own white, outlined, a little taller, and
   open at the bottom so it reads as the front folder. The baseline is an
   inset shadow rather than a border so the row can scroll sideways on a
   phone without clipping the chosen tab's join. */
.tabs { display: flex; align-items: flex-end; gap: 4px; margin: 4px 0 0; padding: 0 0 0 0;
        position: relative; z-index: 1; overflow-x: auto; scrollbar-width: none; }
.tabs::-webkit-scrollbar { display: none; }
.tabs a { flex: none; display: inline-flex; align-items: center; gap: 6px; padding: 8px 16px 7px;
          color: var(--ink-2); font-weight: 600; font-size: 14px; text-decoration: none; white-space: nowrap;
          background: var(--surface-3); border: 1px solid var(--border); border-bottom-color: transparent;
          border-radius: 9px 9px 0 0; box-shadow: inset 0 -1px 0 var(--border); transition: background .12s, color .12s; }
.tabs a:hover { background: var(--surface-2); color: var(--ink); }
.tabs a.is-on { position: relative; padding-top: 11px; padding-bottom: 9px; color: var(--accent-ink);
                background: var(--surface); border-color: var(--border); border-bottom-color: var(--surface);
                box-shadow: inset 0 3px 0 var(--accent), 0 -2px 8px -4px rgba(12, 17, 22, .18); }
.tabs a:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tab-n { font-size: 12px; font-weight: 600; padding: 0 7px; border-radius: 999px; background: var(--surface); color: var(--ink-2); border: 1px solid var(--border); }
.tabs a.is-on .tab-n { background: var(--accent-quiet); color: var(--accent-ink); border-color: transparent; }
.tab-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ok); }
.tab-panel > .panel:first-child, .tab-panel > .tab-h:first-child { margin-top: 0; }
/* The tabs sit on the first section of the open tab, its top edge their
   baseline: the chosen tab's white bottom covers that edge where it joins. */
.tabs + .tab-panel, .tab-panel + .tab-panel { margin-top: -1px; }
.tab-panel > .panel:first-child { border-top-left-radius: 0; }
.tab-h { font-size: 18px; margin: 0 0 12px; }
.tab-panel { display: flex; flex-direction: column; gap: 16px; }
.tab-panel > .panel { margin: 0; }

/* ---- the pinned top: header and breadcrumb (templates/_nav.html) ---- */
.app-top { position: sticky; top: 0; z-index: 45; background: var(--bg); box-shadow: 0 1px 0 var(--border); }
html { scroll-padding-top: calc(var(--app-top-h, 0px) + 12px); }
/* Sticky things on the page itself sit below the pinned top; inside the
   task dialog (its own scroller) they stay at 0. */
.tc-resulthead, .tp-tabs { top: var(--app-top-h, 0px); }
.task-dialog .tp-tabs { top: 0; }

/* ---- search for a page, in the header (static/nav_search.js) ---- */
.nav-search { position: relative; display: flex; align-items: center; gap: 8px; flex: 0 1 360px; min-width: 0;
              margin: 0 0 0 auto; padding: 0 12px; height: 36px; border: 1px solid var(--border);
              border-radius: 999px; background: var(--surface-2); color: var(--muted); }
.nav-search:focus-within { border-color: var(--accent); background: var(--surface); box-shadow: 0 0 0 3px var(--accent-quiet); }
.nav-search input { flex: 1; min-width: 0; height: 100%; border: 0; background: transparent; color: var(--ink);
                    font: inherit; font-size: 14px; outline: none; }
.nav-search input::-webkit-search-cancel-button { cursor: pointer; }
.nav-search-list { position: absolute; top: calc(100% + 6px); left: 0; right: 0; min-width: 300px; z-index: 60;
                   list-style: none; margin: 0; padding: 6px; background: var(--surface); border: 1px solid var(--border);
                   border-radius: 12px; box-shadow: var(--shadow), 0 12px 32px -12px rgba(12, 17, 22, .35);
                   max-height: 60vh; overflow-y: auto; }
.nav-search-list li a { display: flex; flex-direction: column; gap: 1px; padding: 8px 10px; margin: 0;
                        border-radius: 8px; color: var(--ink); text-decoration: none; }
.nav-search-list li[aria-selected="true"] a, .nav-search-list li a:hover { background: var(--accent-quiet); text-decoration: none; }
.nav-search-t { font-size: 14px; color: var(--ink-2); }
.nav-search-t b { color: var(--ink); font-weight: 700; }
.nav-search-k { font-size: 12px; color: var(--muted); }
.nav-search-none { padding: 10px; font-size: 13px; color: var(--muted); }
@media (max-width: 760px) {
  .nav-search { order: 3; flex: 1 0 100%; margin: 2px 0 0; }
}
/* Hovering from a menu's name down into its list crosses a 6px gap; this
   keeps the pointer "inside" the menu on the way, so it stays open. */
.nav-panel::before { content: ""; position: absolute; left: 0; right: 0; top: -8px; height: 8px; }
/* The comment box sits above the newest-first conversation now, in the
   flow rather than pinned to the foot. */
.tp-composer { position: static; border-top: 1px solid var(--line-soft); border-bottom: 0; margin-top: 8px; }
.tp img.rt-img { cursor: zoom-in; }

/* ---- a client's own home for their tickets (templates/my_tickets.html) ---- */
.mt-page { max-width: 880px; }
.mt-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-end; gap: 10px 16px; }
.mt-head h1 { margin-bottom: 2px; }
.mt-head-acts { display: flex; gap: 8px; flex-wrap: wrap; }
.mt-new { margin-top: 14px; }
.mt-new > summary { cursor: pointer; font-weight: 700; font-size: 16px; display: flex; align-items: center; gap: 10px; list-style: none; }
.mt-new > summary::-webkit-details-marker { display: none; }
.mt-new-plus { display: inline-grid; place-items: center; width: 28px; height: 28px; border-radius: 50%; background: var(--accent); color: var(--on-accent); font-size: 20px; line-height: 1; }
.mt-new[open] > summary { margin-bottom: 12px; }
.mt-new-form { display: flex; flex-direction: column; gap: 12px; }
.mt-new-form input[type="text"], .mt-new-form textarea { width: 100%; font-size: 16px; }
.mt-new-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 10px; }
.mt-new-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; }
.mt-scope { margin-top: 16px; }
.mt-section { margin-top: 18px; }
.mt-section h2 { font-size: 16px; margin: 0 0 4px; display: flex; align-items: center; gap: 8px; }
.mt-section > .meta { margin: 0 0 8px; }
.mt-resolved > summary { cursor: pointer; list-style: none; }
.mt-resolved > summary::-webkit-details-marker { display: none; }
.mt-resolved > summary h2::before { content: "\25B8"; color: var(--muted); font-size: 12px; }
.mt-resolved[open] > summary h2::before { content: "\25BE"; }
.mt-list { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.mt-ticket { display: flex; flex-direction: column; gap: 6px; padding: 12px 14px; background: var(--surface); border: 1px solid var(--border);
             border-radius: var(--radius); color: var(--ink); text-decoration: none; }
.mt-ticket:hover { border-color: var(--accent); text-decoration: none; }
.mt-ticket:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.mt-waiting .mt-ticket { border-left: 4px solid var(--warning); }
.mt-line1 { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; }
.mt-title { font-weight: 600; font-size: 15px; display: flex; align-items: center; gap: 6px; min-width: 0; overflow-wrap: anywhere; }
.mt-ticket.is-unread .mt-title { font-weight: 800; }
.mt-id { color: var(--muted); font-size: 12px; flex: none; }
.mt-line2 { display: flex; flex-wrap: wrap; gap: 4px 6px; align-items: center; font-size: 12px; }
.mt-status { padding: 1px 8px; border-radius: 999px; font-weight: 700; font-size: 12px; background: var(--surface-3); color: var(--ink-2); }
.mt-status.s-doing { background: var(--info-bg); color: var(--info); }
.mt-status.s-waiting { background: var(--warning-bg); color: var(--warning); }
.mt-status.s-blocked { background: var(--error-bg); color: var(--error); }
.mt-status.s-done { background: var(--ok-bg); color: var(--ok); }
.mt-line3 { font-size: 12.5px; }
.mt-late { color: var(--error); font-weight: 600; }
  .tp-composer textarea { font-size: 16px; }
  .tp-composer button[type="submit"] { width: 100%; min-height: 44px; }
  .tp-quick-form input[type="text"], .tp-approval-form input[type="text"] { font-size: 16px; height: 40px; }
}

/* ---- Board setup -> Automation rules ---- */
.rule-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.rule-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px; display: flex; flex-direction: column; gap: 6px; }
.rule-card.is-off { opacity: .7; }
.rule-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.rule-n { display: inline-grid; place-items: center; min-width: 24px; height: 24px; border-radius: 50%; background: var(--surface-3); font-size: 12px; }
.rule-name { font-size: 15px; }
.rule-line { margin: 0; font-size: 14px; color: var(--ink-2); overflow-wrap: anywhere; }
.rule-line em { font-style: normal; color: var(--muted); }
.rule-k { display: inline-block; min-width: 46px; font-weight: 700; font-size: 12px; text-transform: uppercase; letter-spacing: .05em; color: var(--accent-ink); }
.rule-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 4px; }
.rule-editor { scroll-margin-top: calc(var(--app-top-h, 0px) + 12px); }
.rule-form { display: flex; flex-direction: column; gap: 14px; }
.rule-top { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 14px; }
.rule-block { border: 1px solid var(--line-soft); border-radius: var(--radius-sm); padding: 10px 12px 12px; margin: 0; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.rule-block legend { padding: 0 6px; display: flex; align-items: center; gap: 8px; }
.rule-row { display: flex; flex-wrap: wrap; gap: 6px; }
.rule-row select, .rule-row input, .rule-block > select, .rule-block legend select { height: 36px; border: 1.5px solid var(--field-border); border-radius: 6px; padding: 0 8px; background: var(--surface); color: var(--ink); font: inherit; font-size: 14px; min-width: 0; max-width: 100%; }
.rule-row input[type="text"], .rule-row input[type="email"] { flex: 1 1 14rem; }
.rule-row [hidden] { display: none; }
.rule-test-form { display: flex; flex-direction: column; gap: 10px; }
.rule-test-form textarea { width: 100%; }
.rule-test-result { margin-top: 10px; padding: 10px 12px; background: var(--surface-2); border-radius: var(--radius-sm); }
.rule-test-result ol { margin: 4px 0 0; padding-left: 20px; }
@media (max-width: 600px) { .rule-row select, .rule-row input { flex: 1 1 100%; } }
.tab-panel ol.rule-list { margin: 0; padding: 0; }
.rule-acts .button, .rule-acts button:not(.link-button) { height: 32px; padding: 0 12px; font-size: 13px; display: inline-flex; align-items: center; }

/* The Board row on a task: pick a board and Move. Highlighted while the
   task "needs a board" (it came in by email from an unknown sender). */
.tp-board-pick { display: flex; gap: 6px; align-items: center; min-width: 0; }
.tp-board-pick select { flex: 1 1 auto; min-width: 0; }
.tp-board-field.is-needed { background: var(--warning-bg); border-radius: var(--radius-sm); padding: 4px 6px; margin: 0 -6px; }

.rule-remove { flex: none; width: 36px; height: 36px; border: 1px solid var(--border); border-radius: 6px;
               background: var(--surface); color: var(--muted); font-size: 18px; line-height: 1; cursor: pointer; }
.rule-remove:hover { color: var(--error); border-color: var(--error-line); background: var(--error-bg); }

.tp-board-field.is-needed { flex-wrap: wrap; }
.tp-keep-board { margin-top: 6px; width: 100%; }

/* Task Board: "Open on this board by default" beside the board picker */
.board-default { display: inline-flex; align-items: center; gap: 8px; margin: 0 0 0 12px; flex-wrap: wrap; }
.board-default-on { font-size: 13px; font-weight: 600; color: var(--accent); }
@media (max-width: 640px) { .board-default { margin: 6px 0 0; } }

/* Admin -> the consultancy's users: which boards each may open */
.user-boards-form { display: flex; flex-direction: column; gap: 10px; margin-top: 14px; }
.user-boards-form .td-head { margin: 0; }
.board-checks { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 4px 16px; }
.user-default-board { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--muted); max-width: 360px; }
.user-boards-form button { align-self: flex-start; }
.user-default-board select { padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px;
                             font-size: 14px; background: var(--surface); color: inherit; }
