/* Logro Repricing Web UI
   Light theme. Colours, type, radius and spacing come from logro-brand.css,
   which every shell loads BEFORE this file (the directory is the brand
   source). The names below (--accent, --primary, --navy, --radius, the
   --text-* and --sp-* scales) are this file's legacy vocabulary, mapped onto
   the brand tokens so the platform and the directory read as one product. */

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

/* Enable `auto` keyword interpolation so `height: 0 → auto` transitions animate
   smoothly (used by collapsible detail rows on the job page). Chrome 129+,
   Firefox 129+, Safari 17.4+. Older browsers gracefully fall back to instant. */
:root {
  interpolate-size: allow-keywords;
}

:root {
  /* Palette -- clean light */
  --bg: #ffffff;
  --bg-alt: #f6f7f9;
  --bg-card: #ffffff;
  --bg-elevated: #f0f1f4;
  --text: #1a1d23;
  --text-secondary: #4b5060;
  --text-muted: #565a6a;
  --border: #dfe1e6;
  --border-light: #e8eaee;

  /* Brand -- from logro-brand.css. Navy fills the actions; the one accent is
     brand blue (links, selected edges, counts, focus). Orange is the logo's
     alone and has no token. WCAG 2.1 AA: brand blue is 5.3:1 on white and
     4.6:1 on its tint; navy is 16:1. */
  --accent: var(--brand-blue);
  --accent-hover: #285f94;
  --accent-light: var(--brand-blue-tint);
  --focus-color: var(--navy);
  /* Primary action fill: navy, white text 16:1. The dark workspace shell has
     its own pair (--ws2-accent-fill / --ws2-on-accent in workspace_glass.css). */
  --primary: var(--navy);
  --primary-hover: var(--navy-hover);
  --on-primary: #ffffff;
  --navy-light: var(--navy-hover);
  /* Referenced across the app (page titles in account/admin/guide/catalog, the
     help scenario cards) but never defined, so those fell back to inherited
     colour. Map them onto the existing palette. --color-guide-heading is
     navy on the accent tint (~14:1); --text-primary is the body text colour. */
  --text-primary: var(--text);
  --color-guide-heading: var(--navy);
  --color-guide-accent: var(--accent);
  --color-guide-scenario-bg: var(--accent-light);

  /* Semantic. --success and --warning are used as TEXT colours, so they were
     darkened for WCAG 1.4.3 (2026-10-08): --success #1a8a5c was 4.34:1 on white
     and 3.97:1 on --success-bg (now 5.13:1 / 4.69:1); --warning #a17216 was
     4.26:1 on white and 3.96:1 on --warning-bg (now 5.20:1 / 4.83:1). */
  --success: #177d52;
  --success-bg: #edf7f2;
  --error: #c93b3b;
  --error-bg: #fdf0f0;
  --warning: #8f6513;
  --warning-bg: #fdf6e8;
  /* Caution -- distinct from --warning (used by pulsing self-healing states).
       completed_unverified is TERMINAL, not self-healing, so it gets its own
       deeper amber/orange tone -- a caution color, not an "awaiting input" one. */
  --caution: #b45309;
  --caution-bg: #fdf0dc;
  --caution-border: #f0c284;

  /* Shared */
  --radius: 6px; /* = logro-brand.css --radius; one radius, flat */
  --radius-sm: 4px;
  --shadow-sm: none; /* the brand is flat: no shadows */
  --shadow: none;
  --shadow-md: none;
  --savings-green: #177d52;
  --savings-yellow: #8f6513;
  --savings-red: #c93b3b;

  /* Type scale -- 18px base (senior-friendly), scales from root font-size */
  /* 16px floor (PU-01): --text-xs and --text-sm are the smallest type there is */
  --text-xs: 0.889rem; /* 16px at 18px base, brand "small" */
  --text-sm: 0.889rem; /* 16px at 18px base */
  --text-base: 1rem; /* 18px, brand "body" */
  --text-md: 1.111rem; /* 20px, brand "item" */
  --text-lg: 1.222rem; /* 22px, brand "section" */
  --text-xl: 1.556rem; /* 28px, brand "title" */
  --text-2xl: 1.556rem; /* 28px: page titles stay on the one title size */

  /* Spacing */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.25rem;
  --sp-6: 1.5rem;
  --sp-8: 2rem;
  --sp-10: 2.5rem;
  --sp-12: 3rem;

  /* Unified form-control heights -- inputs, selects, textareas all share these */
  --control-h: 48px;
  --control-h-sm: 44px; /* PU-05 tap floor */

  --focus-ring: 0 0 0 2px #fff, 0 0 0 5px var(--focus-color);
}

/* ─── Accessibility ────────────────────────────────── */

.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;
}

:focus-visible {
  outline: 3px solid var(--focus-color);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

input:focus-visible,
textarea:focus-visible,
select:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(24, 32, 56, 0.25);
  border-color: var(--navy);
}

/* ─── Base ─────────────────────────────────────────── */

html {
  font-size: 18px;
}

body {
  font-family: var(--sans);
  font-size: var(--text-base);
  color: var(--text);
  background: var(--bg-alt);
  line-height: 1.6;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* ─── Layout ───────────────────────────────────────── */

header {
  /* Brand header: white, hairline, no shadow, the logo in its own colours
     (the directory bar; the logo is the only orange on the page). */
  background: var(--canvas);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 1000;
  /* overflow-x:hidden removed -- it forced overflow-y:auto which clipped
       child dropdowns, and also constrained the stacking context so the
       notification panel (even with position:fixed) appeared behind page
       overlays. The nav's max-width:1120px already prevents horizontal bleed. */
}

nav {
  max-width: 1120px;
  margin: 0 auto;
  padding: var(--sp-4) var(--sp-6);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.logo {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  text-decoration: none;
}

.logo img {
  height: var(--logo-h);
  width: auto;
  max-width: var(--logo-max-w);
}

.logo-text {
  font-weight: 700;
  font-size: var(--text-base);
  color: var(--ink);
  letter-spacing: 0.01em;
}

.logo-divider {
  width: 1px;
  height: 22px;
  background: var(--line);
}

.logo-product {
  font-weight: 600;
  font-size: var(--text-sm);
  color: var(--ink-3);
  letter-spacing: 0.02em;
}

main {
  max-width: 1120px;
  width: 100%;
  margin: 0 auto;
  padding: var(--sp-10) var(--sp-6) var(--sp-12);
  flex: 1;
}

footer {
  border-top: 1px solid var(--border);
  background: var(--bg);
  text-align: center;
  padding: var(--sp-4) var(--sp-6);
  font-size: 1rem;
  color: #565f73; /* palette ink-3 (PU-12); was --text-muted #565a6a */
}

/* ─── Typography ───────────────────────────────────── */

/* Links are the one accent, underlined (colour is never the only cue). Zero
   specificity beyond the element, so every classed control keeps its own
   colour; this only replaces the browser's default blue and purple. */
a {
  color: var(--accent);
  text-underline-offset: 0.15em;
}
a:where(:hover) {
  color: var(--accent-hover);
}

h1 {
  font-size: var(--text-xl);
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--navy);
  line-height: 1.3;
  margin-bottom: var(--sp-6);
}

h2 {
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--navy);
}

h3 {
  font-size: var(--text-base);
  font-weight: 700;
  margin-top: var(--sp-8);
  margin-bottom: var(--sp-3);
  color: var(--text);
}

/* PU-09: no heading or paragraph ends in a single word. Short blocks (headings,
   table cells, labels, hints) balance their lines; running prose uses pretty,
   which only avoids a short last line. Both are progressive enhancement. */
h1,
h2,
h3,
h4,
h5,
h6,
th,
td,
label,
dt,
summary,
legend,
figcaption,
.meta,
.form-hint,
.cost-card-label,
.admin-tool-card-desc,
.guide-help-item-title,
.guide-help-item-desc {
  text-wrap: balance;
}

p,
li,
dd,
blockquote {
  text-wrap: pretty;
}

hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: var(--sp-8) 0;
}

p.meta {
  color: var(--text-secondary);
  font-size: var(--text-sm);
  line-height: 1.5;
}

/* ─── Nav structure ────────────────────────────────── */

.nav-left {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}

/* ─── Back link ────────────────────────────────────── */

.back-link {
  display: inline-block;
  color: var(--primary);
  text-decoration: none;
  font-size: var(--text-sm);
  font-weight: 600;
  margin-bottom: var(--sp-5);
  transition: color 0.15s;
}

.back-link:hover {
  color: var(--primary-hover);
}

.job-count {
  font-size: var(--text-sm);
  color: var(--text-muted);
  font-weight: 600;
}

/* ─── Buttons ──────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  padding: 12px 22px;
  font-size: var(--text-sm);
  font-weight: 600;
  font-family: inherit;
  color: var(--text-secondary);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  text-decoration: none;
  cursor: pointer;
  transition: all 0.15s;
  line-height: 1.4;
  white-space: nowrap;
  min-height: 48px;
}

.btn:hover {
  color: var(--text);
  border-color: var(--text-muted);
  background: var(--bg-alt);
}

.btn-primary {
  background: var(--primary);
  color: var(--on-primary);
  border-color: var(--primary);
  font-weight: 700;
}

.btn-primary:hover {
  background: var(--primary-hover);
  border-color: var(--primary-hover);
  color: var(--on-primary);
}

.btn-sm {
  padding: 9px 16px;
  font-size: var(--text-sm);
  min-height: 44px; /* PU-05 tap floor */
}

.btn-icon {
  padding: 0;
  width: 44px;
  height: 44px;
  min-height: 44px; /* PU-05 tap floor */
  font-size: var(--text-base);
  line-height: 1;
  border-radius: var(--radius-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  color: var(--text-secondary);
  border: 1px solid var(--border);
}

.btn-icon:hover {
  color: var(--text);
  border-color: var(--text-muted);
  background: var(--bg-alt);
}

.btn-icon svg {
  display: block;
}

.btn-danger {
  color: var(--error);
  border-color: #e8c0c0;
}

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

.btn-download {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 14px 36px;
  font-size: var(--text-md);
  font-weight: 700;
  font-family: inherit;
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: var(--radius);
  text-decoration: none;
  cursor: pointer;
  transition: all 0.15s;
  letter-spacing: 0.01em;
}

.btn-download:hover {
  background: var(--accent-hover);
  transform: translateY(-1px);
}

.btn-download:active {
  transform: translateY(0);
}

.btn-upload {
  padding: 14px 30px;
  font-size: var(--text-base);
  font-weight: 700;
  min-height: 56px;
}

.btn-upload.is-processing {
  background: #e8a08a;
  color: #fff;
  pointer-events: none;
  border-color: #e8a08a;
}

.btn-upload .spinner-inline {
  display: none;
  width: 16px;
  height: 16px;
  border: 2px solid rgba(255, 255, 255, 0.4);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
  margin-right: var(--sp-2);
}

.btn-upload.is-processing .spinner-inline {
  display: inline-block;
}

/* Generic in-button spinner for non-upload buttons */
.btn .spinner-inline {
  display: none;
  width: 14px;
  height: 14px;
  border: 2px solid rgba(255, 255, 255, 0.4);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
  margin-right: var(--sp-2);
  vertical-align: middle;
}

.btn.is-processing:not(.btn-upload) .spinner-inline {
  display: inline-block;
}
.btn.is-processing:not(.btn-upload) {
  pointer-events: none;
  opacity: 0.8;
}

/* ─── Cards ────────────────────────────────────────── */

.card {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--sp-5);
  box-shadow: var(--shadow-sm);
  margin-bottom: var(--sp-6);
}

.card h3 {
  margin: 0 0 var(--sp-4);
  font-size: var(--text-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

.card h3:not(:first-child) {
  margin-top: var(--sp-6);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--border);
}

.card .summary-grid {
  margin-top: 0;
}
.card .stat {
  background: var(--bg-alt);
}
.card .error-box {
  margin-top: 0;
}
.card .file-list {
  margin: 0;
}
.card .download-row {
  margin: 0;
  padding: var(--sp-8) 0;
}

/* ─── Tables ───────────────────────────────────────── */

table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
  background: var(--bg);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  border: 1px solid var(--border);
}

thead th {
  text-align: center;
  padding: 12px;
  border-bottom: 2px solid var(--border);
  font-size: var(--text-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--navy);
  background: #dce1e8;
}

thead th:first-child {
  text-align: left;
}

tbody td {
  padding: 8px 12px;
  border-bottom: 1px solid var(--border-light);
  vertical-align: middle;
  text-align: center;
}

tbody td:first-child {
  text-align: left;
}

tbody tr:last-child td {
  border-bottom: none;
}
tbody tr:hover {
  background: var(--bg-alt);
}
tbody tr[data-href] {
  cursor: pointer;
}

tbody td a {
  color: var(--accent);
  text-decoration: none;
  font-weight: 700;
}

tbody td a:hover {
  text-decoration: underline;
}

table.compact {
  font-size: var(--text-sm);
  box-shadow: none;
  background: transparent;
  border: none;
}
table.compact td {
  padding: 6px 14px 6px 0;
  border: none;
}

/* ─── Actions cell ─────────────────────────────────── */

.actions {
  display: flex;
  gap: var(--sp-2);
  align-items: center;
  justify-content: flex-end;
}

thead th.th-actions {
  text-align: right;
}

/* ─── Status badges ────────────────────────────────── */

.status {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: var(--text-sm);
  font-weight: 700;
  text-transform: capitalize;
  letter-spacing: 0.01em;
  line-height: 1.3;
  white-space: nowrap;
}

.status-processing {
  background: var(--bg-elevated);
  color: var(--text-secondary);
}
.status-complete {
  background: var(--success-bg);
  color: var(--success);
}
.status-completed {
  background: var(--success-bg);
  color: var(--success);
}
.status-complete_with_errors {
  background: var(--warning-bg);
  color: var(--warning);
}
.status-needs_support {
  background: var(--warning-bg);
  color: var(--warning);
}
.status-needs-input {
  background: var(--warning-bg);
  color: var(--warning);
  font-weight: 600;
}
/* completed_unverified -- terminal caution state, no pulse (not self-healing). */
.status-needs-review {
  background: var(--caution-bg);
  color: var(--caution);
  font-weight: 700;
  border: 1px solid var(--caution-border);
}
.status-resolving {
  background: var(--warning-bg);
  color: var(--warning);
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.status-resolving::before {
  content: "";
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--warning);
  animation: resolving-pulse 1.4s ease-in-out infinite;
  flex-shrink: 0;
}
@keyframes resolving-pulse {
  0%,
  100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.4;
    transform: scale(0.75);
  }
}
.status-error {
  background: var(--error-bg);
  color: var(--error);
}
.status-submitted {
  background: var(--bg-elevated);
  color: var(--text-secondary);
}
.status-resolved {
  background: var(--success-bg);
  color: var(--success);
}
.status-needs_attention {
  background: var(--warning, #e6a817);
  color: white;
}
.status-in_progress {
  background: var(--accent);
  color: white;
}
.status-closed {
  background: var(--text-muted);
  color: white;
}
.status-open {
  background: var(--secondary, #6c757d);
  color: white;
}
/* Provider data report triage states (ER-O4, admin.html). */
.status-new,
.status-wont_fix,
.status-duplicate {
  background: var(--bg-elevated);
  color: var(--text-secondary);
}
.status-triaged {
  background: var(--warning-bg);
  color: var(--warning);
}
.status-fixed {
  background: var(--success-bg);
  color: var(--success);
}
.status-awaiting-answer {
  background: var(--warning-bg);
  color: var(--warning);
  font-weight: 600;
}

/* ─── Inline meta-line status (no pill) ───────────────── */

.meta-status {
  font-weight: 700;
  white-space: nowrap;
  text-transform: capitalize;
}
.meta-status-awaiting-answer {
  color: var(--warning);
}

/* status_css_class filter returns "status-{status}" or "status-resolving" */
.meta-status-status-complete {
  color: var(--success);
}
.meta-status-status-error {
  color: var(--error);
}
.meta-status-status-resolving {
  color: var(--warning);
}
.meta-status-status-needs-review {
  color: var(--caution);
  font-weight: 700;
}
.meta-status-status-needs-input {
  color: var(--warning);
  font-weight: 700;
}

/* ─── Savings ──────────────────────────────────────── */

.savings-green {
  color: var(--savings-green);
  font-weight: 700;
}
.savings-yellow {
  color: var(--savings-yellow);
  font-weight: 700;
}
.savings-red {
  color: var(--savings-red);
  font-weight: 700;
}
.savings-none {
  color: var(--text-muted);
  font-style: italic;
}

/* ─── Summary grid ─────────────────────────────────── */

.summary-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--sp-2);
  margin: var(--sp-3) 0;
  align-items: stretch;
}

.stat {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--sp-3);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--sp-2);
  min-height: calc(
    var(--text-sm) * 1.4 * 2 + var(--text-base) * 1.2 + var(--sp-4) * 2 +
      var(--sp-3)
  );
}

.stat .label {
  display: flex;
  align-items: flex-start;
  font-size: var(--text-sm);
  font-weight: 700;
  line-height: 1.4;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  min-height: calc(var(--text-sm) * 1.4 * 2);
}

.stat .value {
  display: block;
  font-size: var(--text-base);
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: -0.02em;
  color: var(--navy);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.stat.highlight {
  background: var(--success-bg);
  border-color: #b8dcc8;
}
.stat.highlight .value {
  color: var(--success);
}

/* completed_unverified: the highlighted savings figures must not read as a
   validated "green" result -- a screenshotted headline shouldn't be mistaken
   for a passed verification. */
.stat.highlight.is-unverified {
  background: var(--caution-bg);
  border-color: var(--caution-border);
}
.stat.highlight.is-unverified .value {
  color: var(--caution);
}
.unverified-caption {
  display: block;
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--caution);
  text-transform: none;
  letter-spacing: normal;
}

/* ─── Download / integrity ─────────────────────────── */

.download-row {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--sp-4);
  margin: 0;
  padding: var(--sp-5) 0;
}

.btn-download-secondary {
  background: transparent;
  color: var(--accent);
  border: 1px solid var(--accent);
  box-shadow: none;
}

.btn-download-secondary:hover {
  background: var(--accent);
  color: #fff;
}

.download-row-diagnostic {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: var(--sp-4);
  padding-bottom: var(--sp-2);
}

.link-diagnostic {
  font-size: var(--text-sm);
  color: var(--color-text-muted, #6b7280);
  text-decoration: underline;
}

.link-diagnostic:hover {
  color: var(--color-primary, #2563eb);
}

.hint {
  font-size: var(--text-xs, 0.75rem);
  color: var(--color-text-muted, #9ca3af);
  margin: var(--sp-1) 0 0;
  text-align: center;
  max-width: 480px;
}

.files-card {
  padding: 0;
}

/* Animated collapse for <details>: keep body in layout, animate max-height */
.files-card > .files-card-body {
  display: block !important;
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transition:
    max-height 280ms cubic-bezier(0.2, 0.6, 0.2, 1),
    opacity 200ms ease;
}
.files-card[open] > .files-card-body {
  max-height: 1200px;
  opacity: 1;
  transition:
    max-height 320ms cubic-bezier(0.2, 0.6, 0.2, 1),
    opacity 240ms ease 40ms;
}
.files-card-inner {
  padding: var(--sp-2) 0 var(--sp-4);
}
.files-card-inner .file-list,
.files-card-inner h3 {
  margin-left: var(--sp-6);
  margin-right: var(--sp-6);
}
.files-card-inner h3 {
  margin-top: var(--sp-5);
}

.files-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-6);
  cursor: pointer;
  list-style: none;
  font-size: var(--text-base);
  font-weight: 700;
  color: var(--text);
  border-radius: var(--radius);
  transition: background 0.15s;
}

.files-summary::-webkit-details-marker {
  display: none;
}
.files-summary:hover {
  background: var(--bg-alt);
}

.files-summary::after {
  content: "";
  width: 10px;
  height: 10px;
  border-right: 2px solid var(--text-muted);
  border-bottom: 2px solid var(--text-muted);
  transform: rotate(45deg);
  margin-right: var(--sp-1);
  transition: transform 0.15s;
}

.files-card[open] .files-summary::after {
  transform: rotate(-135deg);
  margin-top: 4px;
}

.files-count {
  margin-left: auto;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-muted);
}

.integrity-pass {
  text-align: center;
  color: var(--success);
  font-size: var(--text-sm);
  font-weight: 600;
  margin-top: var(--sp-4);
  padding: var(--sp-3);
  background: var(--success-bg);
  border-radius: var(--radius-sm);
}

.integrity-fail {
  margin-top: var(--sp-4);
  padding: var(--sp-4);
  background: var(--error-bg);
  border: 1px solid #e8c0c0;
  border-radius: var(--radius-sm);
  color: var(--error);
  font-size: var(--text-sm);
}

.integrity-fail ul {
  margin: var(--sp-2) 0 0 var(--sp-5);
  padding: 0;
}
.integrity-fail li {
  margin-bottom: var(--sp-1);
}

/* ─── Count chip ───────────────────────────────────── */

.count-chip {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: var(--text-sm);
  font-weight: 700;
  line-height: 1.5;
  min-width: 24px;
  text-align: center;
  background: var(--bg-elevated);
  color: var(--text-secondary);
  flex-shrink: 0;
}

.count-chip.is-warning {
  background: var(--warning-bg);
  color: var(--warning);
}

.count-chip.is-error {
  background: var(--error-bg);
  color: var(--error);
}

.count-chip.is-success {
  background: var(--success-bg);
  color: var(--success);
}

.count-chip.is-muted {
  background: var(--bg-elevated);
  color: var(--text-muted);
  opacity: 0.6;
}

/* ─── Detail sections (inside Results Summary card) ── */

.detail-sections {
  border-top: 1px solid var(--border-light);
}

.detail-row-intro {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin-bottom: var(--sp-3);
}

/* ─── Integrity check list ─────────────────────────── */

.integrity-check-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.integrity-check-item {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
}

.integrity-check-icon {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
  margin-top: 7px;
  background: var(--success);
}

.integrity-check-item.failed .integrity-check-icon {
  background: var(--error);
}

.integrity-check-item div {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.integrity-check-item span {
  font-size: var(--text-sm);
  color: var(--text);
  line-height: 1.4;
}

.loaded-file-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.loaded-file-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  font-size: var(--text-sm);
  line-height: 1.4;
}

.anomaly-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.anomaly-item {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  font-size: var(--text-sm);
  line-height: 1.4;
}

.anomaly-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
  margin-top: 7px;
  background: var(--text-muted);
}

.anomaly-dot-warn {
  background: var(--warning);
}
.anomaly-dot-info {
  background: var(--text-muted);
}
.anomaly-dot-error {
  background: var(--error);
}

/* ─── Error file list (mirrors anomaly-list pattern) ─────────────────────── */

.error-file-item {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  font-size: var(--text-sm);
  line-height: 1.4;
}

.error-file-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
  margin-top: 7px;
  background: var(--error);
}

.error-file-item strong {
  font-weight: 600;
  color: var(--text);
}

/* ─── Integrity check failure message (inline under failed check) ─────────── */

.integrity-check-failure-message {
  font-size: var(--text-sm);
  color: var(--error);
  line-height: 1.4;
}

.integrity-issues {
  margin-top: var(--sp-4);
  padding: var(--sp-4);
  background: var(--error-bg);
  border: 1px solid #e8c0c0;
  border-radius: var(--radius-sm);
  color: var(--error);
  font-size: var(--text-sm);
}

.integrity-issues strong {
  display: block;
  margin-bottom: var(--sp-2);
  font-weight: 700;
}

.integrity-issues ul {
  margin: 0 0 0 var(--sp-5);
  padding: 0;
}

.integrity-issues li {
  margin-bottom: var(--sp-1);
}

/* ─── Job detail header ────────────────────────────── */

.job-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-6);
  margin-bottom: var(--sp-6);
}

.job-header h1 {
  margin-bottom: var(--sp-2);
}

.job-actions {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: wrap;
  justify-content: flex-end;
  flex-shrink: 0;
  padding-top: var(--sp-1);
}

/* ─── Needs support banner ─────────────────────────── */

.needs-support-banner {
  background: var(--warning-bg);
  border: 1px solid #dfc78a;
  border-radius: var(--radius);
  padding: var(--sp-5);
  margin-bottom: var(--sp-6);
  display: flex;
  align-items: center;
  gap: var(--sp-5);
}

.needs-support-banner .banner-icon {
  font-size: 1.4rem;
  line-height: 1;
  flex-shrink: 0;
}
.needs-support-banner .banner-text {
  flex: 1;
  margin: 0;
  color: var(--text);
  font-size: var(--text-sm);
}

.needs-input-files {
  margin-bottom: var(--sp-6);
}

/* ─── Needs review banner (completed_unverified -- terminal, not self-healing) ── */

.needs-review-banner {
  background: var(--caution-bg);
  border: 1px solid var(--caution-border);
  border-radius: var(--radius);
  padding: var(--sp-5);
  margin-bottom: var(--sp-6);
  display: flex;
  align-items: flex-start;
  gap: var(--sp-5);
}

.needs-review-banner .banner-icon {
  font-size: 1.4rem;
  line-height: 1;
  flex-shrink: 0;
  color: var(--caution);
}
.needs-review-banner-body {
  flex: 1;
}
.needs-review-banner .banner-text {
  flex: 1;
  margin: 0;
  color: var(--text);
  font-size: var(--text-sm);
}
.needs-review-banner .banner-text strong {
  color: var(--caution);
}

/* ─── Q&A blocked / review banners (job <-> question link) ── */

.qa-blocked-banner {
  background: var(--warning-bg);
  border: 1px solid #dfc78a;
  border-radius: var(--radius);
  padding: var(--sp-5);
  margin-bottom: var(--sp-6);
  display: flex;
  align-items: flex-start;
  gap: var(--sp-5);
}
.qa-blocked-banner .banner-icon {
  font-size: 1.4rem;
  line-height: 1;
  flex-shrink: 0;
}
.qa-blocked-body {
  flex: 1;
}
.qa-blocked-banner .banner-text {
  flex: 1;
  margin: 0;
  color: var(--text);
  font-size: var(--text-sm);
}
.qa-blocked-links {
  margin: var(--sp-2) 0 0;
}
.qa-blocked-links a {
  color: var(--warning);
  font-weight: 600;
  font-size: var(--text-sm);
  text-decoration: none;
}
.qa-blocked-links a:hover {
  text-decoration: underline;
}

.qa-review-banner {
  background: var(--success-bg);
  border: 1px solid var(--border-light);
  border-radius: var(--radius);
  padding: var(--sp-5);
  margin-bottom: var(--sp-6);
  display: flex;
  align-items: center;
  gap: var(--sp-5);
}
.qa-review-banner .banner-icon {
  font-size: 1.4rem;
  line-height: 1;
  flex-shrink: 0;
  color: var(--success);
}
.qa-review-banner .banner-text {
  flex: 1;
  margin: 0;
  color: var(--text);
  font-size: var(--text-sm);
}

.unlock-file-item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--border-light);
}
.unlock-file-item:last-child {
  border-bottom: none;
}
.unlock-file-item .loaded-file-name {
  flex: 1;
}

.tag-warning {
  background: var(--warning-bg);
  color: var(--warning);
}

/* ─── Unlock modal ─────────────────────────────────── */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}

.modal-box {
  background: var(--bg-card);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  padding: var(--sp-8);
  width: 100%;
  max-width: 420px;
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
}

.modal-title {
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--text);
  margin: 0;
}

.modal-subtitle {
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin: 0;
  word-break: break-all;
}

.modal-body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.modal-label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-secondary);
}

.modal-input {
  width: 100%;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--text-base);
  font-family: inherit;
  color: var(--text);
  background: var(--bg);
  transition:
    border-color 0.15s,
    box-shadow 0.15s;
}

.modal-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(47, 111, 168, 0.2);
}

.modal-error {
  font-size: var(--text-sm);
  color: var(--error);
  margin: 0;
  padding: var(--sp-2) var(--sp-3);
  background: var(--error-bg);
  border-radius: var(--radius-sm);
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-3);
}

.btn-secondary {
  background: var(--bg-elevated);
  color: var(--text-secondary);
  border: 1px solid var(--border);
}

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

.btn-primary {
  background: var(--primary);
  color: var(--on-primary);
  border: 1px solid transparent;
}

.btn-primary:hover {
  background: var(--primary-hover);
}

.btn-primary:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* ─── File counts ──────────────────────────────────── */

/* ─── Processing ───────────────────────────────────── */

.processing-time {
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin-top: var(--sp-1);
}

.spinner {
  display: inline-block;
  width: 20px;
  height: 20px;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
  margin-left: var(--sp-2);
  vertical-align: middle;
}

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

.processing-pulse {
  color: var(--text-secondary);
}

/* ─── Processing banner ────────────────────────────── */

@keyframes processing-bg-pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.7;
  }
}

.processing-banner {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  background: var(--bg);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  padding: var(--sp-4) var(--sp-5);
  margin-top: var(--sp-4);
  animation: processing-bg-pulse 2.4s ease-in-out infinite;
}

.processing-banner .processing-spinner {
  width: 28px;
  height: 28px;
  border-width: 3px;
  flex-shrink: 0;
  margin-left: 0;
}

.processing-banner-text {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}

.processing-banner-text strong {
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--text);
  line-height: 1.2;
}

.processing-banner-text span {
  font-size: var(--text-sm);
  color: var(--text-muted);
}

/* ─── Upload form ──────────────────────────────────── */

.upload-form {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--sp-8);
  margin-top: var(--sp-6);
  box-shadow: var(--shadow);
}

.field {
  margin-bottom: var(--sp-8);
}

/* Lays a set of related fields out horizontally instead of stacking them
   full-width -- used by directory_search.html (specialty/taxonomy/network,
   state/city/zip, etc.) to group several ".field .field-inline" children
   into one responsive row. Mirrors the flex-wrap + fixed-gap pattern
   already used by .directory-search-facets in directory_search.css. */
.field-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-6);
  margin-bottom: var(--sp-8);
}

/* Each field inside a .field-group also carries the base .field class;
   override its stacked margin-bottom since the group itself owns the
   bottom spacing, and let it flex-wrap at a sane minimum width. */
.field-inline {
  flex: 1 1 200px;
  min-width: 200px;
  margin-bottom: 0;
}

.field label {
  display: block;
  font-weight: 700;
  font-size: var(--text-base);
  margin-bottom: var(--sp-2);
  color: var(--text);
}

/* ─── Unified form controls ─────────────────────────
   Single source of truth for text/email/password/number/search inputs,
   textareas, and selects across .field (upload/proposal forms),
   .form-field (admin/users forms), the bare .form-input class, and
   .auth-form (login/MFA/password-reset forms). Do not reintroduce
   per-context padding/font-size -- add new contexts to this selector
   union instead. */
.field input[type="text"],
.field input[type="email"],
.field input[type="password"],
.field input[type="number"],
.field input[type="search"],
.field select,
.field textarea,
.form-field input[type="text"],
.form-field input[type="email"],
.form-field input[type="password"],
.form-field input[type="number"],
.form-field input[type="search"],
.form-field select,
.form-field textarea,
.form-field .form-input,
.form-input,
.auth-form input[type="text"],
.auth-form input[type="email"],
.auth-form input[type="password"],
.auth-form select,
.auth-form textarea {
  width: 100%;
  box-sizing: border-box;
  min-height: var(--control-h);
  padding: 12px 16px;
  font-size: var(--text-base);
  font-family: inherit;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  /* background-color (not the `background` shorthand) so this rule never
       clobbers background-image -- .form-field .form-input has higher
       specificity than .form-field select and would otherwise reset the
       chevron below back to none for <select class="form-input">. */
  background-color: var(--bg);
  color: var(--text);
  transition: border-color 0.15s;
}

/* Custom chevron so native <select> rendering is identical across OSes.
   Stroke color is the resolved hex of --text-muted (#565a6a) -- CSS
   custom properties don't reliably resolve inside external data-URI
   SVG backgrounds, so it's hardcoded here and must be kept in sync if
   --text-muted ever changes. */
.field select,
.form-field select,
.auth-form select {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='%23565a6a' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 12px;
  padding-right: 40px;
}

/* Shared focus ring for every unified control (including .select-compact) */
.field input[type="text"]:focus,
.field input[type="email"]:focus,
.field input[type="password"]:focus,
.field input[type="number"]:focus,
.field input[type="search"]:focus,
.field select:focus,
.field textarea:focus,
.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus,
.form-field .form-input:focus,
.form-input:focus,
.auth-form input:focus,
.auth-form select:focus,
.auth-form textarea:focus,
.select-compact:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(47, 111, 168, 0.2);
}

/* Styled file input for tool upload forms — consistent across all tool pages */
input[type="file"] {
  display: block;
  width: 100%;
  padding: var(--sp-2) var(--sp-3);
  border: 2px dashed var(--border);
  border-radius: var(--radius);
  background: var(--bg-alt);
  color: var(--text);
  font-size: var(--text-sm);
  cursor: pointer;
  transition: border-color 0.15s;
  box-sizing: border-box;
}
input[type="file"]:hover {
  border-color: var(--accent);
}
input[type="file"]:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(47, 111, 168, 0.2);
}
/* Drop-zone hidden inputs: override global styles — these are invisible overlays */
.drop-zone input[type="file"],
.ws-drop-zone input[type="file"],
.ws2-dropzone input[type="file"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: none;
  border-radius: 0;
  background: none;
  opacity: 0;
  cursor: pointer;
  box-shadow: none;
}
/* Hidden file inputs (e.g. triggered by custom buttons) */
.file-input-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  overflow: hidden;
}
.field input[type="file"] {
  font-size: var(--text-sm);
}

.field-error {
  color: var(--error);
  font-size: var(--text-sm);
  margin: var(--sp-1) 0 var(--sp-2);
}

.help {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin-bottom: var(--sp-2);
}

/* ─── Upload notice (password-prompt mode) ─────────── */

.upload-notice {
  background: var(--warning-bg);
  border: 1px solid #e8d8a0;
  border-radius: var(--radius-sm);
  padding: var(--sp-3) var(--sp-5);
  margin-top: var(--sp-4);
  font-size: var(--text-sm);
  color: var(--warning);
}

.upload-notice p {
  margin: 0;
}

/* ─── Password show/hide toggle ─────────────────────── */

.pw-wrap {
  position: relative;
  display: flex;
  align-items: center;
}

.pw-wrap input[type="password"],
.pw-wrap input[type="text"] {
  width: 100%;
  padding-right: 4rem;
}

.pw-toggle {
  position: absolute;
  right: var(--sp-3);
  background: none;
  border: none;
  padding: var(--sp-1) var(--sp-2);
  cursor: pointer;
  color: var(--text-secondary);
  font-size: var(--text-sm);
  line-height: 1;
  user-select: none;
  white-space: nowrap;
}

.pw-toggle:hover {
  color: var(--text);
}
.pw-toggle:focus-visible {
  outline: 2px solid var(--focus-color);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* ─── Upload workflow note (below submit) ──────────── */

.upload-workflow-note {
  margin-top: var(--sp-5);
  background: var(--bg-alt);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  padding: var(--sp-4) var(--sp-5);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.6;
}

.upload-workflow-note strong {
  color: var(--text);
}

/* ─── Help details / cheat-sheet ──────────────────── */

.help-summary {
  cursor: pointer;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--accent);
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) 0;
  user-select: none;
}

.help-summary::-webkit-details-marker {
  display: none;
}

.help-summary::before {
  content: "\25B6";
  font-size: var(--text-sm);
  transition: transform 0.15s;
  color: var(--accent);
}

details[open] .help-summary::before {
  transform: rotate(90deg);
}

.help-summary:hover {
  color: var(--accent-hover);
}

.help-details {
  margin-top: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  background: var(--bg-alt);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.6;
}

.help-details p {
  margin: 0 0 var(--sp-2);
}
.help-details p:last-child {
  margin-bottom: 0;
}

.help-details ul {
  margin: var(--sp-1) 0 var(--sp-2) var(--sp-5);
  padding: 0;
}

.help-details ul li {
  margin-bottom: var(--sp-1);
}

.help-details code {
  font-family: ui-monospace, monospace;
  font-size: var(--text-sm);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 1px 5px;
}

.provider-format-note {
  margin-top: 0;
}

/* ─── Inline status help text ─────────────────────── */

.status-help-text {
  display: block;
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin-top: var(--sp-2);
  line-height: 1.5;
}

.status-help-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--border-light);
  line-height: 1.5;
}

.status-help-legend span {
  white-space: nowrap;
}

.status-help-legend strong {
  color: var(--text-secondary);
  font-weight: 600;
}

.status-error-reasons {
  margin-top: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  background: var(--bg-alt);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.6;
}

.status-error-reasons strong {
  display: block;
  font-weight: 700;
  color: var(--text);
  margin-bottom: var(--sp-2);
}

.status-error-reasons ul {
  margin: 0 0 var(--sp-3) var(--sp-5);
  padding: 0;
}

.status-error-reasons li {
  margin-bottom: var(--sp-1);
}

.status-error-reasons .support-link {
  color: var(--accent);
  text-decoration: none;
  font-weight: 600;
}

.status-error-reasons .support-link:hover {
  text-decoration: underline;
}

/* ─── Alert banners ────────────────────────────────── */

.alert {
  padding: var(--sp-3) var(--sp-5);
  border-radius: var(--radius-sm);
  margin: var(--sp-4) 0;
  font-size: var(--text-sm);
}

.alert-error {
  background: var(--error-bg);
  border: 1px solid #e8c0c0;
  color: var(--error);
}

/* ─── Button row ───────────────────────────────────── */

.button-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-2);
}

.btn-secondary {
  background: var(--bg);
  color: var(--text-secondary);
  border: 1px solid var(--border);
}

/* ─── Drop zone ────────────────────────────────────── */

.drop-zone {
  border: 2px dashed var(--border);
  border-radius: var(--radius);
  padding: var(--sp-8) var(--sp-6);
  text-align: center;
  transition:
    border-color 0.2s,
    background 0.2s;
  cursor: pointer;
  position: relative;
}

.drop-zone:hover,
.drop-zone.dragover {
  border-color: var(--accent);
  background: var(--accent-light);
}

.drop-zone input[type="file"] {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}

.selected-files {
  margin-top: var(--sp-3);
  text-align: left;
}
.selected-files ul {
  list-style: none;
  padding: 0;
}

.selected-files li {
  padding: var(--sp-1) 0;
  font-size: var(--text-sm);
  color: var(--text);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.selected-files li::before {
  content: "";
  width: 6px;
  height: 6px;
  background: var(--success);
  border-radius: 50%;
  flex-shrink: 0;
}

.size-info {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin-top: var(--sp-2);
}

.size-warning {
  font-size: var(--text-sm);
  color: var(--warning);
  background: var(--warning-bg);
  border: 1px solid #dfc78a;
  border-radius: var(--radius-sm);
  padding: var(--sp-3) var(--sp-4);
  margin-top: var(--sp-2);
  font-weight: 600;
}

/* ─── Form actions ─────────────────────────────────── */

.form-actions {
  margin-top: var(--sp-4);
}

/* Generic text input matching textarea style -- sizing lives in the
   unified form-control rule above; this just sets display + placeholder. */
.form-input {
  display: block;
}

.form-input::placeholder {
  color: var(--text-muted);
}

/* Reusable field label */
.form-label {
  display: block;
  font-weight: 600;
  font-size: var(--text-sm);
  color: var(--text);
  margin-bottom: var(--sp-1);
}

/* Stacked form field (label + input) with consistent bottom spacing.
   Control sizing (padding/font/border/focus) lives in the unified
   form-control rule above -- do not reimpose it here. */
.form-field {
  margin-bottom: var(--sp-4);
}

/* ─── Platform confirmation modal ─────────────────── */

.platform-modal-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 9000;
  align-items: center;
  justify-content: center;
}

.platform-modal-backdrop.is-open {
  display: flex;
}

.platform-modal {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  padding: var(--sp-6);
  max-width: 420px;
  width: calc(100% - var(--sp-8));
}

.platform-modal-title {
  font-size: var(--text-base);
  font-weight: 700;
  color: var(--navy);
  margin-bottom: var(--sp-3);
}

.platform-modal-message {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.6;
  margin-bottom: var(--sp-6);
}

.platform-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--sp-3);
}

/* ─── Workspace settings modal ─────────────────────────
   Account settings and the app-menu pages open in this native <dialog> inside
   /workspace (workspace_v2_shell.html, workspace-settings-modal.js). It hosts
   the page in a same-origin frame (embed mode), so the frame content keeps the
   light shell tokens and the bar uses them too. Full screen on a phone. */

.ws2-settings-modal {
  width: 90vw;
  height: 90vh;
  max-width: none;
  max-height: none;
  /* The global reset zeroes margins; a native dialog centres itself only
     with margin: auto. */
  margin: auto;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.4);
  overflow: hidden;
}

.ws2-settings-modal[open] {
  display: flex;
  flex-direction: column;
}

.ws2-settings-modal::backdrop {
  background: rgba(0, 0, 0, 0.55);
}

.ws2-settings-modal-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--border);
  background: var(--bg);
  flex: 0 0 auto;
}

.ws2-settings-modal-title {
  margin: 0;
  font-size: var(--text-base);
  font-weight: 700;
  color: var(--navy);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ws2-settings-modal-close {
  flex: 0 0 auto;
  padding: var(--sp-2) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  color: var(--text);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
}

.ws2-settings-modal-close:hover {
  background: var(--border-light);
}

.ws2-settings-modal-close:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

.ws2-settings-modal-frame {
  flex: 1 1 auto;
  width: 100%;
  min-height: 0;
  border: 0;
  background: var(--bg-alt);
}

@media (max-width: 640px) {
  .ws2-settings-modal {
    width: 100vw;
    height: 100vh;
    height: 100dvh;
    border: 0;
    border-radius: 0;
  }
}

/* Embed mode (base_main.html with request.state.embed): the page is shown
   inside the modal frame with no site chrome, so it needs less outer padding. */
body.embed-shell main {
  padding-top: var(--sp-6);
}

/* ─── Textarea ─────────────────────────────────────── */

textarea {
  width: 100%;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--text-base);
  font-family: inherit;
  resize: none;
  overflow: hidden;
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  transition: border-color 0.15s;
}

textarea::placeholder {
  color: var(--text-muted);
  line-height: 1.6;
}

textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(47, 111, 168, 0.2);
}

/* ─── Feedback (per-job) ───────────────────────────── */

.feedback-divider {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  margin-top: var(--sp-12);
  margin-bottom: var(--sp-6);
}

.feedback-divider-label {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--text-muted);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.feedback-divider-rule {
  flex: 1;
  height: 1px;
  background: var(--border);
}

.feedback-section .feedback-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.feedback-section .feedback-form textarea {
  width: 100%;
}
.feedback-section .feedback-form .btn {
  align-self: flex-start;
}

.feedback-history-label {
  margin-top: var(--sp-8);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--border);
  font-size: var(--text-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-bottom: var(--sp-3);
}

.feedback-history {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.feedback-item {
  padding: var(--sp-4) var(--sp-5);
  background: var(--bg-alt);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.feedback-item .feedback-meta {
  margin: 0;
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--border-light);
  font-size: var(--text-sm);
  color: var(--text-muted);
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  flex-wrap: wrap;
  line-height: 1.3;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.feedback-item .feedback-status {
  font-weight: 700;
  white-space: nowrap;
}

.feedback-item .feedback-status-submitted {
  color: var(--text-secondary);
}
.feedback-item .feedback-status-resolved {
  color: var(--success);
}

.feedback-item .feedback-meta-sep {
  color: var(--border);
}
.feedback-item .feedback-meta-date {
  font-variant-numeric: tabular-nums;
}

.feedback-item .feedback-auto-badge {
  display: inline-block;
  padding: 1px 6px;
  margin-left: 2px;
  border-radius: 4px;
  background: var(--bg-elevated);
  color: var(--text-muted);
  font-size: var(--text-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  line-height: 1.4;
}

.feedback-item .feedback-text {
  margin: 0;
  font-size: var(--text-base);
  line-height: 1.55;
  color: var(--text);
}

.resolution {
  color: var(--success);
  font-style: italic;
  font-size: var(--text-sm);
  margin: 0;
}

/* ─── Feedback resolution card & reply thread ─────── */

.fb-resolution-card {
  margin-top: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: rgba(34, 160, 98, 0.07);
  border-left: 3px solid var(--success);
  border-radius: var(--radius-sm);
}

.fb-resolution-header {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-2);
}

.fb-resolution-icon {
  color: var(--success);
  font-weight: 700;
  font-size: var(--text-sm);
}

.fb-resolution-label {
  font-weight: 700;
  font-size: var(--text-sm);
  color: var(--success);
}

.fb-resolution-date {
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin-left: auto;
  white-space: nowrap;
}

.fb-resolution-text {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.6;
  margin: 0 0 var(--sp-2) 0;
}

.fb-download-btn {
  margin-top: var(--sp-1);
}

.fb-thread {
  margin-top: var(--sp-3);
}

/* Team messages: warm accent tint (matches admin styling convention) */
.thread-team {
  background: rgba(47, 111, 168, 0.05);
}

/* Client messages: neutral alt-bg (same as thread-user default) */
.thread-client {
  background: var(--bg-alt);
}

.thread-author-team {
  color: var(--accent);
}

.thread-author-client {
  color: var(--success);
}

.fb-reply-section {
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--border-light);
}

.fb-reply-prompt {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin: 0 0 var(--sp-2) 0;
}

.fb-reply-form textarea {
  width: 100%;
  box-sizing: border-box;
}

.fb-reply-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-2);
  flex-wrap: wrap;
}

/* ─── Feedback (global page) ──────────────────────── */

.feedback-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  margin-top: var(--sp-8);
}

.feedback-ticket {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--sp-5);
}

.ticket-header {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-3);
  row-gap: var(--sp-2);
  margin-bottom: var(--sp-3);
}

.ticket-source {
  color: var(--text-secondary);
  font-size: var(--text-sm);
}

.ticket-source-link {
  color: var(--accent);
  text-decoration: none;
  font-size: var(--text-sm);
  font-weight: 700;
}

.ticket-source-link:hover {
  text-decoration: underline;
}

.ticket-date {
  color: var(--text-muted);
  font-size: var(--text-sm);
  margin-left: auto;
  white-space: nowrap;
}

.ticket-body {
  font-size: var(--text-sm);
  line-height: 1.6;
  white-space: pre-line;
  color: var(--text);
}

.ticket-body + .resolution {
  margin-top: var(--sp-2);
}

.ticket-meta {
  color: var(--text-muted);
  font-size: var(--text-sm);
  margin: var(--sp-2) 0 0;
}

.feedback-category {
  display: flex;
  gap: var(--sp-4);
  margin-bottom: var(--sp-4);
}

.category-option {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  cursor: pointer;
  font-weight: 600;
}

.category-option input[type="radio"] {
  accent-color: var(--accent);
}
.category-option:has(input:checked) span {
  color: var(--text);
}

/* ─── Questions ──────────────────────────────── */

.section-heading {
  margin-top: var(--sp-10);
  margin-bottom: var(--sp-4);
  font-size: var(--text-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}

.count-badge {
  background: var(--accent);
  color: white;
  font-size: var(--text-sm);
  font-weight: 700;
  padding: 2px 8px;
  border-radius: 10px;
  letter-spacing: 0;
  text-transform: none;
}

.count-badge.count-muted {
  background: var(--text-muted);
}

.count-badge.count-attention {
  background: var(--warning, #e6a817);
}

.form-hint {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin-bottom: var(--sp-4);
  line-height: 1.5;
}

.questions-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.question-card {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--sp-5);
}

.question-open {
  border-left: 3px solid var(--accent);
}

.question-answered {
  border-left: 3px solid var(--success);
}

.question-needs_attention {
  border-left: 3px solid var(--warning, #e6a817);
}

.question-in_progress {
  border-left: 3px solid var(--accent);
}

.question-closed {
  border-left: 3px solid var(--text-muted);
  opacity: 0.7;
}

.question-header {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-3);
  row-gap: var(--sp-2);
  margin-bottom: var(--sp-3);
}

.question-asker {
  font-weight: 700;
  font-size: var(--text-sm);
  color: var(--text);
}

.question-text {
  font-size: var(--text-sm);
  line-height: 1.6;
  white-space: pre-line;
  color: var(--text);
  margin-bottom: var(--sp-3);
}

.answer-form textarea {
  font-size: var(--text-sm);
}

.answer-by {
  font-weight: 700;
  font-size: var(--text-sm);
  color: var(--success);
}

.answer-text {
  font-size: var(--text-sm);
  line-height: 1.6;
  white-space: pre-line;
  color: var(--text);
  margin-top: var(--sp-2);
}

/* ─── Thread ─────────────────────────────────── */

.thread {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  margin: var(--sp-3) 0;
  padding-left: var(--sp-4);
  border-left: 2px solid var(--border-light);
}

.thread-message {
  padding: var(--sp-3);
  border-radius: var(--radius-sm);
  background: var(--bg-alt);
}

.thread-admin {
  background: rgba(47, 111, 168, 0.05);
}

.thread-msg-header {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-bottom: var(--sp-1);
}

.thread-author {
  font-weight: 700;
  font-size: var(--text-sm);
}

.thread-author-admin {
  color: var(--accent);
}

.thread-author-user {
  color: var(--success);
}

.thread-msg-text {
  font-size: var(--text-sm);
  line-height: 1.6;
  white-space: pre-line;
  color: var(--text);
}

/* Markdown-rendered Q&A message bodies (question body + thread messages).
   white-space: normal so <p> tags don't create extra gaps;
   p margins give the breathing room that pre-line used to provide. */
.qa-msg-body {
  font-size: var(--text-sm);
  line-height: 1.6;
  white-space: normal;
  color: var(--text);
  margin-bottom: var(--sp-3);
}
.qa-msg-body p {
  margin: 0 0 var(--sp-2) 0;
}
.qa-msg-body p:last-child {
  margin-bottom: 0;
}
.qa-msg-body strong {
  font-weight: 600;
}
.qa-msg-body em {
  font-style: italic;
}
.qa-msg-body code {
  font-family: monospace;
  background: var(--bg-alt);
  padding: 0.1em 0.3em;
  border-radius: 2px;
  font-size: 1em;
}
.qa-msg-body ul,
.qa-msg-body ol {
  padding-left: var(--sp-4);
  margin: 0 0 var(--sp-2) 0;
}
.qa-msg-body a {
  color: var(--accent);
  text-decoration: underline;
}

.reply-form {
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--border-light);
}

.reply-form textarea {
  font-size: var(--text-sm);
}

.status-controls {
  display: flex;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--border-light);
}

.answer-form-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-2);
}

.answer-form-actions .btn:last-child {
  margin-left: auto;
}

.file-input-hidden {
  display: none;
}

.btn-outline {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-secondary);
}

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

.file-upload-names {
  font-size: var(--text-xs, 0.75rem);
  color: var(--text-muted);
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.answer-header {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-2);
}

.answer-attachments {
  margin-top: var(--sp-3);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.attachment-link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  font-size: var(--text-sm);
  color: var(--accent);
  text-decoration: none;
  padding: var(--sp-1) var(--sp-3);
  background: var(--bg);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
}

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

.section-divider {
  border: none;
  border-top: 1px solid var(--border-light);
  margin: var(--sp-10) 0;
}

.section-title {
  font-size: var(--text-lg, 1.125rem);
  font-weight: 700;
  color: var(--text);
  margin-bottom: var(--sp-5);
}

/* ─── Error / file lists ──────────────────────────── */

.error-box {
  background: var(--error-bg);
  border: 1px solid #e8c0c0;
  border-radius: var(--radius-sm);
  padding: var(--sp-4) var(--sp-5);
  margin: var(--sp-4) 0;
  color: var(--error);
  font-size: var(--text-sm);
}

.success-box {
  background: var(--success-bg);
  border: 1px solid #b3dfc9;
  border-radius: var(--radius-sm);
  padding: var(--sp-4) var(--sp-5);
  margin: var(--sp-4) 0;
  color: var(--success);
  font-size: var(--text-sm);
}

.dev-notice {
  background: var(--caution-bg);
  border: 1px solid var(--caution-border);
  border-left: 4px solid var(--caution);
  padding: var(--sp-4);
  border-radius: var(--radius);
  color: #444e62; /* PU-22: orange text on the tint was 3.2:1 */
  font-size: var(--text-sm);
  font-weight: 500;
  margin: var(--sp-4) 0;
  display: block;
}

.error-list li {
  color: var(--error);
  margin-left: var(--sp-6);
  margin-bottom: var(--sp-1);
}

.file-list {
  list-style: none;
}
.file-list li {
  padding: var(--sp-2) 0;
  font-size: var(--text-sm);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.file-list li + li {
  border-top: 1px solid var(--border-light);
}
.file-list.muted li {
  color: var(--text-muted);
}

.tag {
  display: inline-block;
  padding: 4px 10px;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin-left: auto;
  vertical-align: middle;
  flex-shrink: 0;
  font-weight: 600;
}

/* A question's context can be a whole paragraph or an unbroken URL. The base
   .tag is flex-shrink:0 (right for short labels), so inside the wrapping
   .question-header it kept its full max-content width and stretched the admin
   dashboard to thousands of px on a phone. Let it shrink to the card and wrap
   anywhere; the full text stays in the title attribute. */
.question-header .tag {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 100%;
  overflow-wrap: anywhere;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}

/* The global input[type="file"] rule (display:block; width:100%) outranks
   .file-input-hidden, so the reply form's "Attach Files" input rendered as a
   full-width absolute box that poked 44px past a 390px page. Re-assert the
   1px visually-hidden box for the label-wrapped input. */
.file-upload-label input.file-input-hidden {
  width: 1px;
  height: 1px;
  padding: 0;
  border: 0;
}

.question-context {
  max-width: 20rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-left: 0;
}

.tag-linked-job {
  background: var(--accent-light);
  border-color: var(--accent);
  color: var(--accent);
  text-decoration: none;
  cursor: pointer;
}
.tag-linked-job:hover {
  background: var(--accent);
  color: white;
  text-decoration: none;
}

/* ─── Empty state ──────────────────────────────────── */

.empty-state {
  text-align: center;
  padding: var(--sp-12) var(--sp-6);
  color: var(--text-secondary);
  font-size: var(--text-md);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}

.empty-state a {
  color: var(--accent);
  font-weight: 700;
}

/* ─── Dashboard footer link ───────────────────────────
   Content-area link (light background), distinct from the top-bar's own
   link styling (near-white text meant for the dark bar). */
.dashboard-footer-link {
  color: var(--accent);
  font-weight: 700;
  text-decoration: none;
  transition: color 0.15s;
}

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

/* ─── Info box ─────────────────────────────────────── */

.info-box {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--sp-6);
  margin-top: var(--sp-8);
  box-shadow: var(--shadow-sm);
}

.info-box h3 {
  margin: 0 0 var(--sp-4);
  font-size: var(--text-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}

/* ─── Agent message ────────────────────────────────── */

.agent-message {
  background: var(--bg-alt);
  border-left: 3px solid var(--accent);
  padding: var(--sp-4) var(--sp-5);
  margin-top: var(--sp-4);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.agent-message h4 {
  margin-bottom: var(--sp-1);
  color: var(--text);
  font-size: var(--text-base);
}

/* ─── Hub page ─────────────────────────────────────── */

.tool-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: var(--sp-5);
}

.tool-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  padding: var(--sp-6);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  text-decoration: none;
  color: var(--text);
  transition:
    border-color 0.15s,
    box-shadow 0.15s;
}

a.tool-card:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow);
}

.tool-icon {
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-alt);
  border-radius: var(--radius-sm);
  color: var(--accent);
}

.tool-info h2 {
  font-size: var(--text-md);
  font-weight: 700;
  color: var(--navy);
  margin-bottom: var(--sp-1);
}

.tool-info p {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.5;
}

.tool-status {
  display: inline-block;
  padding: 4px 12px;
  border-radius: 999px;
  font-size: var(--text-sm);
  font-weight: 700;
  width: fit-content;
}

.tool-status-active {
  background: var(--success-bg);
  color: var(--success);
}

.tool-status-planned {
  background: var(--bg-elevated);
  color: var(--text-muted);
}

/* ─── Print ────────────────────────────────────────── */

@media print {
  header,
  footer,
  nav,
  .btn,
  .btn-download,
  .download-row,
  .back-link,
  form,
  .actions,
  .feedback-history,
  .agent-message,
  .btn-danger {
    display: none !important;
  }
  body {
    background: #fff;
    color: #222;
    font-size: 11pt;
  }
  main {
    max-width: 100%;
    padding: 0;
    margin: 0;
  }
  .summary-grid {
    grid-template-columns: repeat(3, 1fr);
  }
  .stat,
  .card,
  .info-box,
  table {
    box-shadow: none;
    border: 1px solid #ddd;
  }
  .needs-support-banner {
    border: 2px solid #b8860b;
  }
  h1,
  h2 {
    color: #222;
  }
  a {
    color: #222 !important;
    text-decoration: none !important;
  }
}

/* ─── Responsive ───────────────────────────────────── */

@media (max-width: 640px) {
  main {
    padding: var(--sp-6) var(--sp-4) var(--sp-8);
  }
  nav {
    padding: var(--sp-3) var(--sp-4);
  }
  .summary-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  table {
    font-size: var(--text-sm);
  }
  thead th,
  tbody td {
    padding: 12px 10px;
  }
  .upload-form {
    padding: var(--sp-5);
  }
  .drop-zone {
    padding: var(--sp-6) var(--sp-4);
  }
  .stat .value {
    font-size: var(--text-md);
  }
  .btn-download {
    padding: 12px 20px;
    font-size: var(--text-base);
    width: 100%;
    justify-content: center;
  }
  .job-header {
    flex-direction: column;
    gap: var(--sp-3);
  }
  .job-actions {
    justify-content: flex-start;
  }
  .logo-divider,
  .logo-product {
    display: none;
  }
  .cost-summary-grid {
    grid-template-columns: 1fr 1fr;
  }
  .admin-charts .card canvas {
    height: 200px !important;
  }
}

/* ─── Admin Dashboard ─────────────────────────────── */

.admin-header {
  margin-bottom: var(--sp-8);
}

.admin-header h1 {
  margin-bottom: var(--sp-2);
}

.admin-header-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-bottom: var(--sp-8);
}

.admin-header-row h1 {
  margin-bottom: 0;
}

/* Inline status text (not badges) */
.text-success {
  color: var(--success);
  font-weight: 600;
}
.text-warning {
  color: var(--warning);
  font-weight: 600;
}
.text-error {
  color: var(--error);
  font-weight: 600;
}

/* Compact inline role/action form in table cells */
.table-action-form {
  display: flex;
  gap: var(--sp-2);
  align-items: center;
}

/* Tap targets (PU-05). A link that stands alone (its own table cell, its own
   line, a nav row) or a checkbox row is a 44px control; a link inside a
   sentence stays inline. */
.tap-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

/* The same control on its own line under the text it belongs to. */
.tap-link-block {
  display: flex;
  width: fit-content;
}

.tap-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  min-height: 44px;
  cursor: pointer;
}

/* Smaller sibling of the unified control system (inline table-row selects) --
   shares border/radius/chevron/focus with .field/.form-field selects, just
   at --control-h-sm instead of --control-h. */
.select-compact {
  min-height: var(--control-h-sm);
  font-size: var(--text-sm);
  padding: 9px 36px 9px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--text);
  font-family: inherit;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='%23565a6a' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 10px;
}

.admin-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--sp-4);
  margin-bottom: var(--sp-8);
}

.admin-card {
  text-decoration: none;
  display: block;
  padding: var(--sp-5);
  transition:
    border-color 0.15s,
    box-shadow 0.15s;
}

.admin-card h3 {
  margin-top: 0;
}

.admin-card:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow);
}

.admin-section-heading {
  margin-top: var(--sp-2);
  margin-bottom: var(--sp-4);
}

.cost-summary-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--sp-4);
  margin-bottom: var(--sp-8);
}

.cost-card {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--sp-5) var(--sp-6);
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  box-shadow: var(--shadow-sm);
}

/* Label / value / sub line up across the row of cards even when one label
   wraps to two lines (subgrid; browsers without it keep the flex column). */
@supports (grid-template-rows: subgrid) {
  .cost-card {
    display: grid;
    grid-row: span 3;
    grid-template-rows: subgrid;
    align-content: start;
  }
}

.cost-card-label {
  font-size: var(--text-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

.cost-card-value {
  font-size: var(--text-2xl);
  font-weight: 800;
  color: var(--navy);
  letter-spacing: -0.02em;
  line-height: 1.2;
}

.cost-card-sub {
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

.admin-charts {
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
}

.admin-charts .card canvas {
  max-height: 280px;
  margin-bottom: var(--sp-4);
}

.admin-cost-table {
  margin-top: var(--sp-4);
}

.admin-cost-table .total-row td {
  border-top: 2px solid var(--border);
  padding-top: var(--sp-3);
}

/* ─── Telemetry banner ────────────────────────────── */

.telemetry-banner {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 8000;
  background: var(--bg-elevated);
  border-top: 1px solid var(--border-light);
  padding: var(--sp-2) var(--sp-6);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-4);
  font-size: var(--text-sm); /* 13px */
  color: var(--text-muted);
  line-height: 1.4;
}

.telemetry-banner-close {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-muted);
  font-size: 1rem;
  line-height: 1;
  padding: 0 var(--sp-1);
  flex-shrink: 0;
  opacity: 0.7;
  transition: opacity 0.15s;
}

.telemetry-banner-close:hover {
  opacity: 1;
}

/* ─── Telemetry admin ─────────────────────────────── */

.telemetry-stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--sp-4);
  margin-bottom: var(--sp-8);
}

.telemetry-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
  margin-top: var(--sp-4);
}

.telemetry-table th,
.telemetry-table td {
  text-align: left;
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--border-light);
}

.telemetry-table th {
  font-weight: 600;
  color: var(--text-secondary);
  font-size: var(--text-sm);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: var(--bg-alt);
}

.telemetry-table tbody tr:hover {
  background: var(--bg-alt);
}

.telemetry-bar {
  display: inline-block;
  height: 10px;
  border-radius: 3px;
  background: var(--accent);
  min-width: 2px;
  vertical-align: middle;
}

.telemetry-count {
  font-weight: 600;
  color: var(--navy);
  min-width: 3rem;
  display: inline-block;
}

/* ─── Auth pages ─────────────────────────────────── */

/* ─── Error page ────────────────────────────────── */

.error-page {
  max-width: 560px;
  margin: 60px auto;
  text-align: center;
  padding: 0 var(--sp-4);
}

.error-page-code {
  font-size: 3.5rem;
  font-weight: 800;
  color: var(--text-muted);
  line-height: 1;
  margin-bottom: var(--sp-3);
  letter-spacing: -2px;
}

.error-page-title {
  font-size: var(--text-xl);
  font-weight: 700;
  margin: 0 0 var(--sp-2);
  text-wrap: balance;
}

.error-page-message {
  color: var(--text-secondary);
  font-size: 1rem;
  margin: 0 0 var(--sp-6);
  line-height: 1.65;
  text-wrap: pretty;
}

.error-page-actions {
  display: flex;
  gap: var(--sp-3);
  justify-content: center;
  flex-wrap: wrap;
}

/* Staff error page buttons: 44px tall, 16px text (the small button is 40px / 14px). */
.error-page-actions .btn-sm {
  min-height: 44px;
  font-size: 1rem;
}

.error-support-box {
  background: var(--success-bg);
  border: 1px solid rgba(26, 138, 92, 0.2);
  border-radius: var(--radius-sm);
  padding: var(--sp-4) var(--sp-5);
  margin-bottom: var(--sp-6);
  text-align: left;
}

.error-support-box p {
  margin: 0;
  font-size: 1rem;
  text-wrap: pretty;
  color: var(--success);
  line-height: 1.55;
}

.error-support-box code {
  font-family: ui-monospace, SFMono-Regular, monospace;
  font-weight: 600;
  background: transparent;
  padding: 1px 5px;
  border-radius: 3px;
}

/* PU-12 / PU-22: the error pages and the unsubscribe page use the brand
   palette (docs/reference/ui-gate-rules.json) instead of the shell's own
   greys and greens. The green box text is 6:1+ on its tint, and the
   correlation id sits on white in ink (the old green-on-green id was 4.26:1). */
.error-page-code {
  color: #565f73;
}
.error-page-title {
  color: #182038;
}
.error-page-message {
  color: #444e62;
  text-wrap: pretty;
}
.error-page-actions .btn:not(.btn-primary) {
  color: #444e62;
}
.error-support-box {
  background: #e8f5ed;
  border-color: #c4e3d1;
}
.error-support-box p {
  color: #1c6b40;
  text-wrap: pretty;
}
.error-support-box code {
  background: #fff;
  border: 1px solid #c4e3d1;
  color: #182038;
}

body.unsub-page .auth-card {
  background: #fff;
  border-color: #d3d9e1;
}
body.unsub-page .auth-card h1 {
  color: #182038;
  text-wrap: balance;
}
body.unsub-page .auth-card .auth-intro {
  color: #444e62;
  text-wrap: pretty;
}
@media (max-width: 480px) {
  /* Phone: the page already has its own side gutter, a tighter box and a
     smaller heading let the last two words of a title (kept together with a
     non-breaking space) and the correlation id fit on one line, so nothing is
     cut mid-word. */
  .error-page {
    padding: 0;
  }
  .error-page-title,
  body.unsub-page .auth-card h1 {
    font-size: var(--text-md);
  }
  .error-support-box {
    padding: var(--sp-4);
  }
  body.unsub-page .auth-wrap {
    padding-inline: 0;
  }
  body.unsub-page .auth-card {
    padding: var(--sp-8) var(--sp-5);
  }
}

/* ─── Auth ──────────────────────────────────────── */

.auth-skip-link {
  position: absolute;
  left: var(--sp-4);
  top: var(--sp-2);
  transform: translateY(-200%);
  background: var(--bg);
  color: var(--text);
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--radius-sm);
  font-weight: 600;
  z-index: 100;
}

.auth-skip-link:focus {
  transform: none;
}

/* A plain brand row above the card, not the sticky platform bar: the generic
   `header` rule (and workspace_glass.css's opaque auth-page header) would
   otherwise pin it with a bar and rule that cut under the wordmark. */
body.auth-page header.auth-brand {
  display: flex;
  justify-content: center;
  position: static;
  background: transparent;
  border-bottom: 0;
  box-shadow: none;
  padding: var(--sp-8) var(--sp-4) 0;
}

/* The wordmark is dark navy; the sign-in shell is dark glass, so render it white
   (same treatment as .logo img in the platform header). */
.auth-brand img {
  height: 44px;
  width: auto;
  filter: brightness(0) invert(1);
}

main#main-content:focus {
  outline: none;
}

.auth-help {
  text-align: center;
  font-size: var(--text-sm);
  color: var(--ws2-text-tertiary, var(--text-secondary));
  padding: 0 var(--sp-4) var(--sp-8);
}

.auth-help a {
  color: var(--ws2-accent-text, var(--primary));
  font-weight: 600;
}

.auth-wrap {
  display: flex;
  justify-content: center;
  padding: var(--sp-12) var(--sp-4);
}

.auth-card {
  background: var(--bg-card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: var(--sp-10) var(--sp-8);
  width: 100%;
  max-width: 420px;
}

.auth-card h1 {
  font-size: var(--text-xl);
  font-weight: 700;
  margin-bottom: var(--sp-2);
}

.auth-card .auth-intro {
  color: var(--text-secondary);
  margin-bottom: var(--sp-6);
  font-size: var(--text-sm);
}

/* A card that ends on its intro text (link gone, request received) keeps the
   card's own bottom padding, not that padding plus the 27px section margin. */
.auth-card .auth-intro:last-child {
  margin-bottom: 0;
}

.auth-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

.auth-form label {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-secondary);
}

/* Control sizing (padding/font/border/focus) for .auth-form inputs lives
   in the unified form-control rule above -- do not reimpose it here. */

.auth-form .btn-primary {
  width: 100%;
  margin-top: var(--sp-2);
}

.auth-error {
  background: var(--error-bg);
  color: var(--error);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  font-weight: 500;
  border: 1px solid rgba(201, 59, 59, 0.2);
  margin-bottom: var(--sp-4);
}

.auth-info {
  background: var(--success-bg);
  color: var(--success);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  border: 1px solid rgba(26, 138, 92, 0.2);
  margin-bottom: var(--sp-4);
}

.auth-footer-links {
  margin-top: var(--sp-6);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--border-light);
  text-align: center;
  font-size: var(--text-sm);
}

.auth-footer-links a {
  color: var(--accent);
  text-decoration: none;
  font-weight: 600;
}

.auth-footer-links a:hover {
  text-decoration: underline;
}

.auth-mfa-code {
  font-size: var(--text-lg);
  letter-spacing: 0.3em;
  text-align: center;
  font-family:
    ui-monospace, SFMono-Regular, Menlo, Monaco, "Courier New", monospace;
}

.auth-totp-secret {
  background: var(--bg-elevated);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-sm);
  font-family: ui-monospace, monospace;
  font-size: var(--text-sm);
  word-break: break-all;
  user-select: all;
}

/* The sign-in shell is dark glass: keep the manual key readable on it. */
body.auth-page .auth-totp-secret {
  background: var(--ws2-elevated, var(--bg-elevated));
  color: var(--ws2-text-primary, var(--text));
}

.auth-qr {
  display: block;
  margin: var(--sp-4) auto;
  width: 200px;
  height: 200px;
  background: #fff;
  padding: var(--sp-2);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
}

/* ─── Secure handoff link ───────────────────────────── */

.secure-link-card-wide {
  max-width: 680px;
}

.secure-link-card h1 {
  text-align: center;
}

.secure-link-card .auth-intro {
  text-align: center;
}

.secure-link-secret {
  background: var(--bg-elevated);
  color: var(--text);
  padding: var(--sp-4);
  border-radius: var(--radius-sm);
  font-family:
    ui-monospace, SFMono-Regular, Menlo, Monaco, "Courier New", monospace;
  font-size: var(--text-sm);
  white-space: pre-wrap;
  word-break: break-word;
  user-select: all;
}

.secure-link-note {
  color: var(--text-secondary);
  font-size: var(--text-sm);
  text-align: center;
  margin-top: var(--sp-4);
}

.secure-link-card .btn-primary {
  display: flex;
  width: 100%;
  margin-top: var(--sp-4);
}

/* The access-code error follows the "Expires" note: 18px between them (it sat
   flush against it), the same step it keeps from the field below. */
.secure-link-card .auth-error {
  margin-top: var(--sp-4);
}

/* The copy-status line under a reveal is empty until a legacy copy fills it:
   it adds no gap of its own, and the block above it gives up its 27px bottom
   margin so the card keeps one even bottom padding. */
.secure-link-card .secure-link-note:empty {
  margin: 0;
}

.secure-link-card
  :is(.secure-field-list, .secure-qr-block):has(+ .secure-link-note:empty) {
  margin-bottom: 0;
}

/* Inside the form the flex gap already spaces the button from its field:
   the extra margin made it 36px, against 27px under every other form. */
.secure-link-card .auth-form .btn-primary {
  margin-top: var(--sp-2);
}

/* ─── Secure handoff link — structured fields ───────── */

.secure-field-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  margin: 0 0 var(--sp-6);
}

.secure-field-row {
  background: var(--bg-elevated);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  padding: var(--sp-3) var(--sp-4);
}

.secure-field-row-sensitive {
  border-left: 3px solid var(--primary);
}

.secure-field-label {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-secondary);
  margin: 0 0 var(--sp-2);
}

.secure-field-value-row {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: var(--sp-3);
  margin: 0;
}

.secure-field-value {
  flex: 1 1 12rem;
  min-width: 0;
  overflow-wrap: anywhere;
  display: flex;
  align-items: center;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--sp-2) var(--sp-3);
  font-family:
    ui-monospace, SFMono-Regular, Menlo, Monaco, "Courier New", monospace;
  font-size: var(--text-base);
  white-space: pre-wrap;
  word-break: break-word;
  user-select: all;
  cursor: pointer;
}

.secure-field-value:hover {
  border-color: var(--primary);
}

.secure-field-actions {
  flex-shrink: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}

.secure-field-toggle-btn,
.secure-field-save-btn,
.secure-field-copy-btn {
  flex-shrink: 0;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-weight: 600;
  padding: 0.5rem 0.85rem;
  cursor: pointer;
  white-space: nowrap;
  min-width: 5.5rem;
  min-height: 44px;
}

/* Phones: the action buttons drop under the value and share the full row, so
   the reveal never pushes the page wider than the screen (PU-06). */
@media (max-width: 600px) {
  .secure-field-actions {
    flex: 1 1 100%;
  }

  .secure-field-toggle-btn,
  .secure-field-save-btn,
  .secure-field-copy-btn {
    flex: 1 1 5.5rem;
    /* Half a phone row is narrower than "Save key file" plus the full
       padding, so the label ran onto the right edge; 9px a side fits it. */
    padding-left: var(--sp-2);
    padding-right: var(--sp-2);
  }
}

.secure-field-toggle-btn:hover,
.secure-field-save-btn:hover,
.secure-field-copy-btn:hover {
  background: var(--bg-alt);
  border-color: var(--primary);
  color: var(--primary);
}

.secure-field-copy-btn-success,
.secure-field-save-btn-success {
  background: var(--success-bg);
  border-color: var(--success);
  color: var(--success);
}

.secure-tooltip {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.secure-tooltip-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-muted);
  font-size: var(--text-sm);
  font-weight: 700;
  cursor: help;
}

.secure-tooltip-text {
  position: absolute;
  bottom: calc(100% + var(--sp-2));
  left: 50%;
  transform: translateX(-50%);
  width: 240px;
  background: var(--text);
  color: var(--bg);
  font-size: var(--text-sm);
  font-weight: 400;
  line-height: 1.4;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-sm);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.15s ease;
  z-index: 10;
  pointer-events: none;
}

/* Phones: anchor the hint to the whole label row instead of centring a fixed
   240px box on the icon, which spilled past the screen edge (PU-06). */
@media (max-width: 600px) {
  .secure-field-label {
    position: relative;
  }

  .secure-tooltip {
    position: static;
  }

  .secure-tooltip-text {
    left: 0;
    right: 0;
    width: auto;
    transform: none;
  }
}

.secure-tooltip:hover .secure-tooltip-text,
.secure-tooltip:focus .secure-tooltip-text,
.secure-tooltip:focus-within .secure-tooltip-text {
  opacity: 1;
  visibility: visible;
}

.secure-qr-block {
  text-align: center;
  margin-bottom: var(--sp-6);
}

.secure-qr-manual {
  margin-top: var(--sp-3);
  font-size: var(--text-sm);
}

.secure-qr-manual summary {
  cursor: pointer;
  color: var(--primary);
  font-weight: 600;
  min-height: 44px;
  padding: 10px 0;
  box-sizing: border-box;
}

/* ─── Secure link callouts ──────────────────────────── */

.secure-link-callout {
  background: var(--bg-alt);
  border-left: 3px solid var(--primary);
  padding: var(--sp-4) var(--sp-5);
  margin-bottom: var(--sp-6);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: var(--text-sm);
  color: var(--text);
}

.secure-link-callout p {
  margin: 0 0 var(--sp-3);
  line-height: 1.6;
}

.secure-link-callout p:last-child {
  margin-bottom: 0;
}

.secure-link-callout a {
  color: var(--primary);
  text-decoration: none;
  font-weight: 600;
}

.secure-link-callout a:hover {
  text-decoration: underline;
}

.secure-link-reveal-callout {
  background: var(--success-bg);
  border-left: 3px solid var(--success);
  padding: var(--sp-4) var(--sp-5);
  margin-bottom: var(--sp-6);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: var(--text-sm);
  color: var(--text);
}

.secure-link-reveal-callout p {
  margin: 0 0 var(--sp-3);
  line-height: 1.6;
}

.secure-link-reveal-callout p:last-child {
  margin-bottom: 0;
}

.secure-link-reveal-callout a {
  color: var(--primary);
  text-decoration: none;
  font-weight: 600;
}

.secure-link-reveal-callout a:hover {
  text-decoration: underline;
}

/* The sign-in shell is dark glass: keep the success callout and the revealed
   values readable on it (the light connect-host shell is unchanged). */
body.auth-page .auth-card .secure-link-reveal-callout,
body.auth-page .auth-card .secure-link-reveal-callout p,
body.auth-page .auth-card .secure-link-reveal-callout strong {
  color: var(--ws2-text-primary, var(--text));
}

body.auth-page .auth-card .secure-link-reveal-callout {
  background: var(--ws2-elevated, var(--success-bg));
}

/* The QR block sits straight on the dark card (not in a light row), so its
   label takes the shell's primary text colour instead of the grey that is
   meant for light rows (PU-22). */
body.auth-page .secure-qr-block .secure-field-label {
  color: var(--ws2-text-primary, var(--text));
}

body.auth-page .secure-qr-manual summary {
  color: #7ab4e3;
}

/* Top-bar brand link on the sign-in shell: 44px tap target, with equal
   negative margin so the bar keeps its height. */
body.auth-page .app-topbar-brand {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0.5rem 0;
  margin: -0.5rem 0;
  box-sizing: border-box;
}

/* The field rows stay light cards on the dark shell, so the value box keeps
   its light fill and takes dark text instead of the shell's light text. */
body.auth-page .secure-field-value {
  background: var(--bg);
  color: var(--text);
}

/* ─── Partner SFTP Setup Guide ─────────────────────── */
/* 2026-09-22 simplification: removed the 3-step how-it-works strip, the
   4-section field-grid layout, chips/checkbox groups, the vertical
   radio-group pattern, the "what happens next" connect panel, and the
   eight reference <details> toggles per an explicit product-owner
   instruction to simplify this page. See
   claude/memory/project_logro_partner_sftp_setup_guide.md's 2026-09-22
   section before restoring any of it.

   2026-10-01: a fresh, explicit product-owner instruction asked for a
   "one and done" form that captures everything needed, while keeping the
   page itself visually simple. The default view is UNCHANGED (still only
   organization/name/email/direction/notes) -- the added detail is revealed
   only after a direction is picked, via the same pure-CSS :has() technique
   as the original collect-fields reveal. See that section's 2026-10-01
   entry before restructuring this page again. */

/* Setup-only modifier: the shared .auth-card is 420px (login uses it too,
   and must stay that narrow); the partner form needs more room. Still
   width: 100% of the viewport below the cap, so phones stay full-width. */
.auth-card.setup-card {
  max-width: 620px;
}

/* Partner pages hold the 16px text floor (PU-01) and the 44px tap floor
   (PU-05). --text-sm is 15px and --text-xs 13.5px site-wide; scoped here so
   the shared tokens, and every other page, stay as they are. */
.auth-card.setup-card,
.auth-card.secure-link-card {
  --text-sm: 0.889rem;
  --text-xs: 0.889rem;
}

body.auth-page > footer {
  font-size: 0.889rem;
}

/* Phones: give the card's text the width the padding was taking, so headings
   and paragraphs wrap into balanced lines, and stack the two direction cards
   (each half-width card broke its description into 5 lines). */
@media (max-width: 480px) {
  .auth-card.setup-card,
  .auth-card.secure-link-card {
    padding-left: var(--sp-5);
    padding-right: var(--sp-5);
  }

  .auth-card.setup-card h1 {
    font-size: var(--text-lg);
  }

  .setup-direction-options {
    grid-template-columns: 1fr;
  }

  .auth-card.secure-link-card h1 {
    font-size: var(--text-lg);
  }
}

@media (max-width: 360px) {
  .auth-card.setup-card,
  .auth-card.secure-link-card {
    padding-left: var(--sp-4);
    padding-right: var(--sp-4);
  }

  .auth-card.setup-card h1,
  .auth-card.secure-link-card h1 {
    font-size: var(--text-md);
  }
}

/* The logo link of the connect-host shell (setup + secure link pages). */
body.auth-page > header .logo {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

/* PU-09: no heading or short text block ends in a single word. Balance
   evens out lines of headings and short labels; pretty only guards the last
   line of longer paragraphs. */
.auth-card.setup-card
  :is(
    h1,
    .setup-direction-title,
    .setup-direction-desc,
    .setup-section-title,
    .setup-hint,
    .setup-choice > span,
    .auth-error
  ),
.auth-card.secure-link-card :is(h1, h2, h3, summary, .secure-field-label > span) {
  text-wrap: balance;
}

.auth-card.setup-card p,
.auth-card.secure-link-card p {
  text-wrap: pretty;
}

/* A submitted address is one unbreakable token; let it break instead of
   pushing the page wider than a phone (PU-06). */
.auth-card.setup-card .auth-intro {
  overflow-wrap: anywhere;
}

.setup-intake-form {
  margin-top: var(--sp-6);
}

/* One 9px step from a field's label to its control (and to a hint under it),
   the same step the legends below use; the shared 4.5px sat them glued. */
.setup-intake-form label {
  gap: var(--sp-2);
}

.setup-intake-form textarea {
  resize: vertical;
  min-height: auto;
}

/* Gray out the "—" placeholder when no real option is selected */
.setup-intake-form select:has(option[value=""]:checked) {
  color: var(--text-muted);
}

.setup-direction-group {
  border: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}

.setup-direction-group legend {
  padding: 0 0 var(--sp-2);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-secondary);
}

.setup-direction-options {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-2);
}

/* Prefixed with .setup-intake-form (not just .setup-direction-option) so
   this beats .auth-form label's specificity (0,1,1 vs. a bare 0,1,0) --
   .setup-direction-option is itself a <label>, so without the extra class
   in the selector, .auth-form label's flex-direction: column silently wins
   and the radio dot stacks above the option text instead of beside it.

   Each card is a radio vertically centered against a two-line text block
   (title + one-line description), so both cards read as the same size and
   the dot never floats above the text. */
.setup-intake-form .setup-direction-option {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--sp-4);
  min-height: 76px;
  color: var(--text);
  cursor: pointer;
  transition:
    border-color 0.15s,
    background 0.15s,
    box-shadow 0.15s;
}

.setup-direction-option input {
  margin: 0;
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  accent-color: #5b9bd5;
}

.setup-direction-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.setup-direction-title {
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1.3;
}

.setup-direction-desc {
  font-size: var(--text-xs, 0.75rem);
  font-weight: 400;
  line-height: 1.4;
  color: var(--text-muted);
}

.setup-intake-form .setup-direction-option:hover {
  border-color: var(--text-muted);
}

.setup-intake-form .setup-direction-option:has(input:checked) {
  border-color: #5b9bd5;
  background: rgba(91, 155, 213, 0.1);
  box-shadow: 0 0 0 1px #5b9bd5;
}

/* 2026-10-01 one-and-done form: the three direction-scoped blocks, the
   tier tags, and the choice lists. Layout stays the plain stacked
   .auth-form column -- each block is just more of the same, divided by one
   quiet rule and a small title. */
.setup-block {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

.setup-section-title {
  margin: 0;
  padding-top: var(--sp-2);
  border-top: 1px solid var(--border-light);
  font-size: 1rem;
  font-weight: 700;
  color: var(--text);
}

.setup-hint {
  display: block;
  font-size: var(--text-xs, 0.75rem);
  font-weight: 400;
  line-height: 1.45;
  color: var(--text-muted);
}

.setup-hint code {
  font-size: inherit;
  font-weight: 600;
  color: var(--text-secondary);
}

/* Label text + tier tag share one row; the tag sits flush right so every
   tag on the page lines up in one quiet column. */
.setup-label {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--sp-3);
  width: 100%;
}

/* One tag class, two variants: "required" and "optional" -- a field is
   either required or it is not (product-owner standing decision). Only
   "required" is ever enforced, and only server-side. */
.setup-tier {
  flex: 0 0 auto;
  font-size: var(--text-xs, 0.75rem);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--text-muted);
}

.setup-tier-required {
  color: var(--accent);
}

.setup-field-group {
  border: none;
  margin: 0;
  padding: 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}

.setup-field-group legend,
.setup-direction-group legend {
  width: 100%;
}

/* A <legend> does not inherit .auth-form label's type, so without this the
   grouped questions render at body size and full weight -- visibly larger
   than every other field label on the page. */
.setup-field-group legend {
  padding: 0 0 var(--sp-2);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-secondary);
}

.setup-choice-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

/* Prefixed with .setup-intake-form for the same specificity reason as
   .setup-direction-option above (.auth-form label is a column + 600 weight). */
.setup-intake-form .setup-choice {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: var(--sp-3);
  font-weight: 500;
  line-height: 1.5rem;
  color: var(--text);
  cursor: pointer;
}

/* Fixed line-height above + fixed control size here = the control is
   centered on the first text line at every viewport (top-aligned, so a
   wrapped two-line option still lines up with its first line). */
.setup-choice input {
  width: 14px;
  height: 14px;
  margin: 6px 0 0;
  flex: 0 0 auto;
}

/* Each choice row is a 44px tap target (PU-05): the label is the hit area, so
   the row gets the height and the 14px control stays centred on the text. */
.setup-intake-form .setup-choice {
  align-items: center;
  min-height: 44px;
}

.setup-intake-form .setup-choice input {
  margin: 0;
}

/* Port is short, protocol takes the rest. */
.setup-row {
  display: grid;
  grid-template-columns: 6.5rem 1fr;
  gap: var(--sp-3);
}

/* Reveal rules. Each is a NEGATION (":not(:has(...))" hides, rather than a
   positive ":has(...)" that shows) so the failure mode of a browser without
   :has() support is "always visible" rather than "always hidden" -- a
   permanently hidden required-when-applicable field (collect_host) would
   silently block every logro_collects submission on those browsers. Pure
   CSS, no JavaScript: :has() is supported by every evergreen browser as of
   2026; these rules are the graceful-degradation fallback for the rest.

   The SSH key box is also shown, and the key and IP address labels gain a
   Required tag, when member data is Yes or Other: the server requires both
   then, so they must be visible and marked whichever sign-in method was
   picked, or a partner is bounced for a field they cannot see. */
.setup-intake-form:not(:has(input[name="direction"]:checked))
  .setup-shared-fields,
.setup-intake-form:not(
    :has(input[name="direction"][value="partner_sends"]:checked)
  )
  .setup-send-fields,
.setup-intake-form:not(
    :has(input[name="direction"][value="logro_collects"]:checked)
  )
  .setup-collect-fields,
.setup-intake-form:not(
    :has(input[name="send_auth_method"][value="ssh_key"]:checked)
  ):not(:has(select[name="member_data"] option[value="yes"]:checked)):not(
    :has(select[name="member_data"] option[value="other"]:checked)
  )
  .setup-ssh-key-field,
.setup-intake-form:not(
    :has(select[name="member_data"] option[value="yes"]:checked)
  ):not(:has(select[name="member_data"] option[value="other"]:checked))
  .setup-member-data-required,
.setup-intake-form:not(
    :has(input[name="collect_auth_method"][value="password"]:checked)
  )
  .setup-collect-password-field,
.setup-intake-form:not(
    :has(input[name="collect_auth_method"][value="password"]:checked)
  )
  .setup-collect-username-field {
  display: none;
}

/* "Other" on every dropdown and radio group: a single-line input directly
   under the choices, shown only while that "Other" option is chosen. Same
   negated-:has() convention as the reveals above (no :has() support =
   always visible, never always hidden; the server drops a stale value). */
.setup-intake-form label > input[name$="_other"] {
  margin-top: var(--sp-1);
}

.setup-intake-form:not(
    :has(select[name="cadence"] option[value="other"]:checked)
  )
  input[name="cadence_other"],
.setup-intake-form:not(
    :has(select[name="member_data"] option[value="other"]:checked)
  )
  input[name="member_data_other"],
.setup-intake-form:not(
    :has(select[name="collect_protocol"] option[value="other"]:checked)
  )
  input[name="collect_protocol_other"],
.setup-intake-form:not(
    :has(input[name="send_auth_method"][value="other"]:checked)
  )
  input[name="send_auth_method_other"],
.setup-intake-form:not(
    :has(input[name="collect_auth_method"][value="other"]:checked)
  )
  input[name="collect_auth_method_other"] {
  display: none;
}

/* Logro's outbound IP: masked until "Show" is toggled. The checkbox is a
   CSS-only toggle (visually hidden, still focusable, never submitted -- it
   has no name), so Show/Hide works with JavaScript off. The Copy button is
   rendered `hidden` and only unhidden by the page's inline script. */
.setup-ip {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.setup-ip-label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-secondary);
}

.setup-ip-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.setup-intake-form .setup-ip-toggle {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}

.setup-ip-value {
  flex: 1 1 auto;
  min-width: 0;
  padding: var(--sp-2) var(--sp-3);
  background: var(--bg-elevated);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  font-family:
    ui-monospace, SFMono-Regular, Menlo, Monaco, "Courier New", monospace;
  font-size: var(--text-sm);
  letter-spacing: 0.04em;
  color: var(--text);
  overflow-wrap: anywhere;
}

.setup-ip-real,
.setup-ip-hide-text {
  display: none;
}

.setup-ip-toggle:checked ~ .setup-ip-value .setup-ip-real,
.setup-ip-toggle:checked ~ .setup-ip-toggle-btn .setup-ip-hide-text {
  display: inline;
}

.setup-ip-toggle:checked ~ .setup-ip-value .setup-ip-masked,
.setup-ip-toggle:checked ~ .setup-ip-toggle-btn .setup-ip-show-text {
  display: none;
}

/* Prefixed with .setup-intake-form: the Show control is a <label>, and
   .auth-form label is a column + 600 weight that would otherwise win. */
.setup-intake-form .setup-ip-btn {
  flex: 0 0 auto;
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  min-width: 4.25rem;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-secondary);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
  transition:
    border-color 0.15s,
    color 0.15s;
}

.setup-intake-form .setup-ip-btn[hidden] {
  display: none;
}

.setup-intake-form .setup-ip-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.setup-ip-toggle:focus-visible ~ .setup-ip-toggle-btn,
.setup-intake-form .setup-ip-copy:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.setup-submit-note {
  background: var(--bg-elevated);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  padding: var(--sp-4) var(--sp-5);
  margin-top: var(--sp-2);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.6;
}

.setup-submit-note p {
  margin: 0;
}

/* ─── Partner SFTP Setup Guide — mobile density ─────────
   2026-09-22: the page now uses the plain .auth-card, which already
   handles its own mobile width (see the "Auth" section above, same as
   login.html) -- this block only retunes the surviving .setup-*
   form-control classes for phone widths. */

@media (max-width: 640px) {
  .setup-intake-form {
    margin-top: var(--sp-3);
    gap: var(--sp-4);
  }

  .setup-intake-form label {
    font-size: var(--text-sm);
  }

  .setup-intake-form input[type="text"],
  .setup-intake-form input[type="email"],
  .setup-intake-form select,
  .setup-intake-form textarea {
    padding: var(--sp-3) var(--sp-3);
    font-size: var(--text-base);
    min-height: 44px;
  }

  .setup-intake-form textarea {
    min-height: auto;
  }

  .setup-direction-group {
    gap: var(--sp-2);
  }

  .setup-direction-group legend {
    font-size: var(--text-sm);
  }

  /* Stack the two direction choices as full-width cards on mobile
       instead of inheriting the desktop 2-column grid -- side by side at
       this width squeezed each option's label onto two wrapped lines and
       made both read as cramped checkboxes rather than clear, confident
       choices. The selected-vs-unselected visual difference itself (border
       colour + background tint) comes from the existing
       .setup-direction-option:has(input:checked) rule above, which is not
       breakpoint-scoped and already applies here unchanged. */
  .setup-direction-options {
    grid-template-columns: 1fr;
    gap: var(--sp-2);
  }

  .setup-intake-form .setup-direction-option {
    padding: var(--sp-3) var(--sp-4);
    gap: var(--sp-3);
    min-height: 64px;
    align-items: center;
  }

  .setup-intake-form .btn-primary {
    min-height: 44px;
    padding: var(--sp-3) var(--sp-4);
    font-size: var(--text-lg);
    font-weight: 600;
  }

  .setup-submit-note {
    margin-top: var(--sp-2);
    padding: var(--sp-3) var(--sp-3);
    font-size: var(--text-sm);
  }

  /* The tag keeps sitting flush-right next to the label text at this
       width rather than wrapping to its own line -- there's room, and a
       wrapped tag reads as a second, unrelated line of copy. */
  .setup-label {
    font-size: var(--text-sm);
  }

  /* A fixed narrow port column leaves no room for its "Optional" tag, which
       then runs into the Protocol label beside it. Even halves fit both. */
  .setup-row {
    grid-template-columns: 1fr 1fr;
    gap: var(--sp-2);
  }
}

/* ─── User menu dropdown ────────────────────────────── */

.user-menu {
  position: relative;
}

.user-menu-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: var(--radius-sm);
  color: rgba(255, 255, 255, 0.85);
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
  padding: 0.4rem 0.75rem;
  min-height: 44px;
  min-width: 44px;
  cursor: pointer;
  transition:
    background 0.15s,
    border-color 0.15s,
    color 0.15s;
  white-space: nowrap;
  user-select: none;
}

.user-menu-btn:hover,
.user-menu-btn[aria-expanded="true"] {
  color: #fff;
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.4);
}

.user-menu-btn:focus-visible {
  outline: 2px solid var(--focus-color);
  outline-offset: 2px;
}

.user-menu-label {
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The person glyph that replaces the name on a phone (shown in the phone block
   below); hidden everywhere else. */
.app-topbar .user-menu-icon {
  display: none;
  flex: none;
  width: 20px;
  height: 20px;
}

/* Room for a full display name on a wide bar so it is not ellipsised (PU-07). */
.app-topbar .user-menu-label {
  max-width: 280px;
}

.user-menu-chevron {
  flex-shrink: 0;
  transition: transform 0.15s;
}

.user-menu-btn[aria-expanded="true"] .user-menu-chevron {
  transform: rotate(180deg);
}

.user-menu-panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 100;
  min-width: 220px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  padding: var(--sp-1) 0;
  outline: none;
}

.user-menu-panel[hidden] {
  display: none;
}

/* The role-gated app menu can list a dozen links for admins -- scroll inside
   the panel rather than running off short viewports. */
.user-menu-panel {
  max-height: calc(100vh - 80px);
  overflow-y: auto;
}

.user-menu-identity {
  padding: var(--sp-3) var(--sp-4) var(--sp-2);
  display: flex;
  flex-direction: column;
  gap: 2px;
  border-bottom: 1px solid var(--border-light);
  margin-bottom: var(--sp-1);
}

.user-menu-name {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text);
  line-height: 1.4;
}

.user-menu-email {
  font-size: var(--text-sm);
  color: var(--text-muted);
  line-height: 1.4;
  word-break: break-all;
}

.user-menu-role-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 4px;
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-weight: 600;
  text-transform: capitalize;
  letter-spacing: 0.02em;
  align-self: flex-start;
}

.user-menu-divider {
  display: none;
}

.user-menu-item {
  display: block;
  width: 100%;
  padding: var(--sp-2) var(--sp-4);
  color: var(--text-secondary);
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  text-decoration: none;
  text-align: left;
  background: transparent;
  border: none;
  cursor: pointer;
  transition:
    background 0.12s,
    color 0.12s;
  white-space: nowrap;
}

.user-menu-item:hover,
.user-menu-item:focus-visible {
  background: var(--bg-alt);
  color: var(--text);
  outline: none;
}

.user-menu-item[aria-current="page"] {
  color: var(--text);
  font-weight: 700;
}

/* Section label inside the menu panel (e.g. "Admin" above the admin links). */
.user-menu-group-label {
  padding: var(--sp-2) var(--sp-4) var(--sp-1);
  margin-top: var(--sp-1);
  border-top: 1px solid var(--border-light);
  color: var(--text-muted);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.app-topbar .user-menu-item-logout {
  border-top: 1px solid var(--border-light);
  margin-top: var(--sp-1);
}

.user-menu-logout-form {
  margin: 0;
  padding: 0;
}

/* Mobile: keep panel within viewport */
@media (max-width: 640px) {
  .user-menu-panel {
    right: 0;
    left: auto;
    min-width: 190px;
  }

  .user-menu-label {
    max-width: 120px;
  }
}

/* ─── Notification bell ─────────────────────────────── */

.notif-bell {
  position: relative;
}

.notif-bell-wrap {
  position: relative;
}

.notif-bell-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: var(--radius-sm);
  color: rgba(255, 255, 255, 0.75);
  padding: 0.4rem 0.55rem;
  min-height: 44px;
  min-width: 44px;
  cursor: pointer;
  transition:
    background 0.15s,
    border-color 0.15s,
    color 0.15s;
  line-height: 1;
}

.notif-bell-btn:hover,
.notif-bell-btn[aria-expanded="true"] {
  color: #fff;
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.4);
}

.notif-bell-btn:focus-visible {
  outline: 2px solid var(--focus-color);
  outline-offset: 2px;
}

.notif-badge {
  position: absolute;
  top: -8px;
  right: -8px;
  min-width: 22px;
  height: 22px;
  background: var(--error);
  color: #fff;
  font-size: var(--text-sm);
  font-weight: 700;
  line-height: 22px;
  text-align: center;
  border-radius: 11px;
  padding: 0 3px;
  pointer-events: none;
}

.notif-panel {
  /* Fixed (not absolute, and portaled to body via JS in base.html) so it
       escapes the sticky header's stacking context entirely. Positioned
       under the bell via positionNotifPanel() in base.html, which runs
       before every paint where this panel could be visible -- the top/right
       below are a no-JS fallback only and are intentionally >= the header's
       current rendered height (~72px) plus margin so they never overlap it.
       Keep in sync with header height (nav padding + content) if that grows. */
  position: fixed;
  top: 80px;
  right: 16px;
  z-index: 1001;
  width: 320px;
  max-width: calc(100vw - 16px);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}

.notif-panel[hidden] {
  display: none;
}

.notif-panel-header {
  padding: var(--sp-3) var(--sp-4);
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--text);
  border-bottom: 1px solid var(--border-light);
}

.notif-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 380px;
  overflow-y: auto;
}

.notif-item {
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border-light);
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.notif-item:last-child {
  border-bottom: none;
}

.notif-item-title {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text);
  line-height: 1.4;
}

.notif-item-age {
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.notif-item-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--primary);
  text-decoration: none;
  align-self: flex-start;
  margin-top: 2px;
  transition: color 0.15s;
}

.notif-item-link:hover {
  color: var(--primary-hover);
}

.notif-empty {
  padding: var(--sp-5) var(--sp-4);
  text-align: center;
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.notif-panel-footer-link {
  display: block;
  padding: var(--sp-2) var(--sp-4);
  text-align: center;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
  border-top: 1px solid var(--border-light);
  transition:
    color 0.15s,
    background 0.15s;
}

.notif-panel-footer-link:hover {
  color: var(--accent-hover);
  background: var(--bg-hover, rgba(0, 0, 0, 0.02));
}

/* ─── Stale banner ──────────────────────────────────── */

.notif-stale-banner {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  background: var(--warning-bg);
  color: var(--warning);
  padding: var(--sp-2) var(--sp-5);
  font-size: 1rem;
  font-weight: 500;
  border-bottom: 1px solid rgba(161, 114, 22, 0.2);
}

.notif-stale-banner[hidden] {
  display: none;
}

.notif-stale-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  min-width: 44px;
  font-weight: 700;
  color: var(--warning);
  text-decoration: underline;
  cursor: pointer;
  white-space: nowrap;
  background: none;
  border: none;
  padding: 0 var(--sp-2);
  min-height: 44px; /* PU-05 tap floor */
  font-size: inherit;
}

/* Full-strength colour (the 0.7 opacity measured 2.79:1) and a 44px target. */
.notif-stale-dismiss {
  margin-left: auto;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--warning);
  font-size: 1rem;
  line-height: 1;
  min-width: 44px;
  min-height: 44px;
  padding: 0 var(--sp-1);
  opacity: 0.7;
  transition: opacity 0.15s;
  flex-shrink: 0;
}

.notif-stale-dismiss:hover {
  background: rgba(143, 101, 19, 0.12);
}

/* Mobile: full-width panel below header */
@media (max-width: 600px) {
  .notif-panel {
    position: fixed;
    top: auto;
    left: 0;
    right: 0;
    width: 100%;
    border-radius: 0;
    border-left: none;
    border-right: none;
    max-height: 60vh;
    overflow-y: auto;
  }

  /* Compact nav on narrow screens to prevent horizontal overflow. */
  nav {
    padding-left: var(--sp-3);
    padding-right: var(--sp-3);
  }

  /* Truncate user name label to prevent overflow. */
  .user-menu-label {
    max-width: 72px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* ─── Dedicated notifications page ──────────────────────────────────
   Gated behind LOGRO_NOTIF_PAGE -- these rules only ever apply to the
   .notifications-page container, which only renders when the flag is on,
   so this block cannot affect any flag-off page. */

.notifications-page {
  max-width: 780px;
  margin: 0 auto;
  padding: var(--sp-8) var(--sp-4);
}

.notifications-page-header {
  margin-bottom: var(--sp-8);
}

.notifications-page-title {
  font-size: 1.75rem;
  font-weight: 700;
  margin-bottom: var(--sp-2);
  color: var(--text-primary);
}

.notifications-page-subtitle {
  font-size: 1rem;
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.notifications-section {
  margin-top: var(--sp-8);
}

.notifications-section:first-of-type {
  margin-top: 0;
}

.notif-page-empty {
  padding: var(--sp-5) var(--sp-4);
  text-align: center;
  font-size: 1rem;
  color: var(--text-muted);
  background: var(--bg-card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius);
}

.notifications-active-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.notif-page-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: var(--bg-card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius);
}

.notif-page-item-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 14rem;
  min-width: 0;
}

.notif-page-item-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text);
  text-wrap: balance;
}

/* The page's section headings and count badges are 16px (the shared 15px and 11px sizes sit under the floor). */
.notifications-page .section-heading { font-size: 1rem; }
.notifications-page .count-badge { font-size: 1rem; }

.notif-page-item-age {
  white-space: nowrap;
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.notif-page-item-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
  white-space: nowrap;
  flex-shrink: 0;
  transition: color 0.15s;
}

.notif-page-item-link:hover {
  color: var(--accent-hover);
}

.notifications-history-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.notif-history-item {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-2);
  border-bottom: 1px solid var(--border-light);
}

.notif-history-item:last-child {
  border-bottom: none;
}

.notif-history-icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--bg-muted, #f3f4f6);
  color: var(--text-muted);
  margin-top: 2px;
}

.notif-history-item-qa .notif-history-icon {
  color: var(--accent);
}

.notif-history-item-feedback .notif-history-icon {
  color: var(--warning, #e6a817);
}

.notif-history-body {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.notif-history-title {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text);
  text-decoration: none;
}

.notif-history-title:hover {
  color: var(--accent);
}

.notif-history-age {
  font-size: var(--text-sm);
  color: var(--text-muted);
}

/* ─── Hub onboarding UX ─────────────────────────────── */

/* Tool card restructured as div with click-via-JS */
div.tool-card {
  position: relative;
  cursor: pointer;
}

div.tool-card:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow);
}

div.tool-card:focus {
  outline: 2px solid var(--focus-color);
  outline-offset: 2px;
}

/* How it works details block */
.tool-how-details {
  margin-top: var(--sp-3);
  border-top: 1px solid var(--border-light);
  padding-top: var(--sp-3);
}

.tool-how-details summary {
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--accent);
  cursor: pointer;
  user-select: none;
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-1) 0;
  transition: color 0.15s;
}

.tool-how-details summary::-webkit-details-marker {
  display: none;
}

.tool-how-details summary::before {
  content: "\25B6";
  font-size: var(--text-sm);
  transition: transform 0.15s;
  display: inline-block;
}

.tool-how-details[open] summary::before {
  transform: rotate(90deg);
}

.tool-how-details summary:hover {
  color: var(--accent);
}

.tool-how-list {
  margin: var(--sp-2) 0 0 var(--sp-4);
  padding: 0;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.6;
}

.tool-how-list li {
  margin-bottom: var(--sp-1);
}

.tool-how-list li:last-child {
  margin-bottom: 0;
}

/* ─── Unified job-status colors (platform-wide) ─────────────────────────
   The repricing tool uses status values complete/error/needs_input; the
   other tools (benchmark/directory/geo/disruption/proposal) use
   completed/failed/queued/running/pending_phase1. Map those to the same
   semantic meta-line colors so a finished job reads green and a failed job
   reads red everywhere, matching the repricing gold standard. */
.meta-status-status-completed {
  color: var(--success);
}
.meta-status-status-failed {
  color: var(--error);
}
.meta-status-status-queued,
.meta-status-status-running,
.meta-status-status-processing,
.meta-status-status-pending_phase1 {
  color: var(--text-secondary);
}

/* Stat-grid value colors: raise specificity above `.stat .value` so semantic
   savings colors actually render inside a summary grid (pass=green, fail=red,
   caution=amber) — used by directory QA, geo anomalies, disruption. */
.stat .value.savings-green {
  color: var(--savings-green);
}
.stat .value.savings-yellow {
  color: var(--savings-yellow);
}
.stat .value.savings-red {
  color: var(--savings-red);
}

/* ─── Sortable tables ───────────────────────────────────────────── */
th.th-sortable {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
th.th-sortable:hover {
  color: var(--accent);
}
th.th-sortable::after {
  content: "\2195";
  opacity: 0.3;
  margin-left: 0.35em;
  font-size: 1em;
  font-weight: 400;
}
th.th-sortable[aria-sort="ascending"]::after {
  content: "\2191";
  opacity: 1;
  color: var(--accent);
}
th.th-sortable[aria-sort="descending"]::after {
  content: "\2193";
  opacity: 1;
  color: var(--accent);
}
th.th-sortable:focus-visible {
  outline: 2px solid var(--focus-color);
  outline-offset: -2px;
}

/* ─── App top bar ─────────────────────────────────────
   The directory's bar on the standalone pages that survive outside Workspace
   V2 (account, admin, guide, ...): white, one hairline, the logo on the left
   edge, then "Back to Workspace"; notification bell + role-gated user menu on
   the right (D-0214: was a navy bar with a text wordmark). Rendered by
   _app_topbar.html; workspace_v2_shell.html suppresses it entirely. There is
   no sidebar -- main/footer use the normal centered layout. */

.app-topbar {
  background: var(--canvas);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 900;
}

/* The logo's left edge is the page's left edge, and the bar's contents sit on
   one centre line with it. */
.app-topbar-brand img {
  display: block;
  height: var(--logo-h);
  max-width: var(--logo-max-w);
  width: auto;
}

.app-topbar-nav {
  padding: var(--sp-3) var(--sp-6);
  gap: var(--sp-4);
}

.app-topbar-left,
.app-topbar-right {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  min-width: 0;
}

.app-topbar-brand {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--ink);
  font-size: var(--text-md);
  font-weight: 700;
  text-decoration: none;
}

.app-topbar-back {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  color: var(--ink-2);
  font-size: 1rem;
  font-weight: 600;
  text-decoration: none;
  min-height: 44px;
  padding: 0.3rem 0.6rem;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  transition:
    color 0.15s,
    background 0.15s;
}

.app-topbar-back:hover,
.app-topbar-back:focus-visible {
  color: var(--ink);
  background: var(--sub);
}

/* Bell and user menu on the light bar: ink on white with a measurable control
   border, the same pairing as every field. */
.app-topbar .notif-bell-btn,
.app-topbar .user-menu-btn {
  border-color: var(--control);
  color: var(--ink);
}
.app-topbar .notif-bell-btn:hover,
.app-topbar .notif-bell-btn[aria-expanded="true"],
.app-topbar .user-menu-btn:hover,
.app-topbar .user-menu-btn[aria-expanded="true"] {
  color: var(--ink);
  background: var(--sub);
  border-color: var(--ink);
}

.app-topbar-back:focus-visible,
.app-topbar-brand:focus-visible {
  outline: 2px solid var(--focus-color);
  outline-offset: 2px;
}

/* PU-05: every control in the bar is a 44px target at every width, not only
   on a phone (the phone block below keeps its own, identical floor). */
.app-topbar-brand {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.app-topbar .app-topbar-back,
.app-topbar .notif-bell-btn,
.app-topbar .user-menu-btn {
  min-height: 44px;
  min-width: 44px;
}

@media (max-width: 640px) {
  .app-topbar-nav {
    padding: var(--sp-2) var(--sp-4);
  }
  .app-topbar-left,
  .app-topbar-right {
    gap: var(--sp-2);
  }
  /* Phone tap targets: every control in the bar is at least 44x44 CSS px
     (WCAG 2.5.5 target size, comfortable thumb reach). Measured 34-35px tall
     before 2026-10-08 and 40px after; 44px since ER-U11 (2026-10-14). */
  .app-topbar-brand {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
  .app-topbar-back,
  .notif-bell-btn,
  .user-menu-btn {
    min-height: 44px;
    min-width: 44px;
  }
  .app-topbar-back {
    justify-content: center;
  }
}

/* Narrow phones (320-440px): "Back to Workspace" collapses to the chevron so
   the bar never overlaps the bell or clips the user menu (it overlapped by
   10px at 390 and overflowed the page to 340px at 320). The link keeps its
   accessible name because the label is clipped, not removed. */
@media (max-width: 440px) {
  .app-topbar-back-label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  .app-topbar-back svg {
    width: 18px;
    height: 18px;
  }
  /* A name cut to "Scott Exa..." reads as a bug; the menu opens on the
     identity panel, and the button keeps its accessible name. */
  .user-menu-label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  /* PU-07: a name the bar cannot fit is never cut off with an ellipsis. On a
     phone it gives way to a person glyph; the name stays for assistive tech. */
  .app-topbar .user-menu-icon {
    display: inline-block;
  }
  .app-topbar .user-menu-btn {
    justify-content: center;
    padding-inline: 10px;
  }
  .app-topbar .user-menu-label,
  .app-topbar .user-menu-chevron {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
}

/* ─── Main-shell phone fit (ER-U11, D-0080) ───────────────
   Standalone main-shell pages (account, admin, users, guide, catalog, group
   and configurator admin, the 404 page, ...) at 390 and 360 CSS px: no
   sideways page scroll, nothing clipped. Measure at 390x844 and 360x740
   before merging a new main-shell page (docs/reference/phone-layout.md).

   .table-scroll is the shared wrapper every main-shell <table> sits in
   (role="region" + aria-label in the template; tables.js adds tabindex="0"
   only while the region actually scrolls, so desktop gains no tab stop). It
   is a pure overflow guard: a full-width block, the same box tables.js's own
   .table-resize-scroll wrapper already gave these tables on desktop, so the
   desktop layout is unchanged. The table keeps its width and scrolls inside
   the wrapper instead of pushing the page sideways. */
.table-scroll {
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
}

.table-scroll:focus-visible {
  outline: 2px solid var(--focus-color);
  outline-offset: 2px;
}

/* Everything else is phone-only. body.main-shell is set by base_main.html;
   Workspace V2 (body.ws2-fullpage) shares that shell but has its own phone
   pass, so it is excluded. */
@media (max-width: 640px) {
  /* Long unbroken strings (emails, file names, ids, tokens) wrap instead of
     widening the page. Inside tables the softer break-word keeps columns
     from collapsing to one character: the table scrolls in .table-scroll. */
  body.main-shell:not(.ws2-fullpage) main {
    overflow-wrap: anywhere;
  }
  body.main-shell:not(.ws2-fullpage) main table {
    overflow-wrap: break-word;
  }
  /* Buttons and button-styled links wrap their label rather than run off
     the right edge; form controls and preformatted blocks never exceed
     their column. */
  body.main-shell:not(.ws2-fullpage) main .btn {
    white-space: normal;
    max-width: 100%;
  }
  body.main-shell:not(.ws2-fullpage) main :is(input, select, textarea, img) {
    max-width: 100%;
  }
  body.main-shell:not(.ws2-fullpage) main pre {
    max-width: 100%;
    overflow-x: auto;
  }
  /* User menu: anchored to the right edge (see the rule above), as wide as
     its longest label but never wider than the viewport minus the bar's side
     padding, so a long name, email or menu label wraps instead of pushing
     the panel off the left edge. */
  .app-topbar .user-menu-panel {
    width: max-content;
    max-width: calc(100vw - 2 * var(--sp-4));
  }
  .app-topbar .user-menu-item {
    white-space: normal;
  }
}

/* =========================================================
   Workspace — client conversation surface (Story 2)
   ========================================================= */

/* Thread cards (list page) */
.ws-thread-card {
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 16px;
  margin-bottom: 12px;
  display: flex;
  gap: 12px;
  transition:
    border-color 0.15s,
    background 0.15s;
}
.ws-thread-card:hover {
  border-color: #d1d5db;
  background: #f9fafb;
}
.ws-thread-card-bar {
  width: 4px;
  border-radius: 2px;
  flex-shrink: 0;
  align-self: stretch;
}
.ws-thread-card-bar.received {
  background: #9ca3af;
}
.ws-thread-card-bar.in-progress {
  background: #3b82f6;
}
.ws-thread-card-bar.awaiting-you {
  background: #f59e0b;
}
.ws-thread-card-bar.responded {
  background: #10b981;
}
.ws-thread-card-bar.resolved {
  background: #d1d5db;
}

/* Message bubbles (thread detail) */
.ws-message {
  margin-bottom: 20px;
}
.ws-message-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 6px;
}
.ws-message-time {
  color: #9ca3af;
  font-size: var(--text-sm);
}
.ws-message-client .ws-message-body {
  background: #f0f9ff;
  border-radius: 0 8px 8px 8px;
  padding: 12px 16px;
  font-size: 0.95rem;
  line-height: 1.6;
}
.ws-message-team .ws-message-body {
  background: #f9fafb;
  border: 1px solid #f3f4f6;
  border-radius: 8px 0 8px 8px;
  padding: 12px 16px;
  font-size: 0.95rem;
  line-height: 1.6;
}
.ws-message-attachments {
  margin-top: 10px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  min-width: 0;
  max-width: 100%;
}
.ws-attachment-link {
  font-size: var(--text-sm);
  color: #2563eb;
  text-decoration: none;
  padding: 4px 10px;
  background: #eff6ff;
  border-radius: 4px;
  border: 1px solid #bfdbfe;
  transition: background 0.12s;
}
.ws-attachment-link:hover {
  background: #dbeafe;
}

/* Delivery receipt under the client's latest message: "Delivered" (muted) ->
   "Seen" (accent), like a chat app's ticks. See message_receipts.py. */
.ws-receipt {
  clear: both;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
  /* Padding, not margin: the bubble above is floated and `clear: both`
     swallows a top margin into the clearance, which left the label 0px
     under the bubble. */
  padding-top: 8px;
  font-size: var(--text-sm);
  line-height: 1;
  color: #9ca3af;
}
.ws-receipt-icon {
  flex-shrink: 0;
}
.ws-receipt--seen,
.ws-receipt--working {
  color: #6b7280;
}
.ws-receipt--seen .ws-receipt-icon,
.ws-receipt--working .ws-receipt-icon {
  color: #2563eb;
}
/* "In progress": a gentle pulse on the progress icon; off for reduced motion. */
.ws-receipt--working .ws-receipt-icon {
  animation: ws-receipt-pulse 2s ease-in-out infinite;
}
@keyframes ws-receipt-pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.45;
  }
}
@media (prefers-reduced-motion: reduce) {
  .ws-receipt--working .ws-receipt-icon {
    animation: none;
  }
}

/* Timeline events (status transitions) */
.ws-timeline-event {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 16px 0;
  color: #6b7280;
  font-size: var(--text-sm);
}
.ws-timeline-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #d1d5db;
  flex-shrink: 0;
}
.ws-timeline-label {
  flex: 1;
}
.ws-timeline-time {
  color: #9ca3af;
  white-space: nowrap;
}

/* Awaiting-you amber banner */
.ws-awaiting-banner {
  background: #fef3c7;
  border: 1px solid #f59e0b;
  border-radius: 8px;
  padding: 12px 16px;
  margin-bottom: 20px;
  color: #92400e;
  font-weight: 500;
  font-size: 0.95rem;
}

/* Session-expired banner (composer draft preservation) */
.ws-session-banner {
  background: #fef2f2;
  border: 1px solid #fca5a5;
  border-radius: 8px;
  padding: 12px 16px;
  margin-bottom: 14px;
  color: #991b1b;
  font-weight: 500;
  font-size: 0.9rem;
}
.ws-session-banner a {
  color: #991b1b;
  text-decoration: underline;
  font-weight: 600;
}

/* "New" chip — team has replied since the client last viewed the thread */
.ws-new-chip {
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: #92400e;
  background: #fef3c7;
  border: 1px solid #f59e0b;
  border-radius: 10px;
  padding: 1px 8px;
  margin-left: 4px;
  vertical-align: middle;
}

/* =========================================================
   Workspace V2 -- three-panel thread-native shell (WS-A)
   Flag: LOGRO_WORKSPACE_V2. New surface; reuses the .ws-* rules
   above by class name (message bubbles, timeline, attachment
   links) but introduces its own .ws2-* namespace for panel
   chrome so nothing above is touched or overridden. The
   conversation's ticket-shaped status (received/in_progress/
   awaiting_you/responded/resolved) is intentionally not shown
   as a pill or stepper here -- see .ws2-canvas-state-chip below
   for the 3-state indicator that replaced it on the Canvas panel.
   ========================================================= */

/* Registers --ws2-canvas-w as a typed <length> custom property with a sane
   initial value. inherits:true so a value written on the root element (by
   the no-flash inline script, before .ws2-shell exists in the DOM) still
   reaches .ws2-shell once it renders. Chrome 85+, Safari 15.4+, Firefox 128+. */
@property --ws2-canvas-w {
  syntax: "<length>";
  inherits: true;
  initial-value: 260px;
}

.ws2-shell-wrap {
  width: 100%;
}

.ws2-shell {
  display: grid;
  /* Chat defaults much wider than the thread list here too (was 520px) --
     matches fullpage mode's "chat is the star" treatment (see the
     body.ws2-fullpage .ws2-shell rule below) even though this non-fullpage
     grid has no resize handle wired to --ws2-mid-w yet. */
  grid-template-columns:
    var(--ws2-left-w, 220px) var(--ws2-mid-w, 760px)
    minmax(0, 1fr);
  gap: var(--sp-5);
  align-items: start;
}

.ws2-panel {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.ws2-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border);
  font-weight: 700;
  font-size: var(--text-sm);
  color: var(--navy);
}

.ws2-panel-body {
  padding: var(--sp-3);
  overflow-y: auto;
  max-height: 70vh;
}

.ws2-empty-note {
  color: var(--text-muted);
  font-size: var(--text-sm);
  padding: var(--sp-4) var(--sp-2);
  text-align: center;
}

/* Utility classes (2026-08-25 design-system audit, POLISH finding #11):
   replace one-off `style="margin-top: 1rem;"` / `style="display:inline;"`
   attributes in _workspace_v2_conversation.html with named classes -- same
   visual result, but themeable/reusable/greppable instead of baked-in
   inline CSS. */
.ws2-mt-card {
  margin-top: var(--sp-4); /* 1rem, matches the spacing scale */
}

/* Single-job card (the threadless /workspace?job= view): the UI gate floors
   (text 16px, tap targets 44px, no lone last word). Scoped to the panel so no
   other page moves. */
.ws2-single-job-panel .meta,
.ws2-single-job-panel .meta-status,
.ws2-single-job-panel .card h3,
.ws2-single-job-panel .status-help-text,
.ws2-single-job-panel .hint,
.ws2-single-job-panel .link-diagnostic,
.ws2-single-job-panel .feedback-divider-label,
.ws2-single-job-panel .feedback-history-label,
.ws2-single-job-panel .feedback-item .feedback-meta,
.ws2-single-job-panel .feedback-item .feedback-meta > span,
.ws2-single-job-panel .btn,
.ws2-single-job-panel .btn-download {
  font-size: 1rem;
}
.ws2-single-job-panel .link-diagnostic {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.ws2-single-job-panel .btn,
.ws2-single-job-panel .btn-download {
  min-height: 44px;
  box-sizing: border-box;
}
.ws2-single-job-panel .meta,
.ws2-single-job-panel .card h3,
.ws2-single-job-panel .card p,
.ws2-single-job-panel .status-help-text,
.ws2-single-job-panel .hint,
.ws2-single-job-panel .feedback-text {
  text-wrap: pretty;
}

/* Screen-reader h1 for the list-only phone/tablet view (see workspace_v2_shell.html). */
.ws2-list-h1 {
  display: none;
}
@media (max-width: 900px) {
  body.ws2-fullpage:not(.ws2-has-conversation) .ws2-list-h1 {
    display: block;
  }
}

.ws2-inline {
  display: inline;
}

/* Modifier for the conversation panel's "nothing selected" empty state --
   by far the largest empty column in the shell (the whole middle panel),
   so it gets true both-axes centering and a bigger note instead of the
   compact top-padded treatment the smaller thread-list/canvas empty states
   use. Scoped to its own class rather than changing the shared
   .ws2-panel-body / .ws2-empty-note rules above, which the other (smaller)
   empty states also rely on. */
.ws2-conversation-empty-body {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 240px;
}

.ws2-conversation-empty-note {
  font-size: var(--text-sm);
  padding: 0;
}

/* --- Left panel: thread list --- */

.ws2-thread-items {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.ws2-thread-item {
  display: flex;
  gap: var(--sp-2);
  padding: var(--sp-2);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: inherit;
  transition: background 0.12s;
}

.ws2-thread-item:hover {
  background: var(--bg-alt);
}

.ws2-thread-item-active {
  background: var(--bg-alt);
  box-shadow: inset 2px 0 0 var(--accent);
}

.ws2-thread-item-body {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.ws2-thread-item-title {
  font-weight: 600;
  font-size: var(--text-sm);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ws2-thread-item-cat-icon {
  margin-right: 4px;
}

.ws2-thread-item-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

/* --- WS-E: Q&A/feedback flagged-thread additions --- */

/* Left panel: conversation search box (fixed above the scrolling list). */
.ws2-thread-search {
  position: relative;
  padding: var(--sp-2) var(--sp-3);
}

.ws2-thread-search-icon {
  position: absolute;
  left: calc(var(--sp-3) + 8px);
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-muted);
  pointer-events: none;
}

.ws2-thread-search-input {
  width: 100%;
  box-sizing: border-box;
  min-height: 44px; /* PU-05 tap floor */
  padding: var(--sp-2) var(--sp-2) var(--sp-2) 30px;
  font-size: var(--text-sm);
  color: var(--navy);
  background: var(--bg-alt);
  border: none;
  border-radius: var(--radius-sm);
  outline: none;
}

.ws2-thread-search-input::placeholder {
  color: var(--text-muted);
}

/* Search filter hides non-matching items. .ws2-thread-item sets display:flex,
   which overrides the UA [hidden] rule, so make [hidden] authoritative here. */
.ws2-thread-item[hidden] {
  display: none;
}

.ws2-thread-list-filter {
  padding: 0 0 var(--sp-2);
  margin-bottom: var(--sp-2);
}

.ws2-filter-link {
  font-size: var(--text-sm);
  font-weight: 600;
  color: #5b9bd5;
  text-decoration: none;
}

.ws2-filter-link:hover {
  text-decoration: none;
}

.ws2-flag-badge {
  margin-right: 4px;
}

/* "N waiting on you" attention pill -- shown only when flagged_count > 0.
   Amber/yellow background to stand out as an attention-requiring banner.
   Inherits .ws2-filter-link's font/weight/no-underline; adds pill shape and
   warm-amber fill so it reads as "action needed", not a passive nav link. */
.ws2-awaiting-pill {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 99px;
  background: var(--warning-bg);
  border: 1px solid var(--warning);
  color: var(--warning);
  font-weight: 600;
}

/* Icon inside the badge (2026-08-25 design-system audit, POLISH finding
   #12): the "Show flagged" filter-link icon previously inline-styled this
   same alignment directly on its <svg> instead of using .ws2-flag-badge
   like the per-row flagged icon does -- unify on the class. */
.ws2-flag-badge svg {
  vertical-align: text-top;
  flex-shrink: 0;
}

.ws2-legacy-qa-chip {
  display: inline-block;
  font-size: var(--text-xs, 0.75rem);
  font-weight: 600;
  padding: 1px 6px;
  border-radius: var(--radius-sm);
  background: var(--surface-alt, #eef1f5);
  color: var(--text-muted);
}

/* Sidebar per-row tool-tag chip (LOGRO_WS_TOOL_CHIPS) -- reuses the
   .ws2-tool-badge visual language (muted pill) at a more compact size so it
   reads as a secondary label next to the status pill. */
.ws2-thread-tool-chip {
  display: inline-block;
  flex-shrink: 0;
  font-size: var(--text-xs, 0.75rem);
  font-weight: 600;
  padding: 1px 6px;
  border-radius: var(--radius-sm);
  background: var(--surface-alt, #eef1f5);
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Tool pills at the top of the thread list (LOGRO_WS_TOOL_CHIPS): "All" plus
   one plain link per tool. They wrap onto further lines instead of scrolling
   sideways, so the row fits the narrow column and a 390px phone. The active
   pill carries aria-current="true". Dark-shell colours: workspace_glass.css. */
.ws2-tool-pills {
  /* Reset the bare `nav` site-header rule (max-width, auto margin,
     space-between) that also matches this element. */
  max-width: none;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: flex-start;
  gap: var(--sp-2);
  /* No side padding: the panel body already pads, so the pills line up with
     the search box and two fit per line in the desktop column. */
  padding: var(--sp-2) 0 0;
}

.ws2-tool-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  box-sizing: border-box;
  min-height: 32px;
  max-width: 100%;
  padding: 0 var(--sp-3);
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-alt, #eef1f5);
  color: var(--text);
  font-size: var(--text-sm);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}

.ws2-tool-pill:hover {
  text-decoration: none;
  background: var(--bg-alt);
}

.ws2-tool-pill[aria-current="true"] {
  border-color: var(--primary);
  background: var(--primary);
  color: var(--on-primary);
}

.ws2-tool-pill-count {
  font-weight: 400;
  font-variant-numeric: tabular-nums;
}

.ws2-pending-approval-section {
  padding: 0 var(--sp-2, 8px) var(--sp-2, 8px);
  margin-bottom: var(--sp-2, 8px);
  border-bottom: 1px solid var(--border, rgba(0, 0, 0, 0.08));
}

.ws2-pending-approval-header {
  font-size: var(--text-xs, 0.75rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--warning, #d9a441);
  padding: var(--sp-1, 4px) var(--sp-1, 4px) var(--sp-2, 8px);
}

.ws2-pending-thread-item {
  background: var(--warning-bg, #fff8e6);
  border-radius: var(--radius-sm);
}

.ws2-pending-publish-form {
  margin: 0;
}

.ws2-pending-publish-btn {
  font-size: var(--text-xs, 0.75rem);
  font-weight: 600;
  padding: 2px 10px;
  border-radius: 999px;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: #fff;
  cursor: pointer;
}

.ws2-pending-publish-btn:hover {
  opacity: 0.9;
}

/* Thread-detail publish control (bug fix, 2026-09): the Pending Approval
   section above previously rendered ONLY on the workspace list sidebar --
   the detail page (this panel) had no equivalent, so a platform_admin
   deep-linked here from a notification/reminder could read pending
   content but not publish it. Reuses .ws2-pending-publish-form /
   .ws2-pending-publish-btn for the form/button so the action itself is
   visually and functionally identical to the sidebar's; this banner is
   just the detail-page wrapper around it. */
.ws2-detail-pending-approval-banner {
  display: flex;
  align-items: center;
  gap: var(--sp-3, 12px);
  background: var(--warning-bg, #fff8e6);
  border: 1px solid var(--warning, #d9a441);
  border-radius: var(--radius-sm);
  padding: var(--sp-2) var(--sp-3);
  margin-bottom: var(--sp-3);
  font-size: var(--text-sm);
}

.ws2-detail-pending-approval-icon {
  flex: none;
  color: var(--warning, #d9a441);
}

.ws2-detail-pending-approval-text {
  flex: 1;
}

.ws2-detail-publish-btn {
  flex: none;
}

.ws2-flagged-banner {
  background: var(--warning-bg, #fff8e6);
  border: 1px solid var(--warning, #d9a441);
  border-radius: var(--radius-sm);
  padding: var(--sp-2) var(--sp-3);
  margin-bottom: var(--sp-3);
  font-weight: 600;
  font-size: var(--text-sm);
}

.ws2-legacy-qa-banner {
  background: var(--surface-alt, #eef1f5);
  border-radius: var(--radius-sm);
  padding: var(--sp-2) var(--sp-3);
  margin-bottom: var(--sp-3);
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.ws2-feedback-tag {
  display: inline-block;
  font-size: var(--text-xs, 0.75rem);
  font-weight: 600;
  padding: 1px 6px;
  margin-left: var(--sp-2);
  border-radius: var(--radius-sm);
  background: var(--surface-alt, #eef1f5);
  color: var(--text-muted);
}

/* P2 (ws-usefulness): mirrors .ws2-feedback-tag exactly -- a small,
   non-visual-redesign tag identifying a legacy-QA opening message as the
   original question, since question_messages() now tags it
   kind: "question" instead of the generic "message". */
.ws2-question-tag {
  display: inline-block;
  font-size: var(--text-xs, 0.75rem);
  font-weight: 600;
  padding: 1px 6px;
  margin-left: var(--sp-2);
  border-radius: var(--radius-sm);
  background: var(--surface-alt, #eef1f5);
  color: var(--text-muted);
}

/* D-0300: marks a message that replaces an earlier one -- same chip shape as
   the feedback/question tags. */
.ws2-update-tag {
  display: inline-block;
  font-size: var(--text-xs, 0.75rem);
  font-weight: 600;
  padding: 1px 6px;
  margin-left: var(--sp-2);
  border-radius: var(--radius-sm);
  background: var(--surface-alt, #eef1f5);
  color: var(--text-muted);
}

/* P2 (ws-usefulness): the question's free-text "context" label
   (question_to_virtual_thread's qa_context), shown under the conversation
   title for a legacy-QA thread. */
.ws2-qa-context-subtitle {
  margin: 2px 0 0;
  font-size: var(--text-sm);
  color: var(--text-muted);
}

/* --- Middle panel: conversation --- */

.ws2-conversation-panel {
  /* Header + scrollable body + reply bar stack vertically inside the panel. */
  max-height: calc(70vh + 96px);
}

.ws2-conversation-title {
  /* Renders as an h1 for document heading structure (a11y hardening) --
     reset all UA heading defaults so it stays visually identical to the
     prior span. */
  margin: 0;
  font: inherit;
  font-weight: inherit;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ws2-conversation-body {
  flex: 1;
}

.ws2-inline-error {
  background: var(--error-bg);
  border: 1px solid var(--error);
  border-radius: var(--radius-sm);
  padding: var(--sp-2) var(--sp-3);
  margin-bottom: var(--sp-3);
  color: var(--error);
  font-size: var(--text-sm);
}

.ws2-reply-bar {
  border-top: 1px solid var(--border);
  padding: var(--sp-3);
}

.ws2-reply-bar form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.ws2-reply-textarea {
  width: 100%;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  font-family: inherit;
  resize: vertical;
}

.ws2-reply-submit {
  align-self: flex-end;
}

/* Busy state while a reply is in flight -- dims the button and swaps the
   cursor without touching the SVG send icon or its aria-label markup, so
   the icon never gets clobbered and a stuck-disabled button never loses
   its visual affordance (see the ws2-reply-form submit guard). */
.ws2-reply-submit-busy,
.ws2-reply-submit:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* Decision #7 (LOGRO_WS_ENTER_SEND): persistent drag-and-drop zone rendered
   below the submit/attach buttons in the reply composer. Reuses the shared
   .ws-drop-zone dashed-box look; this modifier just trims the padding/font
   so it reads as a compact affordance rather than the larger new-thread
   compose dropzone it's visually based on. */
.ws2-reply-drop-zone {
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--text-sm);
}

/* --- Right panel: canvas --- */

.ws2-canvas-slot {
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  padding: var(--sp-3);
  margin-bottom: var(--sp-4);
}

.ws2-canvas-slot:last-child {
  margin-bottom: 0;
}

.ws2-canvas-slot-label {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: var(--sp-2);
}

.ws2-canvas-placeholder {
  color: var(--text-muted);
  font-size: var(--text-sm);
  margin: 0;
}

/* Thread-owns-the-canvas: a slot with nothing relevant to show (e.g. the
   downloads slot before any job has produced an output) renders with no
   label and no placeholder text at all -- see
   _workspace_v2_canvas_downloads.html. Hidden, not removed: the element
   keeps its id and (when applicable) its polling hx-* attributes, so htmx
   can still swap it back in once there's something to show. */
.ws2-canvas-slot-empty {
  display: none;
}

/* Role-assignment / job-picker forms rendered in the empty-state canvas
   slot (geo census/provider-roster picks, multi-file disruption
   claims/proposed/current-network picks, proposal source-job picks). These
   reuse .field select's existing chevron/padding/focus-ring styling (see
   the "Unified form controls" block above) but need a much tighter
   margin-bottom than that block's full-page-form default so several stacked
   fields fit the compact canvas sidebar without excess whitespace. */
.ws2-canvas-role-form .field {
  margin-bottom: var(--sp-3);
}

.ws2-canvas-role-form .field label {
  display: block;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-secondary);
  margin-bottom: var(--sp-1);
}

/* "Already ran this outside a conversation?" link-job form. Deliberately
   its own class (not .ws2-canvas-slot-label) -- that shared class forces
   uppercase, which read as a shouty section header on this plain question.
   The input+button row: both controls share one height so they read as a
   single aligned control, matching the 38px min-height the fullpage-glass
   override already sets on the input (see workspace_glass.css) -- picked
   up here as the row's shared height so the button matches without a
   fullpage-only special case. */
.ws2-canvas-link-job-label {
  display: block;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin-bottom: var(--sp-2);
}

.ws2-canvas-link-job-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.ws2-canvas-link-job-row input[type="text"] {
  flex: 1 1 auto;
  min-width: 0;
  height: 38px;
  box-sizing: border-box;
}

.ws2-canvas-link-job-row button {
  /* .btn-sm sets min-height:40px, which -- combined with height -- would
     win over this 38px per the CSS height/min-height precedence rule and
     leave the button 2px taller than the input. Override min-height too so
     they actually match. */
  height: 38px;
  min-height: 38px;
  box-sizing: border-box;
  flex-shrink: 0;
}

.ws2-canvas-job-status,
.ws2-canvas-downloads-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.ws2-canvas-job-name {
  font-weight: 600;
  font-size: var(--text-sm);
}

.ws2-canvas-job-state {
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

.ws2-canvas-job-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  flex-wrap: wrap;
}

.ws2-canvas-job-spinner {
  display: inline-block;
  margin-right: var(--sp-1);
  vertical-align: middle;
}

.ws2-canvas-job-message {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin: 0;
}

.ws2-canvas-job-meta {
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin: 0;
}

.ws2-canvas-download-item {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.ws2-canvas-download-meta {
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin: 0;
}

/* Iterate-loop affordances in the job-status card: revision marker, the
   "this looks correct" sign-off, and the one-line hints beside them. */
.ws2-canvas-job-format {
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin: 0 0 var(--sp-1);
}

.ws2-canvas-job-revision {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-secondary);
  margin: 0;
}

.ws2-canvas-job-accepted {
  font-size: var(--text-sm);
  font-weight: 600;
  color: #2f9e6f;
  margin: 0;
}

.ws2-canvas-job-hint {
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin: var(--sp-1) 0 0;
}

.ws2-canvas-accept-form {
  margin: 0;
}

/* One-tap "This is wrong" / "Walk me through it": one wrapping row. */
.ws2-canvas-followup-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.ws2-canvas-followup-form {
  margin: 0;
}

/* "Attach in the chat" / "Go to the chat" under an empty-state example. */
.ws2-canvas-empty-action {
  margin-top: var(--sp-2);
}

.ws2-canvas-repricing-fb-hint {
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin: 0;
}

/* --- Repricing canvas cards: results summary, Q&A banner, feedback --- */

.ws2-canvas-repricing-summary-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
}

.ws2-canvas-repricing-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--sp-2);
  border-radius: var(--radius-sm);
  background: var(--bg-alt);
}

.ws2-canvas-repricing-stat-highlight {
  border: 1px solid var(--border);
}

.ws2-canvas-repricing-stat-highlight.is-unverified {
  border-color: var(--savings-yellow);
}

.ws2-canvas-repricing-stat-label {
  font-size: var(--text-sm);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.ws2-canvas-repricing-stat-value {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--text);
}

/* Geo-access headline cards: the answer, always open at the top of the
   results card (one card per category: access %, then the counts behind it). */
.ws2-geo-headline {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
}

.ws2-geo-headline-card {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-sm);
  background: var(--bg-alt);
  border: 1px solid var(--border);
}

.ws2-geo-headline-value {
  font-size: 1.75rem;
  line-height: 1.15;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.ws2-geo-headline-detail {
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.ws2-canvas-repricing-unverified-caption {
  font-size: var(--text-sm);
  color: var(--savings-yellow);
}

.ws2-canvas-repricing-savings-green {
  color: var(--savings-green);
}
.ws2-canvas-repricing-savings-yellow {
  color: var(--savings-yellow);
}
.ws2-canvas-repricing-savings-red {
  color: var(--savings-red);
}

.ws2-canvas-repricing-summary-download {
  margin-bottom: var(--sp-3);
}

.ws2-canvas-repricing-detail-row {
  border-top: 1px solid var(--border);
  padding-top: var(--sp-2);
  margin-top: var(--sp-2);
}

.ws2-canvas-repricing-detail-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  cursor: pointer;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text);
  list-style: none;
}

.ws2-canvas-repricing-detail-summary::-webkit-details-marker {
  display: none;
}

.ws2-canvas-repricing-count-chip {
  font-size: var(--text-sm);
  font-weight: 700;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--bg-alt);
  color: var(--text-secondary);
}

.ws2-canvas-repricing-count-chip.is-error {
  background: var(--savings-red);
  color: #fff;
}

.ws2-canvas-repricing-count-chip.is-success {
  background: var(--savings-green);
  color: #fff;
}

.ws2-canvas-repricing-anomaly-list,
.ws2-canvas-repricing-integrity-list {
  list-style: none;
  margin: var(--sp-2) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.ws2-canvas-repricing-anomaly-item,
.ws2-canvas-repricing-integrity-item {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

.ws2-canvas-repricing-anomaly-dot,
.ws2-canvas-repricing-integrity-icon {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-top: 5px;
  background: var(--text-muted);
}

.ws2-canvas-repricing-anomaly-dot-critical,
.ws2-canvas-repricing-anomaly-dot-error {
  background: var(--savings-red);
}

.ws2-canvas-repricing-anomaly-dot-warning {
  background: var(--savings-yellow);
}

.ws2-canvas-repricing-anomaly-dot-info {
  background: var(--text-muted);
}

.ws2-canvas-repricing-anomaly-label {
  font-weight: 600;
  color: var(--text);
}

.ws2-canvas-repricing-anomaly-detail {
  margin-top: 2px;
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.ws2-canvas-repricing-integrity-item.passed
  .ws2-canvas-repricing-integrity-icon {
  background: var(--savings-green);
}

.ws2-canvas-repricing-integrity-item.failed
  .ws2-canvas-repricing-integrity-icon {
  background: var(--savings-red);
}

.ws2-canvas-repricing-integrity-failure {
  display: block;
  font-size: var(--text-sm);
  color: var(--savings-red);
  margin-top: 2px;
}

.ws2-canvas-repricing-integrity-issues {
  margin-top: var(--sp-2);
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

.ws2-canvas-repricing-integrity-issues ul {
  margin: var(--sp-1) 0 0;
  padding-left: var(--sp-3);
}

/* Q&A banner */

.ws2-canvas-repricing-qa-banner {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  padding: var(--sp-2);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
}

.ws2-canvas-repricing-qa-blocked {
  background: color-mix(in srgb, var(--savings-yellow) 12%, transparent);
  border: 1px solid var(--savings-yellow);
}

.ws2-canvas-repricing-qa-review {
  background: color-mix(in srgb, var(--savings-green) 10%, transparent);
  border: 1px solid var(--savings-green);
}

.ws2-canvas-repricing-qa-icon {
  flex: 0 0 auto;
}

.ws2-canvas-repricing-qa-body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}

.ws2-canvas-repricing-qa-text {
  margin: 0;
  color: var(--text);
}

.ws2-canvas-repricing-qa-links a {
  color: var(--text);
  font-weight: 600;
}

/* Feedback card */

.ws2-canvas-repricing-fb-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
}

.ws2-canvas-repricing-fb-form textarea {
  width: 100%;
  resize: vertical;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--sp-2);
  font: inherit;
  color: var(--text);
  background: var(--bg-alt);
}

.ws2-canvas-repricing-fb-file-label {
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.ws2-canvas-repricing-fb-history-label {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: var(--sp-2);
}

.ws2-canvas-repricing-fb-history {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.ws2-canvas-repricing-fb-item {
  border-top: 1px solid var(--border);
  padding-top: var(--sp-2);
}

.ws2-canvas-repricing-fb-meta {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  margin: 0 0 var(--sp-1);
  font-size: var(--text-sm);
}

.ws2-canvas-repricing-fb-status {
  font-weight: 700;
  text-transform: capitalize;
  color: var(--text-secondary);
}

.ws2-canvas-repricing-fb-status-resolved,
.ws2-canvas-repricing-fb-status-addressed {
  color: var(--savings-green);
}

.ws2-canvas-repricing-fb-status-reopened {
  color: var(--savings-yellow);
}

.ws2-canvas-repricing-fb-meta-sep {
  color: var(--text-muted);
}

.ws2-canvas-repricing-fb-meta-date {
  color: var(--text-muted);
}

.ws2-canvas-repricing-fb-text {
  margin: 0 0 var(--sp-1);
  font-size: var(--text-sm);
  color: var(--text);
}

.ws2-canvas-repricing-fb-attachments {
  margin: 0 0 var(--sp-1);
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.ws2-canvas-repricing-fb-resolution {
  margin: var(--sp-1) 0 0;
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

.ws2-canvas-repricing-fb-next-step {
  margin: var(--sp-1) 0 0;
  font-size: var(--text-sm);
  color: var(--text-muted);
  font-style: italic;
}

.ws2-canvas-repricing-fb-resolution-card {
  margin-top: var(--sp-2);
  padding: var(--sp-2);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--savings-green) 8%, transparent);
  border: 1px solid var(--savings-green);
}

.ws2-canvas-repricing-fb-resolution-header {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  font-weight: 700;
  font-size: var(--text-sm);
  color: var(--savings-green);
  margin-bottom: var(--sp-1);
}

.ws2-canvas-repricing-fb-resolution-date {
  margin-left: auto;
  font-weight: 400;
  color: var(--text-muted);
}

.ws2-canvas-repricing-fb-resolution-text {
  margin: 0 0 var(--sp-2);
  font-size: var(--text-sm);
  color: var(--text);
}

.ws2-canvas-repricing-fb-thread {
  margin-top: var(--sp-2);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.ws2-canvas-repricing-fb-thread-message {
  padding: var(--sp-2);
  border-radius: var(--radius-sm);
  background: var(--bg-alt);
}

.ws2-canvas-repricing-fb-thread-msg-header {
  display: flex;
  justify-content: space-between;
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin-bottom: 2px;
}

.ws2-canvas-repricing-fb-thread-author {
  font-weight: 700;
  color: var(--text-secondary);
}

.ws2-canvas-repricing-fb-thread-text {
  font-size: var(--text-sm);
  color: var(--text);
}

.ws2-canvas-repricing-fb-thread-attachments {
  margin-top: var(--sp-1);
  font-size: var(--text-sm);
  color: var(--text-muted);
}

.ws2-canvas-repricing-fb-reply-section {
  margin-top: var(--sp-2);
}

.ws2-canvas-repricing-fb-reply-prompt {
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin: 0 0 var(--sp-1);
}

.ws2-canvas-repricing-fb-reply-form textarea {
  width: 100%;
  resize: vertical;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--sp-2);
  font: inherit;
  color: var(--text);
  background: var(--bg-alt);
}

.ws2-canvas-repricing-fb-reply-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-1);
}

.ws2-canvas-repricing-fb-file-input-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
}

.ws2-canvas-repricing-fb-file-upload-label {
  position: relative;
  cursor: pointer;
}

.ws2-canvas-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px; /* PU-05: no desktop exemption */
  height: 44px;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  background: transparent;
  border: none;
  cursor: pointer;
  transition:
    color 0.15s,
    background 0.15s,
    transform 0.18s;
}

.ws2-canvas-toggle:hover {
  color: var(--text);
  background: var(--bg-alt);
}

/* In the workspace shell the toggle uses the ws2 text scale on the light look */
body.ws2-fullpage .ws2-canvas-toggle {
  color: var(--ws2-text-tertiary);
}
body.ws2-fullpage .ws2-canvas-toggle:hover {
  background: var(--sub);
  color: var(--ws2-text-primary);
}

/* Resize handles: hidden in non-fullpage card layout; shown in fullpage mode. */
.ws2-resize-handle {
  display: none;
}

/* Collapsed canvas: shrink the right column to a slim rail with just the
   toggle button, hide its body and title text. A collapse-to-rail pattern
   scoped to this shell only (the app-shell nav that used a similar pattern
   was retired, see docs/decisions/2026-07-29-legacy-ui-retirement-policy.md). */
body.ws2-canvas-collapsed .ws2-shell {
  grid-template-columns: 220px minmax(0, 1fr) 44px;
}

body.ws2-canvas-collapsed .ws2-canvas-panel .ws2-panel-body,
body.ws2-canvas-collapsed .ws2-canvas-panel-title-text {
  display: none;
}

body.ws2-canvas-collapsed .ws2-canvas-toggle svg {
  transform: rotate(180deg);
}

body.ws2-canvas-collapsed .ws2-canvas-panel .ws2-panel-header {
  justify-content: center;
  padding: 0; /* the 44px rail holds exactly the 44px toggle */
}

/* Responsive: stack panels on narrow screens instead of squeezing 3 columns.
   Canvas panel becomes a collapsed-by-default section the client can expand;
   thread list gets a fixed short height so the conversation stays reachable
   without excessive scrolling.
   NOTE (2026-08-25 design-system audit): this breakpoint is intentionally
   900px, not the platform's usual 640px -- a 3-column shell needs to
   collapse to single-column earlier than a single-column page does. Do not
   "fix" this to match 640px. */
@media (max-width: 900px) {
  .ws2-shell,
  body.ws2-canvas-collapsed .ws2-shell {
    grid-template-columns: 1fr;
  }

  .ws2-thread-list-panel .ws2-panel-body {
    max-height: 220px;
  }

  .ws2-conversation-panel {
    max-height: none;
  }

  .ws2-conversation-body {
    max-height: 50vh;
  }

  .ws2-canvas-panel .ws2-panel-body {
    max-height: 50vh;
  }

  body.ws2-canvas-collapsed .ws2-canvas-panel .ws2-panel-body,
  body.ws2-canvas-collapsed .ws2-canvas-panel-title-text {
    display: none;
  }
}

/* =========================================================
   WS2 FULLPAGE MODE
   body.ws2-fullpage: workspace takes over the entire viewport.
   Hides global topbar + footer; resets main's margin/width.
   Three flat columns on the light Logro brand (D-0214): grey side
   columns, a white conversation, brand tokens only.
   All rules are scoped under body.ws2-fullpage so they cannot
   bleed into any other page.
   ========================================================= */

/* --- Custom properties scoped to the ws2 fullpage shell --- */
body.ws2-fullpage {
  /* Light hierarchy: the side columns are the grey --sub, the conversation
     panel the white --canvas, headers white. Every value is a logro-brand.css
     token; nothing here declares a colour of its own. */
  --ws2-bg-sidebar: var(--sub);
  --ws2-bg-content: var(--canvas);
  --ws2-bg-canvas: var(--sub);
  --ws2-bg-glass: var(--canvas);
  --ws2-bg-glass-w: var(--canvas);
  --ws2-border: var(--line);
  --ws2-text-primary: var(--body);
  --ws2-text-secondary: var(--ink-2);
  --ws2-text-tertiary: var(--ink-3);
  /* Brand blue: the one accent. Edges, links and icons only, never a fill
     (fills are navy, --ws2-accent-fill in workspace_glass.css). */
  --ws2-accent: var(--brand-blue);
  --ws2-accent-warm: var(--brand-blue);
  --ws2-hover: var(--sub);
  --ws2-active: var(--select);

  background: var(--page);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* 1. Suppress global chrome (belt-and-suspenders: the shell overrides the
   base template's top-bar block, so no header is rendered at all) */
body.ws2-fullpage > header,
body.ws2-fullpage > footer {
  display: none !important;
}
/* The global stale-notifications banner is a body child (base.html) meant
   to float above the normal page chrome -- it bleeds into the fullpage
   shell (z-index 200) and gets covered by it, so it must be suppressed
   here alongside the rest of the global chrome. */
body.ws2-fullpage .notif-stale-banner {
  display: none !important;
}
body.ws2-fullpage {
  overflow: hidden; /* prevent body scroll — only ws2 panels scroll internally */
}
body.ws2-fullpage main {
  margin-left: 0 !important;
  width: 100% !important;
  max-width: none !important;
  padding: 0 !important;
}

/* 2. Shell: fixed full-viewport grid, zero gap; resize handles occupy 4px columns */
body.ws2-fullpage .ws2-shell {
  position: fixed;
  inset: 0;
  height: 100vh;
  /* Chat is the star: it gets flexible 1fr space (bounded by a 360px floor
     via minmax so it can never be crushed). Thread list is fixed via
     --ws2-left-w, canvas is fixed via --ws2-canvas-w. Left handle resizes
     the thread list; right handle resizes the canvas (drag left grows it,
     drag right shrinks it back toward chat). */
  grid-template-columns:
    var(--ws2-left-w, 320px) 4px minmax(360px, 1fr)
    4px var(--ws2-canvas-w, 420px);
  gap: 0;
  align-items: stretch;
  /* No font-family override here -- this used to hardcode a system-font
     stack, which silently dropped the brand font (Figtree, --sans, set
     on body) the instant fullpage mode kicked in. Inherit from body instead;
     letter-spacing stays as a deliberate fullpage-only tightening. */
  letter-spacing: -0.01em;
  z-index: 200;
  /* No transition here: --ws2-canvas-w now drives a live grid track during
     pointer drag, and animating it would make every drag frame lag behind
     the cursor. The toggle collapse/expand still reads as instant, which
     matches the left handle's (already transition-less) behavior. */
}

/* 2a. Session-expiry banner must float above the fixed full-viewport shell
   (z-index 200) or it renders behind it and the user never sees the
   "your draft was saved" message when their session times out mid-compose. */
body.ws2-fullpage .ws-session-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 300;
  border-radius: 0;
  margin-bottom: 0;
}
/* Collapsed: the canvas rail pins to the far right edge and the CHAT absorbs
   all the reclaimed space. The 360px floor matches the expanded grid's chat
   track (and MIN_MID in the drag script) so the column has one consistent
   minimum in both states. */
body.ws2-fullpage.ws2-canvas-collapsed .ws2-shell {
  grid-template-columns: var(--ws2-left-w, 320px) 4px minmax(360px, 1fr) 48px;
}
body.ws2-fullpage.ws2-canvas-collapsed .ws2-resize-right {
  display: none;
}

/* 2b. Resize handles: visible in fullpage mode as 4px drag zones. The grid
   track itself stays 4px (keeps column math simple) but position:relative
   plus a ::before that overhangs a few px on each side widens the actual
   grabbable/hoverable area — a bare 4px target is hard to hit with a mouse.
   The highlight background stays on the handle itself so it still reads as
   a thin 4px line rather than the wider invisible hit zone. */
body.ws2-fullpage .ws2-resize-handle {
  display: block;
  position: relative;
  height: 100%;
  cursor: col-resize;
  background: transparent;
  transition: background 0.15s;
  z-index: 10;
  user-select: none;
}
body.ws2-fullpage .ws2-resize-handle::before {
  content: "";
  position: absolute;
  inset: 0 -3px;
  cursor: col-resize;
}
body.ws2-fullpage .ws2-resize-handle:hover,
body.ws2-fullpage .ws2-resize-handle.dragging {
  background: var(--brand-blue);
}

/* Applied to <body> for the duration of a fullpage drag so text selection
   doesn't fire mid-drag and the cursor stays col-resize even when the
   pointer strays off the (now widened) handle hit area. */
body.ws2-resizing {
  user-select: none;
  cursor: col-resize;
}

/* 3. Remove card chrome; make each panel a flex column that fills height */
body.ws2-fullpage .ws2-panel {
  border: none;
  border-radius: 0;
  height: 100vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: none;
}

/* 4. Column dividers + panel background colors */
body.ws2-fullpage .ws2-thread-list-panel {
  background: var(--ws2-bg-sidebar);
  border-right: 1px solid var(--ws2-border);
}
body.ws2-fullpage .ws2-canvas-panel {
  border-left: 1px solid var(--ws2-border);
  background: var(--ws2-bg-canvas);
}
body.ws2-fullpage .ws2-conversation-panel {
  background: var(--ws2-bg-content);
  /* Reset the base .ws2-conversation-panel max-height clamp (calc(70vh +
     96px)) -- in fullpage mode the panel is already a flex column with
     .ws2-conversation-body flex:1 and .ws2-reply-bar flex-shrink:0, so it
     should fill the full column height instead of stopping short and
     leaving dead space with the composer hidden below the fold. */
  max-height: none;
}

/* 5. Panel header: flat white bar with a line under it */
body.ws2-fullpage .ws2-panel-header {
  flex-shrink: 0;
  padding: 0 16px;
  height: 52px;
  border-bottom: 1px solid var(--ws2-border);
  border-radius: 0;
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ws2-text-primary);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
body.ws2-fullpage .ws2-thread-list-panel .ws2-panel-header,
body.ws2-fullpage .ws2-canvas-panel .ws2-panel-header {
  background: var(--ws2-bg-glass);
}
body.ws2-fullpage .ws2-conversation-panel .ws2-panel-header {
  background: var(--ws2-bg-glass-w);
}

/* Left panel brand + tenant (workspace) switcher, top-left.
   The switcher trigger renders identically for single- and multi-tenant
   users: today it opens a menu that confirms the current workspace; when
   cross-tenant membership ships the same markup handles real switching. */
body.ws2-fullpage .ws2-brand-header {
  justify-content: space-between;
}

/* Wrapper element that positions the floating menu. */
body.ws2-fullpage .ws2-tenant {
  position: relative;
  min-width: 0;
}

/* Single-tenant static brand (no interaction). */
body.ws2-fullpage .ws2-tenant-brand {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

/* Trigger button: looks like a plain brand label until hovered/active. */
body.ws2-fullpage .ws2-tenant-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  padding: 4px 6px;
  margin: -4px -6px;
  background: none;
  border: none;
  border-radius: var(--radius);
  cursor: pointer;
  color: var(--ws2-text-primary);
  font-family: inherit;
  transition: background 0.12s;
}
body.ws2-fullpage .ws2-tenant-trigger:hover {
  background: var(--ws2-hover);
}
body.ws2-fullpage .ws2-tenant-trigger:focus-visible {
  outline: 2px solid var(--ws2-accent);
  outline-offset: 1px;
}

body.ws2-fullpage .ws2-brand-mark {
  display: flex;
  flex-shrink: 0;
  line-height: 0;
  border-radius: var(--radius);
}
/* The workspace mark: a letter in brand blue on the select tint (the brand's
   tinted icon tile). The SVG carries no colours of its own. */
body.ws2-fullpage .ws2-brand-mark rect {
  fill: var(--brand-blue-tint);
}
body.ws2-fullpage .ws2-brand-mark text {
  fill: var(--brand-blue);
}
body.ws2-fullpage .ws2-brand-name {
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ws2-text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Chevron: always visible (affordance), dims at rest. */
body.ws2-fullpage .ws2-tenant-chevron {
  flex-shrink: 0;
  color: var(--ws2-text-tertiary);
  transition:
    transform 0.15s,
    color 0.12s;
}
body.ws2-fullpage
  .ws2-tenant-trigger[aria-expanded="true"]
  .ws2-tenant-chevron {
  transform: rotate(180deg);
  color: var(--ws2-text-secondary);
}

/* Dropdown menu */
body.ws2-fullpage .ws2-tenant-menu {
  position: absolute;
  top: calc(100% + 10px);
  left: -6px;
  z-index: 120;
  min-width: 200px;
  background: var(--canvas);
  border: 1px solid var(--ws2-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-float);
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
body.ws2-fullpage .ws2-tenant-menu-label {
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ws2-text-tertiary);
  padding: 4px 8px 6px;
}
body.ws2-fullpage .ws2-tenant-option {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--radius);
  border: none;
  background: none;
  color: var(--ws2-text-primary);
  font-family: inherit;
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  text-align: left;
  transition: background 0.1s;
}
body.ws2-fullpage .ws2-tenant-option:hover {
  background: var(--ws2-hover);
}
body.ws2-fullpage .ws2-tenant-option.is-current {
  background: var(--ws2-active);
}
body.ws2-fullpage .ws2-tenant-option:focus-visible {
  outline: 2px solid var(--ws2-accent);
  outline-offset: -2px;
}
body.ws2-fullpage .ws2-tenant-option-mark {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  background: var(--brand-blue-tint);
  border-radius: var(--radius);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-sm);
  font-weight: 800;
  color: var(--ws2-accent);
  line-height: 1;
}
body.ws2-fullpage .ws2-tenant-option-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
body.ws2-fullpage .ws2-tenant-check {
  flex-shrink: 0;
  color: var(--ws2-accent);
}

body.ws2-fullpage .ws2-brand-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

/* Header left side: title stacked over the (optional) Q&A context subtitle. */
.ws2-header-titlewrap {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

/* Header right side: tool badge / status pill,
   kept from being squeezed by the title's ellipsis overflow. */
.ws2-header-right {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

/* Conversation panel header: bigger title -- stronger hierarchy against the
   15px/400 message body so the two are no longer easy to confuse. */
body.ws2-fullpage
  .ws2-conversation-panel
  .ws2-panel-header
  .ws2-conversation-title {
  font-size: 16px;
  font-weight: 650;
  color: var(--ws2-text-primary);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* Quiet subtitle under the title in the workspace shell -- left-aligned,
   not the centered flex-sibling layout the base rule above uses. */
body.ws2-fullpage .ws2-qa-context-subtitle {
  font-size: var(--text-sm);
  color: var(--ws2-text-secondary);
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 6. Panel body: fills remaining height, scrolls internally */
body.ws2-fullpage .ws2-panel-body {
  flex: 1;
  max-height: none;
  overflow-y: auto;
  padding: 16px;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}

/* Thin webkit scrollbars -- neutral grey from the line tokens */
body.ws2-fullpage ::-webkit-scrollbar {
  width: 5px;
  height: 5px;
}
body.ws2-fullpage ::-webkit-scrollbar-track {
  background: transparent;
}
body.ws2-fullpage ::-webkit-scrollbar-thumb {
  background: var(--line);
  border-radius: var(--radius);
}
body.ws2-fullpage ::-webkit-scrollbar-thumb:hover {
  background: var(--control);
}

/* 7. Conversation body: fills between header and composer */
body.ws2-fullpage .ws2-conversation-body {
  flex: 1;
  max-height: none;
  overflow-y: auto;
  padding: 24px 28px;
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--ws2-text-primary);
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}

/* Round 2 fix: a short thread otherwise renders its messages pinned to the
   TOP of .ws2-conversation-body, leaving a large dead gap above the
   composer -- standard chat behavior anchors the transcript to the BOTTOM
   so the newest message sits just above the input. `.ws-timeline` becomes
   the flex container that packs its message/event children against its own
   bottom edge; `min-height: 100%` (of the scrollable `.ws2-conversation-body`
   ancestor above, which has a definite height via flex:1) gives
   justify-content:flex-end room to operate when there are only a few
   messages -- without it, `.ws-timeline`'s box would just be exactly as
   tall as its content and there'd be no free space left to pack against.
   Overflow/scrolling itself stays on the non-flex `.ws2-conversation-body`
   ancestor (not on `.ws-timeline` itself), which is the safe pattern for
   this trick -- putting overflow and justify-content:flex-end on the SAME
   flex container is the version with a known top-content-clipping bug in
   some engines when content overflows. A long thread's `.ws-timeline` just
   grows past 100% naturally (min-height is a floor, not a cap) and scrolls
   normally, oldest message at the top. */
body.ws2-fullpage .ws-timeline {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: 100%;
}

/* 8. Message bubbles: flat, 6px corners, a line border */
body.ws2-fullpage .ws-message {
  margin-bottom: 20px;
}
body.ws2-fullpage .ws-message-header {
  display: flex;
  align-items: baseline;
  /* flex-start so author + kind-tag group left; time gets margin-left:auto
     to anchor to the right. Overrides base `justify-content: space-between`
     which scattered middle children (e.g. the Question tag) to mid-row. */
  justify-content: flex-start;
  gap: 8px;
  margin-bottom: 4px;
  font-size: var(--text-sm);
  color: var(--ws2-text-tertiary);
}
body.ws2-fullpage .ws-message-header strong {
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ws2-text-secondary);
}
body.ws2-fullpage .ws-message-time {
  font-size: var(--text-sm);
  color: var(--ws2-text-tertiary);
  /* Anchor time to the right regardless of how many items precede it
     (author name alone, or author + Question/kind tag). */
  margin-left: auto;
}
/* Client/user messages: right-leaning with subtle bg
   background + border-radius removed (2026-08-25 design-system audit,
   finding #5): both were fully shadowed by workspace_glass.css:127-134
   (identical selector, later load order, same specificity) and dead code.
   `border` is ALSO shadowed there but is deliberately left in place
   (out of scope for this fix) as a fallback if workspace_glass.css is ever
   rolled back per that file's own header comment -- padding/max-width/
   float/clear/font-size/color below are genuinely load-bearing (not
   redeclared in workspace_glass.css at all). */
body.ws2-fullpage .ws-message-client .ws-message-body,
body.ws2-fullpage .ws-message-user .ws-message-body {
  border: 1px solid var(--ws2-border);
  padding: 10px 14px;
  display: inline-block;
  /* 85% of the column, but never past a readable measure -- with the canvas
     collapsed the chat column is ~1500px wide and a bare 85% ran single lines
     past 1200px. */
  max-width: min(85%, 78ch);
  float: right;
  clear: both;
  font-size: var(--text-sm);
  color: var(--ws2-text-primary);
  /* A long unbroken token (URL, long filename, no-spaces word) has no break
     opportunity by default, so it overflows the bubble's max-width and can
     blow out the whole message column. overflow-wrap is the standard
     property; word-break is kept alongside it (matching the fallback
     pairing already used for the same problem elsewhere, e.g.
     .secure-link-secret / .secure-field-value above) for older engines. */
  overflow-wrap: break-word;
  word-break: break-word;
}
/* Client/user header: right-align to match the right-floating bubble.
   `row-reverse` + `flex-end` was previously used but placed items at the
   visual left (flex-end in a reversed row = pack at the reversed start =
   visual left). Plain `justify-content: flex-end` in the default row
   direction achieves the correct visual-right alignment. */
body.ws2-fullpage .ws-message-client .ws-message-header,
body.ws2-fullpage .ws-message-user .ws-message-header {
  justify-content: flex-end;
}
/* Round 2 fix: `.ws-message-time` unconditionally sets margin-left:auto
   (below) so the timestamp right-anchors for TEAM/ADMIN headers -- correct
   there, since those headers are left-aligned and need the trailing
   auto-margin to push time to the right edge. But that same auto margin
   also fires here and eats ALL the row's free space, which defeats this
   rule's justify-content:flex-end (an auto margin always wins over
   justify-content) -- the two fought and the author name ended up pinned
   far left while the bubble floats right. Zeroing it here lets
   justify-content:flex-end actually pack author + time together, right
   above the bubble they belong to. */
body.ws2-fullpage .ws-message-client .ws-message-time,
body.ws2-fullpage .ws-message-user .ws-message-time {
  margin-left: 0;
}
/* Team/admin messages: left-aligned.
   background + border-radius removed (2026-08-25 design-system audit,
   finding #5): both were fully shadowed by workspace_glass.css:117-125
   (identical selector, later load order, same specificity) and dead code
   -- background here still carried the pre-rebrand indigo tint. `border`
   is ALSO shadowed there but is deliberately left in place (out of scope
   for this fix) as a fallback if workspace_glass.css is ever rolled back
   per that file's own header comment -- padding/max-width/float/clear/
   font-size/color below are genuinely load-bearing (not redeclared in
   workspace_glass.css at all). */
body.ws2-fullpage .ws-message-team .ws-message-body,
body.ws2-fullpage .ws-message-admin .ws-message-body {
  border: 1px solid var(--ws2-border);
  padding: 10px 14px;
  display: inline-block;
  max-width: min(85%, 78ch);
  float: left;
  clear: both;
  font-size: var(--text-sm);
  color: var(--ws2-text-primary);
  /* Same wrap fix as the client/user bubble above -- see that rule's comment. */
  overflow-wrap: break-word;
  word-break: break-word;
}
/* Attachment row: the message body above it floats (right for client/user,
   left for team/admin, see the two rules above). Without `clear: both` the
   attachments div top-aligns beside/behind the floated bubble instead of
   dropping below it, letting the translucent bubble paint over the
   attachment cards. Clearing plus matching justify-content keeps the row
   below the bubble and aligned to the same side it floats to. */
body.ws2-fullpage .ws-message-attachments {
  clear: both;
}
body.ws2-fullpage .ws-message-client .ws-message-attachments,
body.ws2-fullpage .ws-message-user .ws-message-attachments {
  justify-content: flex-end;
}
body.ws2-fullpage .ws-message-team .ws-message-attachments,
body.ws2-fullpage .ws-message-admin .ws-message-attachments {
  justify-content: flex-start;
}
/* Attachment cards: a white chip with a line border on the conversation.
   Capped in width so a long filename doesn't force a full-bleed slab. A long
   name wraps inside the chip (anywhere, so an unbroken name cannot run past
   the screen edge) instead of being clipped. */
body.ws2-fullpage .ws-attachment-link {
  display: inline-block;
  max-width: min(320px, 100%);
  min-width: 0;
  font-size: 1rem;
  overflow-wrap: anywhere;
  white-space: normal;
  color: var(--ws2-text-primary);
  background: var(--canvas);
  border: 1px solid var(--ws2-border);
}
body.ws2-fullpage .ws-attachment-link:hover {
  background: var(--sub);
}
/* Delivery receipt: right-aligned under the client's bubble, muted until the
   team has seen it, when the ticks take the accent color. Sits inside
   `.ws-message`, so `clear: both` (base rule) drops it below the floated
   bubble and any attachment row, and the message's own clearfix contains it. */
body.ws2-fullpage .ws-receipt {
  margin: 0 2px 0 0;
  font-size: var(--text-sm);
  color: var(--ws2-text-tertiary);
}
body.ws2-fullpage .ws-receipt--seen,
body.ws2-fullpage .ws-receipt--working {
  color: var(--ws2-text-secondary);
}
body.ws2-fullpage .ws-receipt--seen .ws-receipt-icon,
body.ws2-fullpage .ws-receipt--working .ws-receipt-icon {
  color: var(--ws2-accent);
}
body.ws2-fullpage .ws-message::after {
  content: "";
  display: table;
  clear: both;
}
/* Rendered markdown content inside chat bubbles (global reset zeros ol/ul padding). */
body.ws2-fullpage .ws-message-body p {
  margin: 0 0 8px 0;
}
body.ws2-fullpage .ws-message-body p:last-child {
  margin-bottom: 0;
}
body.ws2-fullpage .ws-message-body ol,
body.ws2-fullpage .ws-message-body ul {
  padding-left: 22px;
  margin: 0 0 8px 0;
}
body.ws2-fullpage .ws-message-body li {
  margin-bottom: 4px;
}
body.ws2-fullpage .ws-message-body li:last-child {
  margin-bottom: 0;
}
body.ws2-fullpage .ws-message-body strong {
  font-weight: 600;
}
body.ws2-fullpage .ws-message-body em {
  font-style: italic;
}
body.ws2-fullpage .ws-message-body code {
  font-family: ui-monospace, monospace;
  font-size: 1em;
  background: var(--tile);
  padding: 0.1em 0.35em;
  border-radius: 3px;
}
body.ws2-fullpage .ws-message-body a {
  color: var(--ws2-accent);
  text-decoration: underline;
}
body.ws2-fullpage .ws-message-body blockquote {
  border-left: 3px solid var(--ws2-border);
  padding-left: 10px;
  margin: 0 0 8px 0;
  color: var(--ws2-text-secondary);
}
/* Markdown tables inside chat bubbles. Borders/cell padding/header weight
   already come from the existing generic `table`/`thead th`/`tbody td`
   rules above (light theme) plus `body.ws2-fullpage table` in
   workspace_glass.css (workspace override) -- no need to duplicate those
   here. The one gap: the bubble itself is `display: inline-block; max-width:
   min(85%, 78ch)` (see .ws-message-client/.ws-message-team .ws-message-body
   above), and a table with several columns can need more width than that to
   render without cramming, which would blow out the bubble/column. `display:
   block` on the table (its table-row-group/row/cell children keep their own
   table-* display values via the CSS anonymous-table-box fixup, so the grid
   still renders correctly) turns the table into a normal block box that
   respects max-width and can scroll its own overflow horizontally, instead
   of forcing the whole bubble wider than the chat column. `white-space:
   nowrap` on the cells is required alongside `overflow-x: auto`, not
   optional -- table auto-layout treats a wrapped word as an acceptable
   minimum column width, so without it the browser crams every column down
   to single-character width (verified visually: at a narrow viewport the
   table wrapped every cell letter-by-letter instead of scrolling). nowrap
   forces cells to keep their natural width, which is what actually makes
   the container overflow and the scrollbar appear. */
body.ws2-fullpage .ws-message-body table {
  display: block;
  max-width: 100%;
  overflow-x: auto;
  margin: 0 0 8px 0;
}
body.ws2-fullpage .ws-message-body table th,
body.ws2-fullpage .ws-message-body table td {
  white-space: nowrap;
}
/* Timeline events */
body.ws2-fullpage .ws-timeline-event {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 12px 0;
  font-size: var(--text-sm);
  color: var(--ws2-text-tertiary);
  justify-content: center;
}
body.ws2-fullpage .ws-timeline-label {
  color: var(--ws2-text-secondary);
}
body.ws2-fullpage .ws-timeline-time {
  font-size: var(--text-sm);
  color: var(--ws2-text-tertiary);
}

/* 9. Composer: a white bar pinned to the bottom */
body.ws2-fullpage .ws2-reply-bar {
  flex-shrink: 0;
  position: relative; /* anchors the transient drag-drop overlay */
  border-top: 1px solid var(--ws2-border);
  padding: 12px 16px 14px 16px;
  background: var(--canvas);
}
/* Round 2 fix: the textarea no longer draws its own rounded box -- that box
   moved up to `.ws2-reply-input-row` (below) so attach + textarea + send
   read as ONE pill-shaped input, not a boxed textarea with a disconnected
   button stack floating outside it. The textarea keeps a matching
   transparent border (not `border: none`) so the existing focus-visible
   rule in workspace_glass.css still has a border to color for keyboard
   users -- see that file's "Keyboard-only focus indicators" block. */
body.ws2-fullpage .ws2-reply-textarea {
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius);
  padding: 6px 4px;
  font-size: var(--text-sm);
  resize: none;
  min-height: 22px;
  max-height: 140px;
  /* The global `textarea` rule sets overflow:hidden -- re-enable vertical
     scroll here so a long reply scrolls within the 140px cap instead of
     being clipped with no scrollbar. */
  overflow-y: auto;
  /* flex-grow within `.ws2-reply-input-row`, not just width:100% -- with
     the attach/send buttons as flex siblings, width:100% would overflow the
     row; flex:1 with min-width:0 lets it shrink/grow correctly beside them. */
  flex: 1 1 auto;
  min-width: 0;
  box-sizing: border-box;
  font-family: inherit;
  color: var(--ws2-text-primary);
  transition:
    border-color 0.18s ease,
    box-shadow 0.18s ease;
  box-shadow: none;
}
body.ws2-fullpage .ws2-reply-textarea:focus {
  /* No focus highlight -- keep the input identical focused or not (simple UI).
     Matches the resting (now transparent/borderless) state; no accent ring. */
  outline: none;
  border-color: transparent;
  box-shadow: none;
}
body.ws2-fullpage .ws2-reply-textarea::placeholder {
  color: var(--ws2-text-tertiary);
}
body.ws2-fullpage .ws-drop-zone.ws2-dropzone {
  border: 1.5px dashed var(--control);
  border-radius: var(--radius);
  font-size: var(--text-sm);
  color: var(--ws2-text-tertiary);
  padding: 10px 16px;
  margin-top: 8px;
  text-align: center;
  cursor: pointer;
  height: 44px; /* PU-05 */
  display: flex;
  align-items: center;
  justify-content: center;
  transition:
    border-color 0.12s ease,
    background 0.12s ease;
}
body.ws2-fullpage .ws-drop-zone.ws2-dropzone:hover,
body.ws2-fullpage .ws-drop-zone.ws2-dropzone.ws-drag-over {
  border-color: var(--ws2-accent);
  background: var(--select);
}
body.ws2-fullpage .ws2-reply-bar form {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* Round 2 fix: single-row chat input. `.ws2-reply-input-row` is now ONE
   rounded container -- attach button, flex-grow textarea, send button, left
   to right -- instead of a boxed textarea with a separate right-hand button
   column outside it (the old `.ws2-reply-actions` vertical stack, removed
   below). `align-items: flex-end` keeps both circular buttons anchored to
   the textarea's LAST line as it auto-grows, instead of drifting to the
   vertical center of a tall multi-line textarea. */
body.ws2-fullpage .ws2-reply-input-row {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  background: var(--canvas);
  border: 1px solid var(--control);
  border-radius: var(--radius);
  padding: 5px 6px 5px 12px;
}
body.ws2-fullpage .ws2-reply-submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 44px; /* PU-05 */
  height: 44px;
  /* Neutralize the base .btn padding (12px 22px) + min-height (48px): on a
     34px circle that side-padding exceeds the width and clips the SVG to
     nothing, and min-height forces it taller than the 34px attach button. */
  padding: 0;
  min-height: 0;
  border-radius: var(--radius);
  background: var(--navy);
  border: none;
  color: var(--on-primary);
  font-size: 16px;
  line-height: 0;
  cursor: pointer;
  transition: background 0.12s ease;
  float: none;
  flex-shrink: 0;
}
body.ws2-fullpage .ws2-reply-submit:hover {
  background: var(--navy-hover);
}
body.ws2-fullpage form.ws2-reply-form::after {
  display: none;
}

/* Attach button: ghost/quiet at rest (no fill, no border) so it never
   competes with the accent-filled send button -- a faint background only
   appears on hover to signal it's interactive. */
body.ws2-fullpage .ws2-reply-attach-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; /* PU-05 */
  height: 44px;
  border-radius: var(--radius);
  background: transparent;
  border: none;
  color: var(--ws2-text-tertiary);
  cursor: pointer;
  transition:
    background 0.12s ease,
    color 0.12s ease;
  flex-shrink: 0;
}
body.ws2-fullpage .ws2-reply-attach-btn:hover {
  background: var(--sub);
  color: var(--ws2-text-secondary);
}

/* Decision #7 (LOGRO_WS_ENTER_SEND): persistent drag-and-drop zone below the
   submit/attach buttons, light variant. Mirrors the compact dashed-box
   look already defined for body.ws2-fullpage .ws-drop-zone.ws2-dropzone
   above (same family of composer dropzone), just under its own selector
   since ws2-reply-drop-zone is additive (flag-gated), not a rename.
   Round 2 fix: kept always-visible (product decision -- not removed) but
   slimmed from ~46px to ~30px tall (explicit height + trimmed vertical
   padding/margin) so it reads as a quiet affordance on every thread instead
   of eating real estate like a second input. */
body.ws2-fullpage .ws2-reply-drop-zone {
  border: 1.5px dashed var(--control);
  border-radius: var(--radius);
  font-size: var(--text-sm);
  color: var(--ws2-text-tertiary);
  height: 44px; /* PU-05 */
  padding: 0 16px;
  margin-top: 6px;
  text-align: center;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition:
    border-color 0.12s ease,
    background 0.12s ease;
}
body.ws2-fullpage .ws2-reply-drop-zone:hover,
body.ws2-fullpage .ws2-reply-drop-zone.ws-drag-over {
  border-color: var(--ws2-accent);
  background: var(--select);
}

/* Transient drag-drop overlay -- hidden by default (inline style), shown by
   JS only while a file drag is over the reply bar. Not part of static
   layout flow, so it never reserves space the way the old always-visible
   dashed dropzone did. */
body.ws2-fullpage .ws2-reply-drop-overlay {
  position: absolute;
  inset: 0;
  z-index: 5;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  margin: 6px;
  border: 2px dashed var(--ws2-accent);
  border-radius: var(--radius);
  background: var(--select);
  color: var(--ws2-text-primary);
  font-size: var(--text-sm);
  font-weight: 600;
}

/* 10. Thread list: spaced items with rounded corners */
body.ws2-fullpage .ws2-thread-items {
  gap: 2px;
}

body.ws2-fullpage .ws2-thread-item {
  min-height: 60px;
  padding: 12px 12px;
  border-radius: var(--radius);
  /* flex-start (not center): the title can now wrap to 2 lines (see
     .ws2-thread-item-title below), and centering would float the avatar
     away from the first line whenever a row wraps. */
  align-items: flex-start;
  gap: 10px;
  transition: background 0.15s ease;
}
/* The thread list is the grey --sub, so a hovered row lifts to white. */
body.ws2-fullpage .ws2-thread-item:hover {
  background: var(--canvas);
}
body.ws2-fullpage .ws2-thread-item-active {
  background: var(--ws2-active);
  box-shadow: inset 3px 0 0 var(--navy);
  border-radius: var(--radius);
}

/* Avatar circle */
.ws2-avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-sm);
  font-weight: 600;
  flex-shrink: 0;
  background: var(--tile);
  color: var(--ink-2);
  text-transform: uppercase;
}
/* Avatars: one neutral tone + one distinct awaiting-you (navy on the select
   tint, with a navy edge). */
.ws2-avatar-in-progress,
.ws2-avatar-responded,
.ws2-avatar-resolved,
.ws2-avatar-received {
  background: var(--tile);
  color: var(--ink-2);
}
.ws2-avatar-awaiting-you {
  background: var(--ws2-highlight-soft);
  border: 1px solid var(--navy);
  color: var(--navy);
}

/* Two-row thread item layout */
body.ws2-fullpage .ws2-thread-item-body {
  min-width: 0;
  flex: 1;
  display: flex;
  flex-direction: column;
  /* 6px between the title row and the chip/time row: the 3px step read as
     crowded (ui gate PU-34); 6px is the same step the sidebar uses between
     its small controls. */
  gap: 6px;
}
.ws2-thread-item-row-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
  min-width: 0;
  flex: 1;
}
.ws2-thread-item-row-bottom {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  overflow: hidden;
}
body.ws2-fullpage .ws2-thread-item-title {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--ws2-text-primary);
  /* 2-line clamp instead of single-line ellipsis -- a one-line cutoff was
     truncating most real thread titles down to a handful of characters.
     -webkit-line-clamp is still the only cross-browser-reliable way to do
     multi-line ellipsis; the standard `line-clamp` is added alongside it
     for forward compat as browser support lands. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  line-height: 1.35;
  overflow: hidden;
  min-width: 0;
  flex: 1;
  /* The base (non-fullpage) .ws2-thread-item-title rule sets
     white-space:nowrap + text-overflow:ellipsis for single-line truncation;
     this rule only wins the cascade on the properties it declares, so
     without an explicit reset here the inherited nowrap silently blocks the
     -webkit-box from ever wrapping to a second line. */
  white-space: normal;
  text-overflow: clip;
}
.ws2-thread-item-ts {
  font-size: var(--text-sm);
  color: var(--ws2-text-tertiary);
  white-space: nowrap;
  /* Lives in .ws2-thread-item-row-bottom now (moved out of row-top so it no
     longer competes with the title for width -- see
     _workspace_v2_thread_list.html). margin-left:auto right-anchors it past
     the left-packed chips; still shrinkable with ellipsis as a safety net
     for very narrow sidebar widths, but no fixed max-width cap. */
  flex-shrink: 1;
  min-width: 0;
  margin-left: auto;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Chip family unified: font-size 11px, radius var(--radius-sm). */
body.ws2-fullpage .ws-new-chip {
  /* Muted "unread" indicator — soft background, no loud border, toned text. */
  background: var(--tile);
  color: var(--ink-2);
  border: none;
  border-radius: var(--radius-sm);
  text-transform: none; /* lowercase "new" instead of uppercase "NEW" */
  font-weight: 500;
  font-size: var(--text-sm);
  padding: 1px 6px;
  letter-spacing: 0;
}
body.ws2-fullpage .ws2-legacy-qa-chip {
  /* The small neutral tile, same as the other thread-row chips. */
  background: var(--tile);
  color: var(--ink-2);
  font-size: var(--text-sm);
}
body.ws2-fullpage .ws2-thread-tool-chip {
  background: var(--tile);
  color: var(--ws2-text-tertiary);
  font-size: var(--text-sm);
}

/* 11. New conversation button in left panel header */
.ws2-new-thread-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px; /* PU-05 */
  height: 44px;
  border-radius: var(--radius);
  background: var(--btn-tint);
  color: var(--navy);
  text-decoration: none;
  flex-shrink: 0;
  transition:
    background 0.12s ease,
    color 0.12s ease;
}
.ws2-new-thread-btn:hover {
  background: var(--navy);
  color: var(--on-primary);
}

/* 12. Canvas panel: job status cards + download items */
body.ws2-fullpage .ws2-canvas-slot {
  background: var(--canvas);
  border: 1px solid var(--ws2-border);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 12px;
}
body.ws2-fullpage .ws2-canvas-slot:last-child {
  margin-bottom: 0;
}
body.ws2-fullpage .ws2-canvas-slot-label {
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ws2-text-tertiary);
  margin-bottom: 10px;
}
body.ws2-fullpage .ws2-canvas-placeholder {
  font-size: var(--text-sm);
  color: var(--ws2-text-secondary);
}
body.ws2-fullpage .ws2-canvas-link-job-label {
  font-size: var(--text-sm);
  color: var(--ws2-text-secondary);
}
body.ws2-fullpage .ws2-canvas-download-item {
  /* min-height, not height: the item is a column (chip over meta line) and
     the chip has overflow:hidden (min-height 0), so a fixed 44px box let
     the chip shrink to a clipped sliver. */
  min-height: 44px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 4px;
  border-radius: var(--radius);
  padding: 6px 8px;
  transition: background 0.12s ease;
}
body.ws2-fullpage .ws2-canvas-download-item:hover {
  background: var(--sub);
}
body.ws2-fullpage .ws2-canvas-download-item a {
  color: var(--ws2-accent);
  text-decoration: none;
  font-size: var(--text-sm);
  flex: 0 0 auto;
  max-width: 100%;
  line-height: 1.4;
}
body.ws2-fullpage .ws2-canvas-download-item a:hover {
  text-decoration: underline;
}

/* Repricing canvas cards (grey tiles on the white slot) */
body.ws2-fullpage .ws2-canvas-repricing-stat {
  background: var(--sub);
}
body.ws2-fullpage .ws2-canvas-repricing-stat-highlight {
  border: 1px solid var(--ws2-border);
}
body.ws2-fullpage .ws2-canvas-repricing-stat-label {
  color: var(--ws2-text-tertiary);
}
body.ws2-fullpage .ws2-canvas-repricing-stat-value {
  color: var(--ws2-text-primary);
}
body.ws2-fullpage .ws2-geo-headline-card {
  background: var(--sub);
  border-color: var(--ws2-border);
}
body.ws2-fullpage .ws2-geo-headline-detail {
  color: var(--ws2-text-secondary);
}
body.ws2-fullpage .ws2-canvas-repricing-detail-row {
  border-top: 1px solid var(--ws2-border);
}
body.ws2-fullpage .ws2-canvas-repricing-detail-summary {
  color: var(--ws2-text-primary);
}
body.ws2-fullpage .ws2-canvas-repricing-count-chip {
  border-radius: var(--radius-tag);
  background: var(--tile);
  color: var(--ws2-text-secondary);
}
body.ws2-fullpage .ws2-canvas-repricing-anomaly-item,
body.ws2-fullpage .ws2-canvas-repricing-integrity-item {
  color: var(--ws2-text-secondary);
}
body.ws2-fullpage .ws2-canvas-repricing-anomaly-label {
  color: var(--ws2-text-primary);
}
body.ws2-fullpage .ws2-canvas-repricing-anomaly-detail {
  color: var(--ws2-text-tertiary);
}
body.ws2-fullpage .ws2-canvas-repricing-integrity-issues {
  color: var(--ws2-text-secondary);
}
body.ws2-fullpage .ws2-canvas-repricing-qa-text {
  color: var(--ws2-text-primary);
}
body.ws2-fullpage .ws2-canvas-repricing-qa-links a {
  color: var(--ws2-accent-text, var(--ws2-accent));
}
body.ws2-fullpage .ws2-canvas-repricing-fb-form textarea,
body.ws2-fullpage .ws2-canvas-repricing-fb-reply-form textarea {
  background: var(--canvas);
  border: 1px solid var(--ws2-border);
  color: var(--ws2-text-primary);
}
body.ws2-fullpage .ws2-canvas-repricing-fb-file-label,
body.ws2-fullpage .ws2-canvas-repricing-fb-history-label {
  color: var(--ws2-text-tertiary);
}
body.ws2-fullpage .ws2-canvas-repricing-fb-item {
  border-top: 1px solid var(--ws2-border);
}
body.ws2-fullpage .ws2-canvas-repricing-fb-status {
  color: var(--ws2-text-secondary);
}
body.ws2-fullpage .ws2-canvas-repricing-fb-meta-sep,
body.ws2-fullpage .ws2-canvas-repricing-fb-meta-date {
  color: var(--ws2-text-tertiary);
}
body.ws2-fullpage .ws2-canvas-repricing-fb-text {
  color: var(--ws2-text-primary);
}
body.ws2-fullpage .ws2-canvas-repricing-fb-attachments {
  color: var(--ws2-text-tertiary);
}
body.ws2-fullpage .ws2-canvas-repricing-fb-resolution {
  color: var(--ws2-text-secondary);
}
body.ws2-fullpage .ws2-canvas-repricing-fb-resolution-date {
  color: var(--ws2-text-tertiary);
}
body.ws2-fullpage .ws2-canvas-repricing-fb-resolution-text {
  color: var(--ws2-text-primary);
}
body.ws2-fullpage .ws2-canvas-repricing-fb-thread-message {
  background: var(--sub);
}
body.ws2-fullpage .ws2-canvas-repricing-fb-thread-msg-header {
  color: var(--ws2-text-tertiary);
}
body.ws2-fullpage .ws2-canvas-repricing-fb-thread-author {
  color: var(--ws2-text-secondary);
}
body.ws2-fullpage .ws2-canvas-repricing-fb-thread-text {
  color: var(--ws2-text-primary);
}
body.ws2-fullpage .ws2-canvas-repricing-fb-thread-attachments {
  color: var(--ws2-text-tertiary);
}
body.ws2-fullpage .ws2-canvas-repricing-fb-reply-prompt {
  color: var(--ws2-text-tertiary);
}

/* Thread list filter link */
body.ws2-fullpage .ws2-thread-list-filter {
  padding: 8px 12px;
  margin-bottom: 4px;
}
body.ws2-fullpage .ws2-filter-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px; /* PU-05 tap floor */
  font-size: var(--text-sm);
  color: var(--ws2-accent);
  text-decoration: none;
}
body.ws2-fullpage .ws2-filter-link:hover {
  color: var(--navy);
  text-decoration: none;
}

/* Empty notes */
body.ws2-fullpage .ws2-empty-note {
  font-size: var(--text-sm);
  color: var(--ws2-text-secondary);
  padding: 32px 16px;
  text-align: center;
}
body.ws2-fullpage .ws2-empty-note a {
  color: var(--ws2-accent);
  text-decoration: none;
}
body.ws2-fullpage .ws2-empty-note a:hover {
  color: var(--navy);
  text-decoration: underline;
}
/* Conversation panel's own empty state: centered both axes in the large
   middle column, not just top-padded like the smaller sidebar/canvas empty
   notes. See the base .ws2-conversation-empty-body/-note rules above for
   why this is its own class instead of a change to .ws2-panel-body /
   .ws2-empty-note directly. */
body.ws2-fullpage .ws2-conversation-empty-body {
  display: flex;
  align-items: center;
  justify-content: center;
}
body.ws2-fullpage .ws2-conversation-empty-note {
  font-size: var(--text-sm);
  padding: 0;
}

/* Inline error / flagged / pushback banners */
body.ws2-fullpage .ws2-inline-error {
  background: var(--red-tint);
  border: 1px solid var(--red-line);
  border-radius: var(--radius);
  color: var(--red);
  font-size: var(--text-sm);
  padding: 10px 14px;
  margin-bottom: 16px;
}
body.ws2-fullpage .ws2-flagged-banner {
  background: var(--ws2-highlight-soft);
  border: 1px solid var(--navy);
  border-radius: var(--radius);
  color: var(--navy);
  font-size: var(--text-sm);
  padding: 8px 14px;
  margin-bottom: 16px;
}
body.ws2-fullpage .ws2-detail-pending-approval-banner {
  background: var(--ws2-highlight-soft);
  border: 1px solid var(--navy);
  color: var(--navy);
  border-radius: var(--radius);
  font-size: var(--text-sm);
  padding: 10px 14px;
  margin-bottom: 16px;
}
body.ws2-fullpage .ws2-detail-pending-approval-icon {
  color: var(--navy);
}
body.ws2-fullpage .ws2-pushback-banner {
  background: var(--select);
  border: 1px solid var(--ws2-border);
  border-radius: var(--radius);
  color: var(--ws2-text-primary);
  font-size: var(--text-sm);
  padding: 10px 14px;
  margin-bottom: 16px;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

/* The base .ws2-pending-thread-item rule uses the amber --warning-bg card;
   the brand has no amber, so a thread waiting for approval takes the same
   attention treatment as .ws2-avatar-awaiting-you and the banners above:
   navy edge on the select tint. */
body.ws2-fullpage .ws2-pending-thread-item {
  background: var(--ws2-highlight-soft);
  border: 1px solid var(--navy);
}

/* 13. Below 900px the shell is a single-pane phone layout (list OR
   conversation, Files as a full-screen sheet) -- see the "Mobile
   single-pane" block at the end of workspace_glass.css, which has to load
   last to win the cascade over the glass panel rules. 900px is intentional
   (the 3-column shell collapses earlier than the platform's usual 640px).
   Do not change it to 640px. */

/* =========================================================
   Workspace V2 -- tool-tag chips + deterministic pushback (WS-B)
   Flag: LOGRO_WORKSPACE_V2. Composer chip row (new-thread page) and the
   conversation-panel tool badge + pushback banner share this block.
   ========================================================= */

.ws2-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.ws2-chip {
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--bg);
  color: var(--text);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  transition:
    background 0.12s,
    border-color 0.12s,
    color 0.12s;
}

.ws2-chip:hover {
  background: var(--bg-alt);
}

.ws2-chip-selected {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-primary);
}

/* Reuses the drop-zone visuals originally inline in workspace_new.html,
   promoted here so the v2 reply composer (_workspace_v2_conversation.html)
   can share them without duplicating a <style> block per template. */
.ws-drop-zone,
.ws2-dropzone {
  border: 2px dashed var(--border);
  border-radius: var(--radius-sm);
  padding: var(--sp-4) var(--sp-3);
  text-align: center;
  cursor: pointer;
  color: var(--text-muted);
  font-size: var(--text-sm);
  transition:
    border-color 0.15s,
    background 0.15s;
}

.ws-drop-zone.ws-drag-over,
.ws2-dropzone.ws-drag-over {
  border-color: var(--accent);
  background: var(--bg-alt);
}

.ws-file-list {
  margin-top: var(--sp-2);
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.ws-file-item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 6px var(--sp-2);
  font-size: var(--text-sm);
}

.ws-file-item-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ws-file-item-size {
  color: var(--text-muted);
  white-space: nowrap;
}

.ws-file-remove {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-muted);
  font-size: 1rem;
  padding: 0 2px;
  line-height: 1;
}

.ws-file-remove:hover {
  color: var(--error);
}

.ws2-tool-badge {
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-tag);
  padding: 2px var(--sp-2);
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--navy);
  white-space: nowrap;
}

/* _workspace_v2_thread_tool_badge.html always renders the
   #ws2-thread-tool-badge element (an htmx OOB swap needs an existing
   element with a matching id to replace -- see that partial's docstring),
   including for a thread with no tool_tag yet. The [hidden] attribute
   already suppresses rendering per HTML semantics; this rule just also
   strips the badge's own border/padding/background so no empty pill-shaped
   gap is reserved in the header layout while it's hidden. */
.ws2-tool-badge-empty {
  display: none;
  border: none;
  padding: 0;
  background: none;
}

/* Canvas job-status chip: the linked job's 3-state indicator (In Progress /
   Needs Input / Resolved) -- see
   workspace_v2_routes.py::_canvas_state_for_job_state for the vocabulary
   this collapses down from. Modeled on .ws2-tool-badge's small-rounded-label
   shape; color semantics follow the brand (green=resolved, blue
   accent=in-progress, navy edge=needs input; no amber). */
.ws2-canvas-state-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 10px;
  border-radius: var(--radius-tag);
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.ws2-canvas-state-chip-in-progress {
  background: var(--select);
  color: var(--brand-blue);
}
.ws2-canvas-state-chip-needs-input {
  background: var(--canvas);
  outline: 1px solid var(--navy); /* an edge that adds no height */
  outline-offset: -1px;
  color: var(--navy);
}
.ws2-canvas-state-chip-resolved {
  background: var(--green-tint);
  color: var(--green);
}

.ws2-pushback-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  background: var(--select);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-3);
  margin-bottom: var(--sp-3);
  color: var(--text);
  font-size: var(--text-sm);
}

.ws2-canvas-job-format {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin: 0 0 var(--sp-2);
}

body.ws2-fullpage .ws2-canvas-job-format {
  color: var(--ws2-text-primary);
}

.auth-brand + main .auth-wrap {
  padding-top: var(--sp-8);
}

/* ─── Site-wide incident banner (ER-O7, _site_banner.html) ───────────────
   Shown on the main and directory/member shells. Colours come from the
   light-shell tokens: info is white on navy, warning is body text on the
   warning tint with a warning-coloured rule. */
.site-banner {
  position: relative;
  z-index: 260;
  padding: 0.5rem 1rem;
  font-size: 0.9375rem;
  line-height: 1.4;
  text-align: center;
}
.site-banner-text {
  margin: 0;
  overflow-wrap: anywhere;
}
.site-banner-label {
  font-weight: 700;
}
.site-banner--info {
  background: var(--navy);
  color: var(--on-primary);
}
.site-banner--warning {
  background: var(--warning-bg);
  color: var(--text);
  border-bottom: 3px solid var(--warning);
}
/* The workspace shell (and its phone canvas sheet) is position:fixed over the
   whole viewport; start it below the banner instead of under it. */
body.ws2-fullpage.has-site-banner .ws2-shell,
body.ws2-fullpage.has-site-banner.ws2-canvas-collapsed .ws2-shell {
  top: var(--site-banner-h, 0px);
  height: auto;
  grid-template-rows: minmax(0, 1fr);
}
body.ws2-fullpage.has-site-banner .ws2-panel {
  height: 100%;
}
body.ws2-fullpage.has-site-banner.ws2-has-conversation.ws2-canvas-open
  .ws2-canvas-panel {
  top: var(--site-banner-h, 0px);
  height: auto;
}

/* Access-code field on a code-protected one-time link: space it from the
   "Expires" note above it. */
.secure-link-card .auth-form {
  margin-top: var(--sp-4);
}

/* STAGING banner space reservation (inert unless _staging_banner.html rendered
   #staging-banner, i.e. LOGRO_ENVIRONMENT=staging). The banner is fixed to the
   bottom edge; reserve its height so it never covers page footers or the
   full-viewport workspace sidebar footer (user menu / theme / logout). */
html:has(#staging-banner) {
  --staging-banner-h: 22px;
}
html:has(#staging-banner) body {
  padding-bottom: var(--staging-banner-h);
}
html:has(#staging-banner) body.ws2-fullpage .ws2-shell {
  height: calc(100vh - var(--staging-banner-h));
  height: calc(100dvh - var(--staging-banner-h));
}
html:has(#staging-banner) body.ws2-fullpage .ws2-panel {
  height: calc(100vh - var(--staging-banner-h));
  height: calc(100dvh - var(--staging-banner-h));
}

/* ==========================================================================
   Workspace canvas: compare slot (#ws2-canvas-compare)
   Client's finished workbook vs ours, the chosen-result state, and the
   "Names shown" / "Names hidden" share state. Output-only: choices are made
   by telling the chat; each block ends in a one-line hint. Self-contained:
   everything is scoped under #ws2-canvas-compare; tokens only (--ws2-* with
   light-token fallbacks). The download link is a 44px tap target; the table
   is fixed-layout so nothing scrolls sideways at 390px.
   ========================================================================== */
#ws2-canvas-compare {
  min-width: 0;
}
#ws2-canvas-compare .ws2-compare-card {
  min-width: 0;
}
#ws2-canvas-compare .ws2-compare-card + .ws2-compare-card {
  margin-top: var(--sp-4, 16px);
  padding-top: var(--sp-4, 16px);
  border-top: 1px solid var(--ws2-border, var(--border));
}
#ws2-canvas-compare .ws2-compare-title {
  margin: 0 0 var(--sp-2, 8px);
}
#ws2-canvas-compare .ws2-compare-file {
  margin: 0 0 var(--sp-2, 8px);
  font-size: var(--text-sm);
  color: var(--ws2-text-secondary, var(--text-secondary));
  overflow-wrap: anywhere;
}
#ws2-canvas-compare .ws2-compare-error {
  margin: 0 0 var(--sp-3, 12px);
  padding: var(--sp-2, 8px) var(--sp-3, 12px);
  border-radius: var(--radius);
  font-size: var(--text-sm);
  color: var(--ws2-text-primary, var(--text));
  background: var(--ws2-highlight-soft, var(--select));
  border: 1px solid var(--ws2-highlight, var(--navy));
}
#ws2-canvas-compare .ws2-compare-rate {
  margin: 0 0 var(--sp-3, 12px);
  font-size: var(--text-sm);
  color: var(--ws2-text-primary, var(--text));
}
#ws2-canvas-compare .ws2-compare-rate-num {
  font-size: 2rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--ws2-accent-text, var(--primary));
}
#ws2-canvas-compare .ws2-compare-rate-of {
  display: block;
  margin-top: 2px;
  font-size: var(--text-sm);
  color: var(--ws2-text-secondary, var(--text-secondary));
}
#ws2-canvas-compare .ws2-compare-stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-2, 8px);
  margin: 0 0 var(--sp-3, 12px);
}
#ws2-canvas-compare .ws2-compare-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--sp-2, 8px);
  border-radius: var(--radius);
  background: var(--sub);
  border: 1px solid var(--ws2-border, var(--border));
  min-width: 0;
}
#ws2-canvas-compare .ws2-compare-stat dt {
  font-size: var(--text-sm);
  color: var(--ws2-text-tertiary, var(--text-secondary));
}
#ws2-canvas-compare .ws2-compare-stat dd {
  margin: 0;
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--ws2-text-primary, var(--text));
}
#ws2-canvas-compare .ws2-compare-sub {
  margin: var(--sp-3, 12px) 0 var(--sp-2, 8px);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ws2-text-primary, var(--text));
}
#ws2-canvas-compare .ws2-compare-reasons {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2, 8px);
}
#ws2-canvas-compare .ws2-compare-reason {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: var(--sp-2, 8px);
  row-gap: 4px;
  align-items: baseline;
  font-size: var(--text-sm);
  color: var(--ws2-text-secondary, var(--text-secondary));
}
#ws2-canvas-compare .ws2-compare-reason-label {
  overflow-wrap: anywhere;
}
#ws2-canvas-compare .ws2-compare-reason-count {
  font-weight: 700;
  color: var(--ws2-text-primary, var(--text));
}
#ws2-canvas-compare .ws2-compare-bar {
  grid-column: 1 / -1;
  width: 100%;
  height: 8px;
  border: 0;
  border-radius: 4px;
  overflow: hidden;
  appearance: none;
  -webkit-appearance: none;
  background: var(--track);
}
#ws2-canvas-compare .ws2-compare-bar::-webkit-progress-bar {
  background: var(--track);
  border-radius: 4px;
}
#ws2-canvas-compare .ws2-compare-bar::-webkit-progress-value {
  background: var(--ws2-accent-fill, var(--primary));
  border-radius: 4px;
}
#ws2-canvas-compare .ws2-compare-bar::-moz-progress-bar {
  background: var(--ws2-accent-fill, var(--primary));
  border-radius: 4px;
}
#ws2-canvas-compare .ws2-compare-table {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
  font-size: var(--text-sm);
  color: var(--ws2-text-secondary, var(--text-secondary));
}
#ws2-canvas-compare .ws2-compare-table thead th:first-child {
  width: 46%;
}
#ws2-canvas-compare .ws2-compare-table th,
#ws2-canvas-compare .ws2-compare-table td {
  padding: 6px 4px;
  text-align: left;
  vertical-align: top;
  border-top: 1px solid var(--ws2-border, var(--border));
  overflow-wrap: anywhere;
  word-break: break-word;
}
#ws2-canvas-compare .ws2-compare-table thead th {
  border-top: 0;
  font-weight: 600;
  color: var(--ws2-text-tertiary, var(--text-secondary));
}
#ws2-canvas-compare .ws2-compare-table tbody th {
  font-weight: 400;
}
#ws2-canvas-compare .ws2-compare-item {
  display: block;
  font-weight: 600;
  color: var(--ws2-text-primary, var(--text));
}
#ws2-canvas-compare .ws2-compare-measure,
#ws2-canvas-compare .ws2-compare-why {
  display: block;
  color: var(--ws2-text-tertiary, var(--text-secondary));
}
#ws2-canvas-compare .ws2-compare-gap {
  white-space: nowrap;
}
#ws2-canvas-compare .ws2-compare-table td {
  color: var(--ws2-text-primary, var(--text));
}
#ws2-canvas-compare .ws2-compare-adopted,
#ws2-canvas-compare .ws2-compare-state {
  margin: var(--sp-3, 12px) 0 0;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ws2-accent-text, var(--primary));
}
#ws2-canvas-compare .ws2-compare-state {
  color: var(--ws2-text-primary, var(--text));
}
#ws2-canvas-compare .ws2-compare-hint {
  margin: var(--sp-1, 4px) 0 0;
  font-size: var(--text-sm);
  color: var(--ws2-text-secondary, var(--text-secondary));
}
#ws2-canvas-compare .ws2-compare-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  min-height: 44px;
  width: 100%;
  padding: 8px 14px;
  text-align: center;
  text-decoration: none;
  white-space: normal;
}
#ws2-canvas-compare .ws2-compare-btn.btn-primary {
  background: var(--ws2-accent-fill, var(--primary));
  color: var(--ws2-on-accent, var(--on-primary));
  border: 1px solid transparent;
}
#ws2-canvas-compare .ws2-compare-download {
  margin-top: var(--sp-3, 12px);
}
#ws2-canvas-compare .ws2-compare-btn:focus-visible {
  outline: 2px solid var(--ws2-accent-text, var(--primary));
  outline-offset: 2px;
}
#ws2-canvas-compare .ws2-compare-help {
  margin: var(--sp-1, 4px) 0 0;
  font-size: var(--text-sm);
  color: var(--ws2-text-secondary, var(--text-secondary));
}

/* Keep the row hover on the workspace tokens (the global `tbody tr:hover`
   paints the shell's own page tint). */
#ws2-canvas-compare .ws2-compare-table tbody tr:hover,
#ws2-canvas-compare .ws2-compare-table tbody tr:hover > th,
#ws2-canvas-compare .ws2-compare-table tbody tr:hover > td {
  background: var(--ws2-hover, var(--sub));
}
